diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index fb34954ac..2b24e33f5 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -22,6 +22,7 @@ import type { AstroCookies } from '../core/cookies'; import type { LogOptions } from '../core/logger/core'; import type { AstroComponentFactory, AstroComponentInstance } from '../runtime/server'; import type { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from './../core/constants.js'; +import { Logger } from '../core/logger/core'; export type { MarkdownHeading, MarkdownMetadata, @@ -1856,56 +1857,87 @@ export interface AstroIntegration { name: string; /** The different hooks available to extend. */ hooks: { - 'astro:config:setup'?: (options: { - config: AstroConfig; - command: 'dev' | 'build' | 'preview'; - isRestart: boolean; - updateConfig: (newConfig: Record) => void; - addRenderer: (renderer: AstroRenderer) => void; - addWatchFile: (path: URL | string) => void; - injectScript: (stage: InjectedScriptStage, content: string) => void; - injectRoute: (injectRoute: InjectedRoute) => void; - addClientDirective: (directive: ClientDirectiveConfig) => void; - // TODO: Add support for `injectElement()` for full HTML element injection, not just scripts. - // This may require some refactoring of `scripts`, `styles`, and `links` into something - // more generalized. Consider the SSR use-case as well. - // injectElement: (stage: vite.HtmlTagDescriptor, element: string) => void; - }) => void | Promise; - 'astro:config:done'?: (options: { - config: AstroConfig; - setAdapter: (adapter: AstroAdapter) => void; - }) => void | Promise; - 'astro:server:setup'?: (options: { server: vite.ViteDevServer }) => void | Promise; - 'astro:server:start'?: (options: { address: AddressInfo }) => void | Promise; - 'astro:server:done'?: () => void | Promise; - 'astro:build:ssr'?: (options: { - manifest: SerializedSSRManifest; - /** - * This maps a {@link RouteData} to an {@link URL}, this URL represents - * the physical file you should import. - */ - entryPoints: Map; - /** - * File path of the emitted middleware - */ - middlewareEntryPoint: URL | undefined; - }) => void | Promise; - 'astro:build:start'?: () => void | Promise; - 'astro:build:setup'?: (options: { - vite: vite.InlineConfig; - pages: Map; - target: 'client' | 'server'; - updateConfig: (newConfig: vite.InlineConfig) => void; - }) => void | Promise; - 'astro:build:generated'?: (options: { dir: URL }) => void | Promise; - 'astro:build:done'?: (options: { - pages: { pathname: string }[]; - dir: URL; - routes: RouteData[]; - }) => void | Promise; + 'astro:config:setup'?: ( + options: { + config: AstroConfig; + command: 'dev' | 'build' | 'preview'; + isRestart: boolean; + updateConfig: (newConfig: Record) => void; + addRenderer: (renderer: AstroRenderer) => void; + addWatchFile: (path: URL | string) => void; + injectScript: (stage: InjectedScriptStage, content: string) => void; + injectRoute: (injectRoute: InjectedRoute) => void; + addClientDirective: (directive: ClientDirectiveConfig) => void; + // TODO: Add support for `injectElement()` for full HTML element injection, not just scripts. + // This may require some refactoring of `scripts`, `styles`, and `links` into something + // more generalized. Consider the SSR use-case as well. + // injectElement: (stage: vite.HtmlTagDescriptor, element: string) => void; + }, + bag: AstroIntegrationBag + ) => void | Promise; + 'astro:config:done'?: ( + options: { + config: AstroConfig; + setAdapter: (adapter: AstroAdapter) => void; + }, + bag: AstroIntegrationBag + ) => void | Promise; + 'astro:server:setup'?: ( + options: { server: vite.ViteDevServer }, + bag: AstroIntegrationBag + ) => void | Promise; + 'astro:server:start'?: ( + options: { address: AddressInfo }, + bag: AstroIntegrationBag + ) => void | Promise; + 'astro:server:done'?: (bag: AstroIntegrationBag) => void | Promise; + 'astro:build:ssr'?: ( + options: { + manifest: SerializedSSRManifest; + /** + * This maps a {@link RouteData} to an {@link URL}, this URL represents + * the physical file you should import. + */ + entryPoints: Map; + /** + * File path of the emitted middleware + */ + middlewareEntryPoint: URL | undefined; + }, + bag: AstroIntegrationBag + ) => void | Promise; + 'astro:build:start'?: (bag: AstroIntegrationBag) => void | Promise; + 'astro:build:setup'?: ( + options: { + vite: vite.InlineConfig; + pages: Map; + target: 'client' | 'server'; + updateConfig: (newConfig: vite.InlineConfig) => void; + }, + bag: AstroIntegrationBag + ) => void | Promise; + 'astro:build:generated'?: ( + options: { dir: URL }, + bag: AstroIntegrationBag + ) => void | Promise; + 'astro:build:done'?: ( + options: { + pages: { pathname: string }[]; + dir: URL; + routes: RouteData[]; + }, + bag: AstroIntegrationBag + ) => void | Promise; }; } +/** + * A set of utilities that are passed at each hook + */ +export type AstroIntegrationBag = { + logger: Logger; +}; + export type MiddlewareNext = () => Promise; export type MiddlewareHandler = ( context: APIContext, diff --git a/packages/astro/src/core/logger/core.ts b/packages/astro/src/core/logger/core.ts index 4f0c281e0..67cc26508 100644 --- a/packages/astro/src/core/logger/core.ts +++ b/packages/astro/src/core/logger/core.ts @@ -127,3 +127,32 @@ export function timerMessage(message: string, startTime: number = Date.now()) { timeDiff < 750 ? `${Math.round(timeDiff)}ms` : `${(timeDiff / 1000).toFixed(1)}s`; return `${message} ${dim(timeDisplay)}`; } + +export class Logger { + type: string; + options: LogOptions; + constructor(type: string, options: LogOptions) { + this.type = type; + this.options = options; + } + + /** + * Creates a new Logger instance using the same log options + */ + fork(type: string): Logger { + return new Logger(type, this.options); + } + + info(message: string) { + info(this.options, this.type, message); + } + warn(message: string) { + warn(this.options, this.type, message); + } + error(message: string) { + error(this.options, this.type, message); + } + debug(message: string) { + debug(this.options, this.type, message); + } +} diff --git a/packages/astro/src/integrations/index.ts b/packages/astro/src/integrations/index.ts index cf50df0e1..c082e137b 100644 --- a/packages/astro/src/integrations/index.ts +++ b/packages/astro/src/integrations/index.ts @@ -16,7 +16,7 @@ import type { SerializedSSRManifest } from '../core/app/types'; import type { PageBuildData } from '../core/build/types'; import { buildClientDirectiveEntrypoint } from '../core/client-directive/index.js'; import { mergeConfig } from '../core/config/index.js'; -import { info, type LogOptions } from '../core/logger/core.js'; +import { info, type LogOptions, Logger } from '../core/logger/core.js'; import { isServerLikeOutput } from '../prerender/utils.js'; async function withTakingALongTimeMsg({ @@ -72,6 +72,8 @@ export async function runHookConfigSetup({ * ``` */ if (integration.hooks?.['astro:config:setup']) { + const logger = new Logger(`${integration.name}`, logging); + const hooks: HookParameters<'astro:config:setup'> = { config: updatedConfig, command, @@ -144,7 +146,7 @@ export async function runHookConfigSetup({ await withTakingALongTimeMsg({ name: integration.name, - hookResult: integration.hooks['astro:config:setup'](hooks), + hookResult: integration.hooks['astro:config:setup'](hooks, { logger }), logging, }); @@ -167,20 +169,24 @@ export async function runHookConfigDone({ logging: LogOptions; }) { for (const integration of settings.config.integrations) { + const logger = new Logger(`${integration.name}`, logging); if (integration?.hooks?.['astro:config:done']) { await withTakingALongTimeMsg({ name: integration.name, - hookResult: integration.hooks['astro:config:done']({ - config: settings.config, - setAdapter(adapter) { - if (settings.adapter && settings.adapter.name !== adapter.name) { - throw new Error( - `Integration "${integration.name}" conflicts with "${settings.adapter.name}". You can only configure one deployment integration.` - ); - } - settings.adapter = adapter; + hookResult: integration.hooks['astro:config:done']( + { + config: settings.config, + setAdapter(adapter) { + if (settings.adapter && settings.adapter.name !== adapter.name) { + throw new Error( + `Integration "${integration.name}" conflicts with "${settings.adapter.name}". You can only configure one deployment integration.` + ); + } + settings.adapter = adapter; + }, }, - }), + { logger } + ), logging, }); } @@ -198,9 +204,10 @@ export async function runHookServerSetup({ }) { for (const integration of config.integrations) { if (integration?.hooks?.['astro:server:setup']) { + const logger = new Logger(`${integration.name}`, logging); await withTakingALongTimeMsg({ name: integration.name, - hookResult: integration.hooks['astro:server:setup']({ server }), + hookResult: integration.hooks['astro:server:setup']({ server }, { logger }), logging, }); } @@ -217,10 +224,12 @@ export async function runHookServerStart({ logging: LogOptions; }) { for (const integration of config.integrations) { + const logger = new Logger(`${integration.name}`, logging); + if (integration?.hooks?.['astro:server:start']) { await withTakingALongTimeMsg({ name: integration.name, - hookResult: integration.hooks['astro:server:start']({ address }), + hookResult: integration.hooks['astro:server:start']({ address }, { logger }), logging, }); } @@ -235,10 +244,12 @@ export async function runHookServerDone({ logging: LogOptions; }) { for (const integration of config.integrations) { + const logger = new Logger(`${integration.name}`, logging); + if (integration?.hooks?.['astro:server:done']) { await withTakingALongTimeMsg({ name: integration.name, - hookResult: integration.hooks['astro:server:done'](), + hookResult: integration.hooks['astro:server:done']({ logger }), logging, }); } @@ -254,9 +265,11 @@ export async function runHookBuildStart({ }) { for (const integration of config.integrations) { if (integration?.hooks?.['astro:build:start']) { + const logger = new Logger(`${integration.name}`, logging); + await withTakingALongTimeMsg({ name: integration.name, - hookResult: integration.hooks['astro:build:start'](), + hookResult: integration.hooks['astro:build:start']({ logger }), logging, }); } @@ -280,16 +293,21 @@ export async function runHookBuildSetup({ for (const integration of config.integrations) { if (integration?.hooks?.['astro:build:setup']) { + const logger = new Logger(`${integration.name}`, logging); + await withTakingALongTimeMsg({ name: integration.name, - hookResult: integration.hooks['astro:build:setup']({ - vite, - pages, - target, - updateConfig: (newConfig) => { - updatedConfig = mergeConfig(updatedConfig, newConfig); + hookResult: integration.hooks['astro:build:setup']( + { + vite, + pages, + target, + updateConfig: (newConfig) => { + updatedConfig = mergeConfig(updatedConfig, newConfig); + }, }, - }), + { logger } + ), logging, }); } @@ -315,13 +333,18 @@ export async function runHookBuildSsr({ }: RunHookBuildSsr) { for (const integration of config.integrations) { if (integration?.hooks?.['astro:build:ssr']) { + const logger = new Logger(`${integration.name}`, logging); + await withTakingALongTimeMsg({ name: integration.name, - hookResult: integration.hooks['astro:build:ssr']({ - manifest, - entryPoints, - middlewareEntryPoint, - }), + hookResult: integration.hooks['astro:build:ssr']( + { + manifest, + entryPoints, + middlewareEntryPoint, + }, + { logger } + ), logging, }); } @@ -338,10 +361,12 @@ export async function runHookBuildGenerated({ const dir = isServerLikeOutput(config) ? config.build.client : config.outDir; for (const integration of config.integrations) { + const logger = new Logger(`${integration.name}`, logging); + if (integration?.hooks?.['astro:build:generated']) { await withTakingALongTimeMsg({ name: integration.name, - hookResult: integration.hooks['astro:build:generated']({ dir }), + hookResult: integration.hooks['astro:build:generated']({ dir }, { logger }), logging, }); } @@ -361,13 +386,18 @@ export async function runHookBuildDone({ config, pages, routes, logging }: RunHo for (const integration of config.integrations) { if (integration?.hooks?.['astro:build:done']) { + const logger = new Logger(`${integration.name}`, logging); + await withTakingALongTimeMsg({ name: integration.name, - hookResult: integration.hooks['astro:build:done']({ - pages: pages.map((p) => ({ pathname: p })), - dir, - routes, - }), + hookResult: integration.hooks['astro:build:done']( + { + pages: pages.map((p) => ({ pathname: p })), + dir, + routes, + }, + { logger } + ), logging, }); }