summaryrefslogtreecommitdiff
path: root/packages/integrations/image/src
diff options
context:
space:
mode:
authorGravatar Tony Sullivan <tony.f.sullivan@outlook.com> 2022-08-22 19:13:19 +0000
committerGravatar GitHub <noreply@github.com> 2022-08-22 19:13:19 +0000
commitc4af8723bd232d78d24dbd58feaef87dbaec07c7 (patch)
treeff43209f73dea7d857bd16a42a76fde999ed816c /packages/integrations/image/src
parent4ac271888346c307dd361801337eade918387e89 (diff)
downloadastro-c4af8723bd232d78d24dbd58feaef87dbaec07c7.tar.gz
astro-c4af8723bd232d78d24dbd58feaef87dbaec07c7.tar.zst
astro-c4af8723bd232d78d24dbd58feaef87dbaec07c7.zip
[@astrojs/image] adds a logger to the the image integration (#4342)
* WIP: adding a console logger that respect vite.logLevel * adds an optional prefix for messages * remove temporary debug log * typo fix * cleaning up log syntax * fixing logger whitespace * adding README docs * test: disable integration logging in tests * chore: add changeset
Diffstat (limited to 'packages/integrations/image/src')
-rw-r--r--packages/integrations/image/src/build/ssg.ts50
-rw-r--r--packages/integrations/image/src/index.ts7
-rw-r--r--packages/integrations/image/src/utils/logger.ts74
3 files changed, 118 insertions, 13 deletions
diff --git a/packages/integrations/image/src/build/ssg.ts b/packages/integrations/image/src/build/ssg.ts
index 496905f92..c4f873f33 100644
--- a/packages/integrations/image/src/build/ssg.ts
+++ b/packages/integrations/image/src/build/ssg.ts
@@ -1,3 +1,4 @@
+import { bgGreen, black, cyan, dim, green, bold } from 'kleur/colors';
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
@@ -5,19 +6,30 @@ import { OUTPUT_DIR } from '../constants.js';
import type { SSRImageService, TransformOptions } from '../loaders/index.js';
import { isRemoteImage, loadLocalImage, loadRemoteImage } from '../utils/images.js';
import { ensureDir } from '../utils/paths.js';
+import { debug, info, warn, LoggerLevel } from '../utils/logger.js';
+
+function getTimeStat(timeStart: number, timeEnd: number) {
+ const buildTime = timeEnd - timeStart;
+ return buildTime < 750 ? `${Math.round(buildTime)}ms` : `${(buildTime / 1000).toFixed(2)}s`;
+}
export interface SSGBuildParams {
loader: SSRImageService;
staticImages: Map<string, Map<string, TransformOptions>>;
srcDir: URL;
outDir: URL;
+ logLevel: LoggerLevel;
}
-export async function ssgBuild({ loader, staticImages, srcDir, outDir }: SSGBuildParams) {
+export async function ssgBuild({ loader, staticImages, srcDir, outDir, logLevel }: SSGBuildParams) {
+ const timer = performance.now();
+
+ info({ level: logLevel, prefix: false, message: `${bgGreen(black(` optimizing ${staticImages.size} image${staticImages.size > 1 ? 's' : ''} `))}` });
+
const inputFiles = new Set<string>();
// process transforms one original image file at a time
- for await (const [src, transformsMap] of staticImages) {
+ for (const [src, transformsMap] of staticImages) {
let inputFile: string | undefined = undefined;
let inputBuffer: Buffer | undefined = undefined;
@@ -35,14 +47,30 @@ export async function ssgBuild({ loader, staticImages, srcDir, outDir }: SSGBuil
if (!inputBuffer) {
// eslint-disable-next-line no-console
- console.warn(`"${src}" image could not be fetched`);
+ warn({ level: logLevel, message : `"${src}" image could not be fetched` });
continue;
}
const transforms = Array.from(transformsMap.entries());
+ debug({ level: logLevel, prefix: false, message: `${green('▶')} ${src}` });
+ let timeStart = performance.now();
+
+ if (inputFile) {
+ const to = inputFile.replace(fileURLToPath(srcDir), fileURLToPath(outDir));
+ await ensureDir(path.dirname(to));
+ await fs.copyFile(inputFile, to);
+
+ const timeEnd = performance.now();
+ const timeChange = getTimeStat(timeStart, timeEnd);
+ const timeIncrease = `(+${timeChange})`;
+ const pathRelative = inputFile.replace(fileURLToPath(srcDir), '');
+ debug({ level: logLevel, prefix: false, message: ` ${cyan('└─')} ${dim(`(original) ${pathRelative}`)} ${dim(timeIncrease)}` });
+ }
+
// process each transformed versiono of the
- for await (const [filename, transform] of transforms) {
+ for (const [filename, transform] of transforms) {
+ timeStart = performance.now();
let outputFile: string;
if (isRemoteImage(src)) {
@@ -58,14 +86,14 @@ export async function ssgBuild({ loader, staticImages, srcDir, outDir }: SSGBuil
ensureDir(path.dirname(outputFile));
await fs.writeFile(outputFile, data);
+
+ const timeEnd = performance.now();
+ const timeChange = getTimeStat(timeStart, timeEnd);
+ const timeIncrease = `(+${timeChange})`;
+ const pathRelative = outputFile.replace(fileURLToPath(outDir), '');
+ debug({ level: logLevel, prefix: false, message: ` ${cyan('└─')} ${dim(pathRelative)} ${dim(timeIncrease)}` });
}
}
- // copy all original local images to dist
- for await (const original of inputFiles) {
- const to = original.replace(fileURLToPath(srcDir), fileURLToPath(outDir));
-
- await ensureDir(path.dirname(to));
- await fs.copyFile(original, to);
- }
+ info({ level: logLevel, prefix: false, message: (dim(`Completed in ${getTimeStat(timer, performance.now())}.\n`)) });
}
diff --git a/packages/integrations/image/src/index.ts b/packages/integrations/image/src/index.ts
index 78f3d3f4e..d3e154d62 100644
--- a/packages/integrations/image/src/index.ts
+++ b/packages/integrations/image/src/index.ts
@@ -3,6 +3,7 @@ import { ssgBuild } from './build/ssg.js';
import { ssrBuild } from './build/ssr.js';
import { PKG_NAME, ROUTE_PATTERN } from './constants.js';
import { ImageService, TransformOptions } from './loaders/index.js';
+import type { LoggerLevel } from './utils/logger.js';
import { filenameFormat, propsToFilename } from './utils/paths.js';
import { createPlugin } from './vite-plugin-astro-image.js';
@@ -27,11 +28,13 @@ export interface IntegrationOptions {
* Entry point for the @type {HostedImageService} or @type {LocalImageService} to be used.
*/
serviceEntryPoint?: string;
+ logLevel?: LoggerLevel;
}
export default function integration(options: IntegrationOptions = {}): AstroIntegration {
const resolvedOptions = {
serviceEntryPoint: '@astrojs/image/sharp',
+ logLevel: 'info' as LoggerLevel,
...options,
};
@@ -72,7 +75,7 @@ export default function integration(options: IntegrationOptions = {}): AstroInte
});
}
},
- 'astro:server:setup': async () => {
+ 'astro:server:setup': async ({ server }) => {
globalThis.astroImage = {};
},
'astro:build:setup': () => {
@@ -107,7 +110,7 @@ export default function integration(options: IntegrationOptions = {}): AstroInte
const loader = globalThis?.astroImage?.loader;
if (loader && 'transform' in loader && staticImages.size > 0) {
- await ssgBuild({ loader, staticImages, srcDir: _config.srcDir, outDir: dir });
+ await ssgBuild({ loader, staticImages, srcDir: _config.srcDir, outDir: dir, logLevel: resolvedOptions.logLevel });
}
}
},
diff --git a/packages/integrations/image/src/utils/logger.ts b/packages/integrations/image/src/utils/logger.ts
new file mode 100644
index 000000000..3c11dacdc
--- /dev/null
+++ b/packages/integrations/image/src/utils/logger.ts
@@ -0,0 +1,74 @@
+// eslint-disable no-console
+import { bold, cyan, dim, green, red, yellow } from 'kleur/colors';
+
+const PREFIX = '@astrojs/image';
+
+// Hey, locales are pretty complicated! Be careful modifying this logic...
+// If we throw at the top-level, international users can't use Astro.
+//
+// Using `[]` sets the default locale properly from the system!
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#parameters
+//
+// Here be the dragons we've slain:
+// https://github.com/withastro/astro/issues/2625
+// https://github.com/withastro/astro/issues/3309
+const dateTimeFormat = new Intl.DateTimeFormat([], {
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit',
+});
+
+export type LoggerLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent'; // same as Pino
+
+export interface LogMessage {
+ level: LoggerLevel;
+ message: string;
+ prefix?: boolean;
+ timestamp?: boolean;
+}
+
+export const levels: Record<LoggerLevel, number> = {
+ debug: 20,
+ info: 30,
+ warn: 40,
+ error: 50,
+ silent: 90,
+};
+
+function getPrefix(level: LoggerLevel, timestamp: boolean) {
+ let prefix = '';
+
+ if (timestamp) {
+ prefix += dim(dateTimeFormat.format(new Date()) + ' ');
+ }
+
+ switch (level) {
+ case 'debug':
+ prefix += bold(green(`[${PREFIX}] `));
+ break;
+ case 'info':
+ prefix += bold(cyan(`[${PREFIX}] `));
+ break;
+ case 'warn':
+ prefix += bold(yellow(`[${PREFIX}] `));
+ break;
+ case 'error':
+ prefix += bold(red(`[${PREFIX}] `));
+ break;
+ }
+
+ return prefix;
+}
+
+const log = (_level: LoggerLevel, dest: (message: string) => void) =>
+ ({ message, level, prefix = true, timestamp = true }: LogMessage) => {
+ if (levels[_level] >= levels[level]) {
+ dest(`${prefix ? getPrefix(level, timestamp) : ''}${message}`);
+ }
+ }
+
+export const info = log('info', console.info);
+export const debug = log('debug', console.debug);
+export const warn = log('warn', console.warn);
+export const error = log('error', console.error);
+