diff --git a/.changeset/long-chefs-jump.md b/.changeset/long-chefs-jump.md new file mode 100644 index 000000000000..ed8f47614bfb --- /dev/null +++ b/.changeset/long-chefs-jump.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +The scrollend mechanism is a better way to record the scroll position compared to throttling, so we now use it whenever a browser supports it. \ No newline at end of file diff --git a/benchmark/bench/_util.js b/benchmark/bench/_util.js index c9108695cf98..23c4726046bc 100644 --- a/benchmark/bench/_util.js +++ b/benchmark/bench/_util.js @@ -1,6 +1,9 @@ -import { createRequire } from 'module'; +import { createRequire } from 'node:module'; +import path from 'node:path'; -export const astroBin = createRequire(import.meta.url).resolve('astro'); +const astroPkgPath = createRequire(import.meta.url).resolve('astro/package.json'); + +export const astroBin = path.resolve(astroPkgPath, '../astro.js'); /** @typedef {{ avg: number, stdev: number, max: number }} Stat */ diff --git a/benchmark/make-project/server-stress-default.js b/benchmark/make-project/server-stress-default.js index 20094daa6f3c..79e8b260af6c 100644 --- a/benchmark/make-project/server-stress-default.js +++ b/benchmark/make-project/server-stress-default.js @@ -7,11 +7,13 @@ import { loremIpsum } from './_util.js'; export async function run(projectDir) { await fs.rm(projectDir, { recursive: true, force: true }); await fs.mkdir(new URL('./src/pages', projectDir), { recursive: true }); + await fs.mkdir(new URL('./src/components', projectDir), { recursive: true }); await fs.writeFile( new URL('./src/pages/index.astro', projectDir), `\ --- +import Paragraph from '../components/Paragraph.astro' const content = "${loremIpsum}" --- @@ -25,13 +27,26 @@ const content = "${loremIpsum}"

Astro

- ${Array.from({ length: 60 }).map(() => '

{content}

')} + ${Array.from({ length: 100 }) + .map(() => '

{content}

') + .join('\n')} +
+
+ ${Array.from({ length: 50 }) + .map((_, i) => '') + .join('\n')}
`, 'utf-8' ); + await fs.writeFile( + new URL('./src/components/Paragraph.astro', projectDir), + `
{Astro.props.num} {Astro.props.str}
`, + 'utf-8' + ); + await fs.writeFile( new URL('./astro.config.js', projectDir), `\ diff --git a/packages/astro/components/ViewTransitions.astro b/packages/astro/components/ViewTransitions.astro index 42e8deb74b74..7fa33676825a 100644 --- a/packages/astro/components/ViewTransitions.astro +++ b/packages/astro/components/ViewTransitions.astro @@ -385,17 +385,15 @@ const { fallback = 'animate' } = Astro.props as Props; }); addEventListener('load', onPageLoad); // There's not a good way to record scroll position before a back button. - // So the way we do it is by listening to scroll and just continuously recording it. - addEventListener( - 'scroll', - throttle(() => { - // only updste history entries that are managed by us - // leave other entries alone and do not accidently add state. - if (history.state) { - persistState({ ...history.state, scrollY }); - } - }, 300), - { passive: true } - ); + // So the way we do it is by listening to scrollend if supported, and if not continuously record the scroll position. + const updateState = () => { + // only update history entries that are managed by us + // leave other entries alone and do not accidently add state. + if (history.state) { + persistState({ ...history.state, scrollY }); + } + } + if ('onscrollend' in window) addEventListener('scrollend', updateState); + else addEventListener('scroll', throttle(updateState, 300)); } diff --git a/packages/astro/e2e/view-transitions.test.js b/packages/astro/e2e/view-transitions.test.js index 02fb76e4f2d1..fc69ac64004a 100644 --- a/packages/astro/e2e/view-transitions.test.js +++ b/packages/astro/e2e/view-transitions.test.js @@ -193,7 +193,10 @@ test.describe('View Transitions', () => { await expect(article, 'should have script content').toHaveText('works'); }); - test('astro:page-load event fires when navigating directly to a page', async ({ page, astro }) => { + test('astro:page-load event fires when navigating directly to a page', async ({ + page, + astro, + }) => { // Go to page 2 await page.goto(astro.resolveUrl('/two')); const article = page.locator('#twoarticle'); diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 94e29a198fee..bc47e07795de 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -20,7 +20,7 @@ import type { AstroConfigType } from '../core/config'; import type { AstroTimer } from '../core/config/timer'; import type { AstroCookies } from '../core/cookies'; import type { ResponseWithEncoding } from '../core/endpoint/index.js'; -import type { AstroIntegrationLogger, LogOptions, LoggerLevel } from '../core/logger/core'; +import type { AstroIntegrationLogger, Logger, LoggerLevel } from '../core/logger/core'; import type { AstroComponentFactory, AstroComponentInstance } from '../runtime/server'; import type { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from './../core/constants.js'; @@ -1378,7 +1378,7 @@ export interface AstroInlineOnlyConfig { /** * @internal for testing only, use `logLevel` instead. */ - logging?: LogOptions; + logger?: Logger; } export type ContentEntryModule = { @@ -2046,7 +2046,7 @@ export type AstroMiddlewareInstance = { export interface AstroPluginOptions { settings: AstroSettings; - logging: LogOptions; + logger: Logger; } export type RouteType = 'page' | 'endpoint' | 'redirect'; diff --git a/packages/astro/src/assets/build/generate.ts b/packages/astro/src/assets/build/generate.ts index b78800a4382a..282db9d6d91d 100644 --- a/packages/astro/src/assets/build/generate.ts +++ b/packages/astro/src/assets/build/generate.ts @@ -1,7 +1,6 @@ import fs, { readFileSync } from 'node:fs'; import { basename, join } from 'node:path/posix'; -import type { StaticBuildOptions } from '../../core/build/types.js'; -import { warn } from '../../core/logger/core.js'; +import type { BuildPipeline } from '../../core/build/buildPipeline'; import { prependForwardSlash } from '../../core/path.js'; import { isServerLikeOutput } from '../../prerender/utils.js'; import { getConfiguredImageService, isESMImportedImage } from '../internal.js'; @@ -24,19 +23,20 @@ interface GenerationDataCached { type GenerationData = GenerationDataUncached | GenerationDataCached; export async function generateImage( - buildOpts: StaticBuildOptions, + pipeline: BuildPipeline, options: ImageTransform, filepath: string ): Promise { + const config = pipeline.getConfig(); + const logger = pipeline.getLogger(); let useCache = true; - const assetsCacheDir = new URL('assets/', buildOpts.settings.config.cacheDir); + const assetsCacheDir = new URL('assets/', config.cacheDir); // Ensure that the cache directory exists try { await fs.promises.mkdir(assetsCacheDir, { recursive: true }); } catch (err) { - warn( - buildOpts.logging, + logger.warn( 'astro:assets', `An error was encountered while creating the cache directory. Proceeding without caching. Error: ${err}` ); @@ -44,12 +44,12 @@ export async function generateImage( } let serverRoot: URL, clientRoot: URL; - if (isServerLikeOutput(buildOpts.settings.config)) { - serverRoot = buildOpts.settings.config.build.server; - clientRoot = buildOpts.settings.config.build.client; + if (isServerLikeOutput(config)) { + serverRoot = config.build.server; + clientRoot = config.build.client; } else { - serverRoot = buildOpts.settings.config.outDir; - clientRoot = buildOpts.settings.config.outDir; + serverRoot = config.outDir; + clientRoot = config.outDir; } const isLocalImage = isESMImportedImage(options.src); @@ -105,10 +105,7 @@ export async function generateImage( if (isLocalImage) { imageData = await fs.promises.readFile( new URL( - '.' + - prependForwardSlash( - join(buildOpts.settings.config.build.assets, basename(originalImagePath)) - ), + '.' + prependForwardSlash(join(config.build.assets, basename(originalImagePath))), serverRoot ) ); @@ -120,11 +117,7 @@ export async function generateImage( const imageService = (await getConfiguredImageService()) as LocalImageService; resultData.data = ( - await imageService.transform( - imageData, - { ...options, src: originalImagePath }, - buildOpts.settings.config.image - ) + await imageService.transform(imageData, { ...options, src: originalImagePath }, config.image) ).data; try { @@ -143,8 +136,7 @@ export async function generateImage( } } } catch (e) { - warn( - buildOpts.logging, + logger.warn( 'astro:assets', `An error was encountered while creating the cache directory. Proceeding without caching. Error: ${e}` ); diff --git a/packages/astro/src/cli/add/index.ts b/packages/astro/src/cli/add/index.ts index fcaeb07c7c71..07fc7b1a83a6 100644 --- a/packages/astro/src/cli/add/index.ts +++ b/packages/astro/src/cli/add/index.ts @@ -16,14 +16,14 @@ import { updateTSConfigForFramework, type frameworkWithTSSettings, } from '../../core/config/tsconfig.js'; -import { debug, info, type LogOptions } from '../../core/logger/core.js'; +import type { Logger } from '../../core/logger/core.js'; import * as msg from '../../core/messages.js'; import { printHelp } from '../../core/messages.js'; import { appendForwardSlash } from '../../core/path.js'; import { apply as applyPolyfill } from '../../core/polyfill.js'; import { parseNpmName } from '../../core/util.js'; import { eventCliSession, telemetry } from '../../events/index.js'; -import { createLoggingFromFlags } from '../flags.js'; +import { createLoggerFromFlags } from '../flags.js'; import { generate, parse, t, visit } from './babel.js'; import { ensureImport } from './imports.js'; import { wrapDefaultExport } from './wrapper.js'; @@ -130,10 +130,10 @@ export async function add(names: string[], { flags }: AddOptions) { // Some packages might have a common alias! We normalize those here. const cwd = flags.root; - const logging = createLoggingFromFlags(flags); + const logger = createLoggerFromFlags(flags); const integrationNames = names.map((name) => (ALIASES.has(name) ? ALIASES.get(name)! : name)); const integrations = await validateIntegrations(integrationNames); - let installResult = await tryToInstallIntegrations({ integrations, cwd, flags, logging }); + let installResult = await tryToInstallIntegrations({ integrations, cwd, flags, logger }); const rootPath = resolveRoot(cwd); const root = pathToFileURL(rootPath); // Append forward slash to compute relative paths @@ -144,7 +144,8 @@ export async function add(names: string[], { flags }: AddOptions) { if (integrations.find((integration) => integration.id === 'tailwind')) { await setupIntegrationConfig({ root, - logging, + logger, + flags, integrationName: 'Tailwind', possibleConfigFiles: [ @@ -159,7 +160,7 @@ export async function add(names: string[], { flags }: AddOptions) { if (integrations.find((integration) => integration.id === 'svelte')) { await setupIntegrationConfig({ root, - logging, + logger, flags, integrationName: 'Svelte', possibleConfigFiles: ['./svelte.config.js', './svelte.config.cjs', './svelte.config.mjs'], @@ -175,7 +176,7 @@ export async function add(names: string[], { flags }: AddOptions) { ) { await setupIntegrationConfig({ root, - logging, + logger, flags, integrationName: 'Lit', possibleConfigFiles: ['./.npmrc'], @@ -186,8 +187,7 @@ export async function add(names: string[], { flags }: AddOptions) { break; } case UpdateResult.cancelled: { - info( - logging, + logger.info( null, msg.cancelled( `Dependencies ${bold('NOT')} installed.`, @@ -209,9 +209,9 @@ export async function add(names: string[], { flags }: AddOptions) { let configURL = rawConfigPath ? pathToFileURL(rawConfigPath) : undefined; if (configURL) { - debug('add', `Found config at ${configURL}`); + logger.debug('add', `Found config at ${configURL}`); } else { - info(logging, 'add', `Unable to locate a config file, generating one for you.`); + logger.info('add', `Unable to locate a config file, generating one for you.`); configURL = new URL('./astro.config.mjs', root); await fs.writeFile(fileURLToPath(configURL), ASTRO_CONFIG_STUB, { encoding: 'utf-8' }); } @@ -220,7 +220,7 @@ export async function add(names: string[], { flags }: AddOptions) { try { ast = await parseAstroConfig(configURL); - debug('add', 'Parsed astro config'); + logger.debug('add', 'Parsed astro config'); const defineConfig = t.identifier('defineConfig'); ensureImport( @@ -232,7 +232,7 @@ export async function add(names: string[], { flags }: AddOptions) { ); wrapDefaultExport(ast, defineConfig); - debug('add', 'Astro config ensured `defineConfig`'); + logger.debug('add', 'Astro config ensured `defineConfig`'); for (const integration of integrations) { if (isAdapter(integration)) { @@ -240,8 +240,7 @@ export async function add(names: string[], { flags }: AddOptions) { if (officialExportName) { await setAdapter(ast, integration, officialExportName); } else { - info( - logging, + logger.info( null, `\n ${magenta( `Check our deployment docs for ${bold( @@ -253,10 +252,10 @@ export async function add(names: string[], { flags }: AddOptions) { } else { await addIntegration(ast, integration); } - debug('add', `Astro config added integration ${integration.id}`); + logger.debug('add', `Astro config added integration ${integration.id}`); } } catch (err) { - debug('add', 'Error parsing/modifying astro config: ', err); + logger.debug('add', 'Error parsing/modifying astro config: ', err); throw createPrettyError(err as Error); } @@ -268,18 +267,18 @@ export async function add(names: string[], { flags }: AddOptions) { configURL, ast, flags, - logging, + logger, logAdapterInstructions: integrations.some(isAdapter), }); } catch (err) { - debug('add', 'Error updating astro config', err); + logger.debug('add', 'Error updating astro config', err); throw createPrettyError(err as Error); } } switch (configResult) { case UpdateResult.cancelled: { - info(logging, null, msg.cancelled(`Your configuration has ${bold('NOT')} been updated.`)); + logger.info(null, msg.cancelled(`Your configuration has ${bold('NOT')} been updated.`)); break; } case UpdateResult.none: { @@ -293,18 +292,17 @@ export async function add(names: string[], { flags }: AddOptions) { (integration) => !deps.includes(integration.packageName) ); if (missingDeps.length === 0) { - info(logging, null, msg.success(`Configuration up-to-date.`)); + logger.info(null, msg.success(`Configuration up-to-date.`)); break; } } - info(logging, null, msg.success(`Configuration up-to-date.`)); + logger.info(null, msg.success(`Configuration up-to-date.`)); break; } default: { const list = integrations.map((integration) => ` - ${integration.packageName}`).join('\n'); - info( - logging, + logger.info( null, msg.success( `Added the following integration${ @@ -315,15 +313,14 @@ export async function add(names: string[], { flags }: AddOptions) { } } - const updateTSConfigResult = await updateTSConfig(cwd, logging, integrations, flags); + const updateTSConfigResult = await updateTSConfig(cwd, logger, integrations, flags); switch (updateTSConfigResult) { case UpdateResult.none: { break; } case UpdateResult.cancelled: { - info( - logging, + logger.info( null, msg.cancelled(`Your TypeScript configuration has ${bold('NOT')} been updated.`) ); @@ -335,7 +332,7 @@ export async function add(names: string[], { flags }: AddOptions) { ); } default: - info(logging, null, msg.success(`Successfully updated TypeScript settings`)); + logger.info(null, msg.success(`Successfully updated TypeScript settings`)); } } @@ -529,13 +526,13 @@ async function updateAstroConfig({ configURL, ast, flags, - logging, + logger, logAdapterInstructions, }: { configURL: URL; ast: t.File; flags: yargs.Arguments; - logging: LogOptions; + logger: Logger; logAdapterInstructions: boolean; }): Promise { const input = await fs.readFile(fileURLToPath(configURL), { encoding: 'utf-8' }); @@ -562,15 +559,13 @@ async function updateAstroConfig({ title: configURL.pathname.split('/').pop(), })}\n`; - info( - logging, + logger.info( null, `\n ${magenta('Astro will make the following changes to your config file:')}\n${message}` ); if (logAdapterInstructions) { - info( - logging, + logger.info( null, magenta( ` For complete deployment options, visit\n ${bold( @@ -582,7 +577,7 @@ async function updateAstroConfig({ if (await askToContinue({ flags })) { await fs.writeFile(fileURLToPath(configURL), output, { encoding: 'utf-8' }); - debug('add', `Updated astro config`); + logger.debug('add', `Updated astro config`); return UpdateResult.updated; } else { return UpdateResult.cancelled; @@ -598,13 +593,15 @@ interface InstallCommand { async function getInstallIntegrationsCommand({ integrations, + logger, cwd = process.cwd(), }: { integrations: IntegrationInfo[]; + logger: Logger; cwd?: string; }): Promise { const pm = await preferredPM(cwd); - debug('add', `package manager: ${JSON.stringify(pm)}`); + logger.debug('add', `package manager: ${JSON.stringify(pm)}`); if (!pm) return null; let dependencies = integrations @@ -644,14 +641,14 @@ async function tryToInstallIntegrations({ integrations, cwd, flags, - logging, + logger, }: { integrations: IntegrationInfo[]; cwd?: string; flags: yargs.Arguments; - logging: LogOptions; + logger: Logger; }): Promise { - const installCommand = await getInstallIntegrationsCommand({ integrations, cwd }); + const installCommand = await getInstallIntegrationsCommand({ integrations, cwd, logger }); const inheritedFlags = Object.entries(flags) .map(([flag]) => { @@ -677,8 +674,7 @@ async function tryToInstallIntegrations({ padding: 0.5, borderStyle: 'round', })}\n`; - info( - logging, + logger.info( null, `\n ${magenta('Astro will run the following command:')}\n ${dim( 'If you skip this step, you can always run it yourself later' @@ -702,7 +698,7 @@ async function tryToInstallIntegrations({ return UpdateResult.updated; } catch (err) { spinner.fail(); - debug('add', 'Error installing dependencies', err); + logger.debug('add', 'Error installing dependencies', err); // eslint-disable-next-line no-console console.error('\n', (err as any).stdout, '\n'); return UpdateResult.failure; @@ -829,7 +825,7 @@ export async function validateIntegrations(integrations: string[]): Promise { @@ -852,7 +848,7 @@ async function updateTSConfig( } if (inputConfig.reason === 'not-found') { - debug('add', "Couldn't find tsconfig.json or jsconfig.json, generating one"); + logger.debug('add', "Couldn't find tsconfig.json or jsconfig.json, generating one"); } const outputConfig = updateTSConfigForFramework( @@ -875,8 +871,7 @@ async function updateTSConfig( title: configFileName, })}\n`; - info( - logging, + logger.info( null, `\n ${magenta(`Astro will make the following changes to your ${configFileName}:`)}\n${message}` ); @@ -890,8 +885,7 @@ async function updateTSConfig( integrations.filter((integration) => conflictingIntegrations.includes(integration)).length > 0; if (hasConflictingIntegrations) { - info( - logging, + logger.info( null, red( ` ${bold( @@ -907,7 +901,7 @@ async function updateTSConfig( await fs.writeFile(inputConfig?.path ?? path.join(cwd, 'tsconfig.json'), output, { encoding: 'utf-8', }); - debug('add', `Updated ${configFileName} file`); + logger.debug('add', `Updated ${configFileName} file`); return UpdateResult.updated; } else { return UpdateResult.cancelled; @@ -971,13 +965,14 @@ function getDiffContent(input: string, output: string): string | null { async function setupIntegrationConfig(opts: { root: URL; - logging: LogOptions; + logger: Logger; flags: yargs.Arguments; integrationName: string; possibleConfigFiles: string[]; defaultConfigFile: string; defaultConfigContent: string; }) { + const logger = opts.logger; const possibleConfigFiles = opts.possibleConfigFiles.map((p) => fileURLToPath(new URL(p, opts.root)) ); @@ -989,8 +984,7 @@ async function setupIntegrationConfig(opts: { } } if (!alreadyConfigured) { - info( - opts.logging, + logger.info( null, `\n ${magenta(`Astro will generate a minimal ${bold(opts.defaultConfigFile)} file.`)}\n` ); @@ -1002,9 +996,9 @@ async function setupIntegrationConfig(opts: { encoding: 'utf-8', } ); - debug('add', `Generated default ${opts.defaultConfigFile} file`); + logger.debug('add', `Generated default ${opts.defaultConfigFile} file`); } } else { - debug('add', `Using existing ${opts.integrationName} configuration`); + logger.debug('add', `Using existing ${opts.integrationName} configuration`); } } diff --git a/packages/astro/src/cli/check/index.ts b/packages/astro/src/cli/check/index.ts index 6ae11d576cef..6ce3bf71ecb6 100644 --- a/packages/astro/src/cli/check/index.ts +++ b/packages/astro/src/cli/check/index.ts @@ -1,23 +1,21 @@ import path from 'node:path'; import type { Arguments } from 'yargs-parser'; -import { error, info } from '../../core/logger/core.js'; -import { createLoggingFromFlags, flagsToAstroInlineConfig } from '../flags.js'; +import { createLoggerFromFlags, flagsToAstroInlineConfig } from '../flags.js'; import { getPackage } from '../install-package.js'; export async function check(flags: Arguments) { - const logging = createLoggingFromFlags(flags); + const logger = createLoggerFromFlags(flags); const getPackageOpts = { skipAsk: flags.yes || flags.y, cwd: flags.root }; const checkPackage = await getPackage( '@astrojs/check', - logging, + logger, getPackageOpts, ['typescript'] ); - const typescript = await getPackage('typescript', logging, getPackageOpts); + const typescript = await getPackage('typescript', logger, getPackageOpts); if (!checkPackage || !typescript) { - error( - logging, + logger.error( 'check', 'The `@astrojs/check` and `typescript` packages are required for this command to work. Please manually install them into your project and try again.' ); @@ -38,6 +36,6 @@ export async function check(flags: Arguments) { const config = parseArgsAsCheckConfig(process.argv); - info(logging, 'check', `Getting diagnostics for Astro files in ${path.resolve(config.root)}...`); + logger.info('check', `Getting diagnostics for Astro files in ${path.resolve(config.root)}...`); return await checker(config); } diff --git a/packages/astro/src/cli/flags.ts b/packages/astro/src/cli/flags.ts index 3d7360a290e7..54177e998585 100644 --- a/packages/astro/src/cli/flags.ts +++ b/packages/astro/src/cli/flags.ts @@ -1,6 +1,6 @@ import type { Arguments as Flags } from 'yargs-parser'; import type { AstroInlineConfig } from '../@types/astro.js'; -import type { LogOptions } from '../core/logger/core.js'; +import { Logger, type LogOptions } from '../core/logger/core.js'; import { nodeLogDestination } from '../core/logger/node.js'; export function flagsToAstroInlineConfig(flags: Flags): AstroInlineConfig { @@ -30,7 +30,7 @@ export function flagsToAstroInlineConfig(flags: Flags): AstroInlineConfig { * The `logging` is usually created from an `AstroInlineConfig`, but some flows like `add` * doesn't read the AstroConfig directly, so we create a `logging` object from the CLI flags instead. */ -export function createLoggingFromFlags(flags: Flags): LogOptions { +export function createLoggerFromFlags(flags: Flags): Logger { const logging: LogOptions = { dest: nodeLogDestination, level: 'info', @@ -42,5 +42,5 @@ export function createLoggingFromFlags(flags: Flags): LogOptions { logging.level = 'silent'; } - return logging; + return new Logger(logging); } diff --git a/packages/astro/src/cli/install-package.ts b/packages/astro/src/cli/install-package.ts index 8793d9985396..919ede0e2059 100644 --- a/packages/astro/src/cli/install-package.ts +++ b/packages/astro/src/cli/install-package.ts @@ -5,7 +5,7 @@ import { createRequire } from 'node:module'; import ora from 'ora'; import prompts from 'prompts'; import whichPm from 'which-pm'; -import { debug, info, type LogOptions } from '../core/logger/core.js'; +import { type Logger } from '../core/logger/core.js'; type GetPackageOptions = { skipAsk?: boolean; @@ -14,7 +14,7 @@ type GetPackageOptions = { export async function getPackage( packageName: string, - logging: LogOptions, + logger: Logger, options: GetPackageOptions, otherDeps: string[] = [] ): Promise { @@ -27,12 +27,11 @@ export async function getPackage( // The `require.resolve` is required as to avoid Node caching the failed `import` packageImport = await import(packageName); } catch (e) { - info( - logging, + logger.info( '', `To continue, Astro requires the following dependency to be installed: ${bold(packageName)}.` ); - const result = await installPackage([packageName, ...otherDeps], options, logging); + const result = await installPackage([packageName, ...otherDeps], options, logger); if (result) { packageImport = await import(packageName); @@ -60,7 +59,7 @@ function getInstallCommand(packages: string[], packageManager: string) { async function installPackage( packageNames: string[], options: GetPackageOptions, - logging: LogOptions + logger: Logger ): Promise { const cwd = options.cwd ?? process.cwd(); const packageManager = (await whichPm(cwd)).name ?? 'npm'; @@ -79,8 +78,7 @@ async function installPackage( padding: 0.5, borderStyle: 'round', })}\n`; - info( - logging, + logger.info( null, `\n ${magenta('Astro will run the following command:')}\n ${dim( 'If you skip this step, you can always run it yourself later' @@ -113,7 +111,7 @@ async function installPackage( return true; } catch (err) { - debug('add', 'Error installing dependencies', err); + logger.debug('add', 'Error installing dependencies', err); spinner.fail(); return false; diff --git a/packages/astro/src/config/index.ts b/packages/astro/src/config/index.ts index d32af35b874f..c5f045a5fd94 100644 --- a/packages/astro/src/config/index.ts +++ b/packages/astro/src/config/index.ts @@ -1,6 +1,6 @@ import type { UserConfig } from 'vite'; import type { AstroUserConfig } from '../@types/astro'; -import type { LogOptions } from '../core/logger/core'; +import { Logger } from '../core/logger/core'; export function defineConfig(config: AstroUserConfig) { return config; @@ -30,24 +30,24 @@ export function getViteConfig(inlineConfig: UserConfig) { import('../integrations/index.js'), import('./vite-plugin-content-listen.js'), ]); - const logging: LogOptions = { + const logger = new Logger({ dest: nodeLogDestination, level: 'info', - }; + }); const { astroConfig: config } = await resolveConfig({}, cmd); const settings = createSettings(config, inlineConfig.root); - await runHookConfigSetup({ settings, command: cmd, logging }); + await runHookConfigSetup({ settings, command: cmd, logger }); const viteConfig = await createVite( { mode, plugins: [ // Initialize the content listener - astroContentListenPlugin({ settings, logging, fs }), + astroContentListenPlugin({ settings, logger, fs }), ], }, - { settings, logging: logging, mode } + { settings, logger, mode } ); - await runHookConfigDone({ settings, logging }); + await runHookConfigDone({ settings, logger }); return mergeConfig(viteConfig, inlineConfig); }; } diff --git a/packages/astro/src/config/vite-plugin-content-listen.ts b/packages/astro/src/config/vite-plugin-content-listen.ts index ecd9ea68b768..bb3cd9d2bc9e 100644 --- a/packages/astro/src/config/vite-plugin-content-listen.ts +++ b/packages/astro/src/config/vite-plugin-content-listen.ts @@ -2,7 +2,7 @@ import type fsMod from 'node:fs'; import type { Plugin, ViteDevServer } from 'vite'; import type { AstroSettings } from '../@types/astro'; import { attachContentServerListeners } from '../content/server-listeners.js'; -import type { LogOptions } from '../core/logger/core.js'; +import type { Logger } from '../core/logger/core.js'; /** * Listen for Astro content directory changes and generate types. @@ -14,11 +14,11 @@ import type { LogOptions } from '../core/logger/core.js'; */ export function astroContentListenPlugin({ settings, - logging, + logger, fs, }: { settings: AstroSettings; - logging: LogOptions; + logger: Logger; fs: typeof fsMod; }): Plugin { let server: ViteDevServer; @@ -33,7 +33,7 @@ export function astroContentListenPlugin({ await attachContentServerListeners({ fs: fs, settings, - logging, + logger, viteServer: server, }); }, diff --git a/packages/astro/src/content/server-listeners.ts b/packages/astro/src/content/server-listeners.ts index 53deffaf8fa8..c5e3da2c4252 100644 --- a/packages/astro/src/content/server-listeners.ts +++ b/packages/astro/src/content/server-listeners.ts @@ -5,14 +5,14 @@ import { fileURLToPath, pathToFileURL } from 'node:url'; import type { ViteDevServer } from 'vite'; import type { AstroSettings } from '../@types/astro.js'; import { loadTSConfig } from '../core/config/tsconfig.js'; -import { info, warn, type LogOptions } from '../core/logger/core.js'; +import type { Logger } from '../core/logger/core.js'; import { appendForwardSlash } from '../core/path.js'; import { createContentTypesGenerator } from './types-generator.js'; import { getContentPaths, globalContentConfigObserver, type ContentPaths } from './utils.js'; interface ContentServerListenerParams { fs: typeof fsMod; - logging: LogOptions; + logger: Logger; settings: AstroSettings; viteServer: ViteDevServer; } @@ -20,27 +20,26 @@ interface ContentServerListenerParams { export async function attachContentServerListeners({ viteServer, fs, - logging, + logger, settings, }: ContentServerListenerParams) { const contentPaths = getContentPaths(settings.config, fs); if (fs.existsSync(contentPaths.contentDir)) { - info( - logging, + logger.info( 'content', `Watching ${cyan( contentPaths.contentDir.href.replace(settings.config.root.href, '') )} for changes` ); const maybeTsConfigStats = getTSConfigStatsWhenAllowJsFalse({ contentPaths, settings }); - if (maybeTsConfigStats) warnAllowJsIsFalse({ ...maybeTsConfigStats, logging }); + if (maybeTsConfigStats) warnAllowJsIsFalse({ ...maybeTsConfigStats, logger }); await attachListeners(); } else { viteServer.watcher.on('addDir', contentDirListener); async function contentDirListener(dir: string) { if (appendForwardSlash(pathToFileURL(dir).href) === contentPaths.contentDir.href) { - info(logging, 'content', `Content dir found. Watching for changes`); + logger.info('content', `Content dir found. Watching for changes`); await attachListeners(); viteServer.watcher.removeListener('addDir', contentDirListener); } @@ -51,12 +50,12 @@ export async function attachContentServerListeners({ const contentGenerator = await createContentTypesGenerator({ fs, settings, - logging, + logger, viteServer, contentConfigObserver: globalContentConfigObserver, }); await contentGenerator.init(); - info(logging, 'content', 'Types generated'); + logger.info('content', 'Types generated'); viteServer.watcher.on('add', (entry) => { contentGenerator.queueEvent({ name: 'add', entry }); @@ -77,26 +76,24 @@ export async function attachContentServerListeners({ } function warnAllowJsIsFalse({ - logging, + logger, tsConfigFileName, contentConfigFileName, }: { - logging: LogOptions; + logger: Logger; tsConfigFileName: string; contentConfigFileName: string; }) { - if (!['info', 'warn'].includes(logging.level)) - warn( - logging, - 'content', - `Make sure you have the ${bold('allowJs')} compiler option set to ${bold( - 'true' - )} in your ${bold(tsConfigFileName)} file to have autocompletion in your ${bold( - contentConfigFileName - )} file. + logger.warn( + 'content', + `Make sure you have the ${bold('allowJs')} compiler option set to ${bold( + 'true' + )} in your ${bold(tsConfigFileName)} file to have autocompletion in your ${bold( + contentConfigFileName + )} file. See ${bold('https://www.typescriptlang.org/tsconfig#allowJs')} for more information. ` - ); + ); } function getTSConfigStatsWhenAllowJsFalse({ diff --git a/packages/astro/src/content/types-generator.ts b/packages/astro/src/content/types-generator.ts index 078197cd04f1..8900419d89ae 100644 --- a/packages/astro/src/content/types-generator.ts +++ b/packages/astro/src/content/types-generator.ts @@ -7,7 +7,7 @@ import { normalizePath, type ViteDevServer } from 'vite'; import type { AstroSettings, ContentEntryType } from '../@types/astro.js'; import { AstroError } from '../core/errors/errors.js'; import { AstroErrorData } from '../core/errors/index.js'; -import { info, warn, type LogOptions } from '../core/logger/core.js'; +import type { Logger } from '../core/logger/core'; import { isRelativePath } from '../core/path.js'; import { CONTENT_TYPES_FILE, VIRTUAL_MODULE_ID } from './consts.js'; import { @@ -49,7 +49,7 @@ type CollectionEntryMap = { type CreateContentGeneratorParams = { contentConfigObserver: ContentObservable; - logging: LogOptions; + logger: Logger; settings: AstroSettings; /** This is required for loading the content config */ viteServer: ViteDevServer; @@ -68,7 +68,7 @@ class UnsupportedFileTypeError extends Error {} export async function createContentTypesGenerator({ contentConfigObserver, fs, - logging, + logger, settings, viteServer, }: CreateContentGeneratorParams) { @@ -140,7 +140,7 @@ export async function createContentTypesGenerator({ case 'addDir': collectionEntryMap[JSON.stringify(collection)] = { type: 'unknown', entries: {} }; if (logLevel === 'info') { - info(logging, 'content', `${cyan(collection)} collection added`); + logger.info('content', `${cyan(collection)} collection added`); } break; case 'unlinkDir': @@ -186,8 +186,7 @@ export async function createContentTypesGenerator({ const collection = getEntryCollectionName({ entry, contentDir }); if (collection === undefined) { if (['info', 'warn'].includes(logLevel)) { - warn( - logging, + logger.warn( 'content', `${cyan( normalizePath( @@ -350,8 +349,7 @@ export async function createContentTypesGenerator({ } } if (unsupportedFiles.length > 0 && ['info', 'warn'].includes(logLevel)) { - warn( - logging, + logger.warn( 'content', `Unsupported file types found. Prefix with an underscore (\`_\`) to ignore:\n- ${unsupportedFiles.join( '\n' @@ -372,7 +370,7 @@ export async function createContentTypesGenerator({ invalidateVirtualMod(viteServer); if (observable.status === 'loaded' && ['info', 'warn'].includes(logLevel)) { warnNonexistentCollections({ - logging, + logger, contentConfig: observable.config, collectionEntryMap, }); @@ -505,16 +503,15 @@ async function writeContentFiles({ function warnNonexistentCollections({ contentConfig, collectionEntryMap, - logging, + logger, }: { contentConfig: ContentConfig; collectionEntryMap: CollectionEntryMap; - logging: LogOptions; + logger: Logger; }) { for (const configuredCollection in contentConfig.collections) { if (!collectionEntryMap[JSON.stringify(configuredCollection)]) { - warn( - logging, + logger.warn( 'content', `The ${JSON.stringify( configuredCollection diff --git a/packages/astro/src/core/app/index.ts b/packages/astro/src/core/app/index.ts index 566d0c60dcee..8fa3f5f14f17 100644 --- a/packages/astro/src/core/app/index.ts +++ b/packages/astro/src/core/app/index.ts @@ -9,7 +9,7 @@ import type { import type { SinglePageBuiltModule } from '../build/types'; import { getSetCookiesFromResponse } from '../cookies/index.js'; import { consoleLogDestination } from '../logger/console.js'; -import { error, type LogOptions } from '../logger/core.js'; +import { Logger } from '../logger/core.js'; import { collapseDuplicateSlashes, prependForwardSlash, @@ -50,12 +50,14 @@ export class App { #manifest: SSRManifest; #manifestData: ManifestData; #routeDataToRouteInfo: Map; - #logging: LogOptions = { + #logger = new Logger({ dest: consoleLogDestination, level: 'info', - }; + }); #baseWithoutTrailingSlash: string; #pipeline: SSRRoutePipeline; + #onRequest: MiddlewareEndpointHandler | undefined; + #middlewareLoaded: boolean; constructor(manifest: SSRManifest, streaming = true) { this.#manifest = manifest; @@ -65,6 +67,7 @@ export class App { this.#routeDataToRouteInfo = new Map(manifest.routes.map((route) => [route.routeData, route])); this.#baseWithoutTrailingSlash = removeTrailingForwardSlash(this.#manifest.base); this.#pipeline = new SSRRoutePipeline(this.#createEnvironment(streaming)); + this.#middlewareLoaded = false; } set setManifest(newManifest: SSRManifest) { @@ -80,7 +83,7 @@ export class App { #createEnvironment(streaming = false) { return createEnvironment({ adapterName: this.#manifest.adapterName, - logging: this.#logging, + logger: this.#logger, mode: 'production', compressHTML: this.#manifest.compressHTML, renderers: this.#manifest.renderers, @@ -100,7 +103,7 @@ export class App { } } }, - routeCache: new RouteCache(this.#logging), + routeCache: new RouteCache(this.#logger), site: this.#manifest.site, ssr: true, streaming, @@ -128,7 +131,21 @@ export class App { if (!routeData || routeData.prerender) return undefined; return routeData; } + + async #getOnRequest() { + if (this.#manifest.middlewareEntryPoint && !this.#middlewareLoaded) { + try { + const middleware = await import(this.#manifest.middlewareEntryPoint); + this.#pipeline.setMiddlewareFunction(middleware.onRequest as MiddlewareEndpointHandler); + } catch (e) { + this.#logger.warn('SSR', "Couldn't load the middleware entry point"); + } + } + this.#middlewareLoaded = true; + } + async render(request: Request, routeData?: RouteData, locals?: object): Promise { + await this.#getOnRequest(); // Handle requests with duplicate slashes gracefully by cloning with a cleaned-up request URL if (request.url !== collapseDuplicateSlashes(request.url)) { request = new Request(collapseDuplicateSlashes(request.url), request); @@ -156,16 +173,12 @@ export class App { ); let response; try { - // NOTE: ideally we could set the middleware function just once, but we don't have the infrastructure to that yet - if (mod.onRequest) { - this.#pipeline.setMiddlewareFunction(mod.onRequest as MiddlewareEndpointHandler); - } response = await this.#pipeline.renderRoute(renderContext, pageModule); } catch (err: any) { if (err instanceof EndpointNotFoundError) { return this.#renderError(request, { status: 404, response: err.originalResponse }); } else { - error(this.#logging, 'ssr', err.stack || err.message || String(err)); + this.#logger.error('ssr', err.stack || err.message || String(err)); return this.#renderError(request, { status: 500 }); } } diff --git a/packages/astro/src/core/app/types.ts b/packages/astro/src/core/app/types.ts index 8812d2c442ab..faeb9377e7c4 100644 --- a/packages/astro/src/core/app/types.ts +++ b/packages/astro/src/core/app/types.ts @@ -49,6 +49,7 @@ export type SSRManifest = { componentMetadata: SSRResult['componentMetadata']; pageModule?: SinglePageBuiltModule; pageMap?: Map; + middlewareEntryPoint: string | undefined; }; export type SerializedSSRManifest = Omit< diff --git a/packages/astro/src/core/build/buildPipeline.ts b/packages/astro/src/core/build/buildPipeline.ts index 5815fa5f509b..1956eede78ed 100644 --- a/packages/astro/src/core/build/buildPipeline.ts +++ b/packages/astro/src/core/build/buildPipeline.ts @@ -2,6 +2,7 @@ import type { AstroConfig, AstroSettings, SSRLoadedRenderer } from '../../@types import { getOutputDirectory, isServerLikeOutput } from '../../prerender/utils.js'; import { BEFORE_HYDRATION_SCRIPT_ID } from '../../vite-plugin-scripts/index.js'; import type { SSRManifest } from '../app/types'; +import { Logger } from '../logger/core.js'; import { Pipeline } from '../pipeline.js'; import { createEnvironment } from '../render/index.js'; import { createAssetLink } from '../render/ssr-element.js'; @@ -28,7 +29,7 @@ export class BuildPipeline extends Pipeline { super( createEnvironment({ adapterName: manifest.adapterName, - logging: staticBuildOptions.logging, + logger: staticBuildOptions.logger, mode: staticBuildOptions.mode, renderers: manifest.renderers, clientDirectives: manifest.clientDirectives, @@ -77,6 +78,17 @@ export class BuildPipeline extends Pipeline { return this.#manifest; } + async retrieveMiddlewareFunction() { + if (this.#internals.middlewareEntryPoint) { + const middleware = await import(this.#internals.middlewareEntryPoint.toString()); + this.setMiddlewareFunction(middleware.onRequest); + } + } + + getLogger(): Logger { + return this.getEnvironment().logger; + } + /** * The SSR build emits two important files: * - dist/server/manifest.mjs diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts index 224b96b637c1..0a3d800007aa 100644 --- a/packages/astro/src/core/build/generate.ts +++ b/packages/astro/src/core/build/generate.ts @@ -10,7 +10,6 @@ import type { ComponentInstance, GetStaticPathsItem, ImageTransform, - MiddlewareEndpointHandler, RouteData, RouteType, SSRError, @@ -33,7 +32,6 @@ import { runHookBuildGenerated } from '../../integrations/index.js'; import { getOutputDirectory, isServerLikeOutput } from '../../prerender/utils.js'; import { PAGE_SCRIPT_ID } from '../../vite-plugin-scripts/index.js'; import { AstroError, AstroErrorData } from '../errors/index.js'; -import { Logger, debug, info } from '../logger/core.js'; import { RedirectSinglePageBuiltModule, getRedirectLocationOrThrow } from '../redirects/index.js'; import { createRenderContext } from '../render/index.js'; import { callGetStaticPaths } from '../render/route-cache.js'; @@ -121,7 +119,6 @@ export function chunkIsPage( } export async function generatePages(opts: StaticBuildOptions, internals: BuildInternals) { - const logger = new Logger(opts.logging); const timer = performance.now(); const ssr = isServerLikeOutput(opts.settings.config); let manifest: SSRManifest; @@ -137,11 +134,13 @@ export async function generatePages(opts: StaticBuildOptions, internals: BuildIn renderers.renderers as SSRLoadedRenderer[] ); } - const buildPipeline = new BuildPipeline(opts, internals, manifest); + const pipeline = new BuildPipeline(opts, internals, manifest); + await pipeline.retrieveMiddlewareFunction(); const outFolder = ssr ? opts.settings.config.build.server : getOutDirWithinCwd(opts.settings.config.outDir); + const logger = pipeline.getLogger(); // HACK! `astro:assets` relies on a global to know if its running in dev, prod, ssr, ssg, full moon // If we don't delete it here, it's technically not impossible (albeit improbable) for it to leak if (ssr && !hasPrerenderedPages(internals)) { @@ -150,9 +149,9 @@ export async function generatePages(opts: StaticBuildOptions, internals: BuildIn } const verb = ssr ? 'prerendering' : 'generating'; - info(opts.logging, null, `\n${bgGreen(black(` ${verb} static routes `))}`); + logger.info(null, `\n${bgGreen(black(` ${verb} static routes `))}`); const builtPaths = new Set(); - const pagesToGenerate = buildPipeline.retrieveRoutesToGenerate(); + const pagesToGenerate = pipeline.retrieveRoutesToGenerate(); if (ssr) { for (const [pageData, filePath] of pagesToGenerate) { if (pageData.route.prerender) { @@ -166,7 +165,7 @@ export async function generatePages(opts: StaticBuildOptions, internals: BuildIn // forcing to use undefined, so we fail in an expected way if the module is not even there. const ssrEntry = ssrEntryPage?.manifest?.pageModule; if (ssrEntry) { - await generatePage(pageData, ssrEntry, builtPaths, buildPipeline, logger); + await generatePage(pageData, ssrEntry, builtPaths, pipeline); } else { throw new Error( `Unable to find the manifest for the module ${ssrEntryURLPage.toString()}. This is unexpected and likely a bug in Astro, please report.` @@ -174,46 +173,47 @@ export async function generatePages(opts: StaticBuildOptions, internals: BuildIn } } else { const ssrEntry = ssrEntryPage as SinglePageBuiltModule; - await generatePage(pageData, ssrEntry, builtPaths, buildPipeline, logger); + await generatePage(pageData, ssrEntry, builtPaths, pipeline); } } if (pageData.route.type === 'redirect') { const entry = await getEntryForRedirectRoute(pageData.route, internals, outFolder); - await generatePage(pageData, entry, builtPaths, buildPipeline, logger); + await generatePage(pageData, entry, builtPaths, pipeline); } } } else { for (const [pageData, filePath] of pagesToGenerate) { if (pageData.route.type === 'redirect') { const entry = await getEntryForRedirectRoute(pageData.route, internals, outFolder); - await generatePage(pageData, entry, builtPaths, buildPipeline, logger); + await generatePage(pageData, entry, builtPaths, pipeline); } else { const ssrEntryURLPage = createEntryURL(filePath, outFolder); const entry: SinglePageBuiltModule = await import(ssrEntryURLPage.toString()); - await generatePage(pageData, entry, builtPaths, buildPipeline, logger); + await generatePage(pageData, entry, builtPaths, pipeline); } } } - info(opts.logging, null, `\n${bgGreen(black(` generating optimized images `))}`); + logger.info(null, `\n${bgGreen(black(` generating optimized images `))}`); for (const imageData of getStaticImageList()) { - await generateImage(opts, imageData[1].options, imageData[1].path); + await generateImage(pipeline, imageData[1].options, imageData[1].path); } delete globalThis?.astroAsset?.addStaticImage; await runHookBuildGenerated({ config: opts.settings.config, - logging: opts.logging, + logger: pipeline.getLogger(), }); - info(opts.logging, null, dim(`Completed in ${getTimeStat(timer, performance.now())}.\n`)); + logger.info(null, dim(`Completed in ${getTimeStat(timer, performance.now())}.\n`)); } -async function generateImage(opts: StaticBuildOptions, transform: ImageTransform, path: string) { +async function generateImage(pipeline: BuildPipeline, transform: ImageTransform, path: string) { + const logger = pipeline.getLogger(); let timeStart = performance.now(); - const generationData = await generateImageInternal(opts, transform, path); + const generationData = await generateImageInternal(pipeline, transform, path); if (!generationData) { return; @@ -225,18 +225,17 @@ async function generateImage(opts: StaticBuildOptions, transform: ImageTransform const statsText = generationData.cached ? `(reused cache entry)` : `(before: ${generationData.weight.before}kb, after: ${generationData.weight.after}kb)`; - info(opts.logging, null, ` ${green('▶')} ${path} ${dim(statsText)} ${dim(timeIncrease)}`); + logger.info(null, ` ${green('▶')} ${path} ${dim(statsText)} ${dim(timeIncrease)}`); } async function generatePage( pageData: PageBuildData, ssrEntry: SinglePageBuiltModule, builtPaths: Set, - pipeline: BuildPipeline, - logger: Logger + pipeline: BuildPipeline ) { let timeStart = performance.now(); - + const logger = pipeline.getLogger(); const pageInfo = getPageDataByComponent(pipeline.getInternals(), pageData.route.component); // may be used in the future for handling rel=modulepreload, rel=icon, rel=manifest etc. @@ -248,10 +247,6 @@ async function generatePage( .reduce(mergeInlineCss, []); const pageModulePromise = ssrEntry.page; - const onRequest = ssrEntry.onRequest; - if (onRequest) { - pipeline.setMiddlewareFunction(onRequest as MiddlewareEndpointHandler); - } if (!pageModulePromise) { throw new Error( @@ -285,12 +280,7 @@ async function generatePage( } // Get paths for the route, calling getStaticPaths if needed. - const paths = await getPathsForRoute( - pageData, - pageModule, - pipeline.getStaticBuildOptions(), - builtPaths - ); + const paths = await getPathsForRoute(pageData, pageModule, pipeline, builtPaths); let prevTimeEnd = timeStart; for (let i = 0; i < paths.length; i++) { @@ -309,9 +299,11 @@ async function generatePage( async function getPathsForRoute( pageData: PageBuildData, mod: ComponentInstance, - opts: StaticBuildOptions, + pipeline: BuildPipeline, builtPaths: Set ): Promise> { + const opts = pipeline.getStaticBuildOptions(); + const logger = pipeline.getLogger(); let paths: Array = []; if (pageData.route.pathname) { paths.push(pageData.route.pathname); @@ -322,15 +314,15 @@ async function getPathsForRoute( mod, route, routeCache: opts.routeCache, - logging: opts.logging, + logger, ssr: isServerLikeOutput(opts.settings.config), }).catch((err) => { - debug('build', `├── ${colors.bold(colors.red('✗'))} ${route.component}`); + logger.debug('build', `├── ${colors.bold(colors.red('✗'))} ${route.component}`); throw err; }); const label = staticPaths.length === 1 ? 'page' : 'pages'; - debug( + logger.debug( 'build', `├── ${colors.bold(colors.green('✔'))} ${route.component} → ${colors.magenta( `[${staticPaths.length} ${label}]` @@ -477,7 +469,7 @@ async function generatePath(pathname: string, gopts: GeneratePathOptions, pipeli addPageName(pathname, pipeline.getStaticBuildOptions()); } - debug('build', `Generating: ${pathname}`); + pipeline.getEnvironment().logger.debug('build', `Generating: ${pathname}`); // may be used in the future for handling rel=modulepreload, rel=icon, rel=manifest etc. const links = new Set(); @@ -524,7 +516,7 @@ async function generatePath(pathname: string, gopts: GeneratePathOptions, pipeli request: createRequest({ url, headers: new Headers(), - logging: pipeline.getStaticBuildOptions().logging, + logger: pipeline.getLogger(), ssr, }), componentMetadata: manifest.componentMetadata, @@ -612,5 +604,8 @@ export function createBuildManifest( ? new URL(settings.config.base, settings.config.site).toString() : settings.config.site, componentMetadata: internals.componentMetadata, + middlewareEntryPoint: internals.middlewareEntryPoint + ? internals.middlewareEntryPoint.toString() + : undefined, }; } diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts index 894a5e6e203c..013d83a5bc4b 100644 --- a/packages/astro/src/core/build/index.ts +++ b/packages/astro/src/core/build/index.ts @@ -21,10 +21,10 @@ import { } from '../../integrations/index.js'; import { isServerLikeOutput } from '../../prerender/utils.js'; import { resolveConfig } from '../config/config.js'; -import { createNodeLogging } from '../config/logging.js'; +import { createNodeLogger } from '../config/logging.js'; import { createSettings } from '../config/settings.js'; import { createVite } from '../create-vite.js'; -import { debug, info, levels, timerMessage, warn, type LogOptions } from '../logger/core.js'; +import { Logger, levels, timerMessage } from '../logger/core.js'; import { apply as applyPolyfill } from '../polyfill.js'; import { RouteCache } from '../render/route-cache.js'; import { createRouteManifest } from '../routing/index.js'; @@ -55,7 +55,7 @@ export default async function build( options?: BuildOptions ): Promise { applyPolyfill(); - const logging = createNodeLogging(inlineConfig); + const logger = createNodeLogger(inlineConfig); const { userConfig, astroConfig } = await resolveConfig(inlineConfig, 'build'); telemetry.record(eventCliSession('build', userConfig)); @@ -63,20 +63,20 @@ export default async function build( const builder = new AstroBuilder(settings, { ...options, - logging, + logger, mode: inlineConfig.mode, }); await builder.run(); } interface AstroBuilderOptions extends BuildOptions { - logging: LogOptions; + logger: Logger; mode?: RuntimeMode; } class AstroBuilder { private settings: AstroSettings; - private logging: LogOptions; + private logger: Logger; private mode: RuntimeMode = 'production'; private origin: string; private routeCache: RouteCache; @@ -89,9 +89,9 @@ class AstroBuilder { this.mode = options.mode; } this.settings = settings; - this.logging = options.logging; + this.logger = options.logger; this.teardownCompiler = options.teardownCompiler ?? true; - this.routeCache = new RouteCache(this.logging); + this.routeCache = new RouteCache(this.logger); this.origin = settings.config.site ? new URL(settings.config.site).origin : `http://localhost:${settings.config.server.port}`; @@ -101,20 +101,20 @@ class AstroBuilder { /** Setup Vite and run any async setup logic that couldn't run inside of the constructor. */ private async setup() { - debug('build', 'Initial setup...'); - const { logging } = this; + this.logger.debug('build', 'Initial setup...'); + const { logger } = this; this.timer.init = performance.now(); this.settings = await runHookConfigSetup({ settings: this.settings, command: 'build', - logging, + logger: logger, }); if (isServerLikeOutput(this.settings.config)) { this.settings = injectImageEndpoint(this.settings); } - this.manifest = createRouteManifest({ settings: this.settings }, this.logging); + this.manifest = createRouteManifest({ settings: this.settings }, this.logger); const viteConfig = await createVite( { @@ -124,12 +124,12 @@ class AstroBuilder { middlewareMode: true, }, }, - { settings: this.settings, logging, mode: 'build', command: 'build' } + { settings: this.settings, logger: this.logger, mode: 'build', command: 'build' } ); - await runHookConfigDone({ settings: this.settings, logging }); + await runHookConfigDone({ settings: this.settings, logger: logger }); const { syncInternal } = await import('../sync/index.js'); - const syncRet = await syncInternal(this.settings, { logging, fs }); + const syncRet = await syncInternal(this.settings, { logger: logger, fs }); if (syncRet !== 0) { return process.exit(syncRet); } @@ -139,22 +139,22 @@ class AstroBuilder { /** Run the build logic. build() is marked private because usage should go through ".run()" */ private async build({ viteConfig }: { viteConfig: vite.InlineConfig }) { - await runHookBuildStart({ config: this.settings.config, logging: this.logging }); + await runHookBuildStart({ config: this.settings.config, logging: this.logger }); this.validateConfig(); - info(this.logging, 'build', `output target: ${colors.green(this.settings.config.output)}`); + this.logger.info('build', `output target: ${colors.green(this.settings.config.output)}`); if (this.settings.adapter) { - info(this.logging, 'build', `deploy adapter: ${colors.green(this.settings.adapter.name)}`); + this.logger.info('build', `deploy adapter: ${colors.green(this.settings.adapter.name)}`); } - info(this.logging, 'build', 'Collecting build info...'); + this.logger.info('build', 'Collecting build info...'); this.timer.loadStart = performance.now(); const { assets, allPages } = await collectPagesData({ settings: this.settings, - logging: this.logging, + logger: this.logger, manifest: this.manifest, }); - debug('build', timerMessage('All pages loaded', this.timer.loadStart)); + this.logger.debug('build', timerMessage('All pages loaded', this.timer.loadStart)); // The names of each pages const pageNames: string[] = []; @@ -162,8 +162,7 @@ class AstroBuilder { // Bundle the assets in your final build: This currently takes the HTML output // of every page (stored in memory) and bundles the assets pointed to on those pages. this.timer.buildStart = performance.now(); - info( - this.logging, + this.logger.info( 'build', colors.dim(`Completed in ${getTimeStat(this.timer.init, performance.now())}.`) ); @@ -171,7 +170,7 @@ class AstroBuilder { const opts: StaticBuildOptions = { allPages, settings: this.settings, - logging: this.logging, + logger: this.logger, manifest: this.manifest, mode: this.mode, origin: this.origin, @@ -193,19 +192,19 @@ class AstroBuilder { fs.writeFileSync(filePath, assets[k], 'utf8'); delete assets[k]; // free up memory }); - debug('build', timerMessage('Additional assets copied', this.timer.assetsStart)); + this.logger.debug('build', timerMessage('Additional assets copied', this.timer.assetsStart)); // You're done! Time to clean up. await runHookBuildDone({ config: this.settings.config, pages: pageNames, routes: Object.values(allPages).map((pd) => pd.route), - logging: this.logging, + logging: this.logger, }); - if (this.logging.level && levels[this.logging.level] <= levels['info']) { + if (this.logger.level && levels[this.logger.level()] <= levels['info']) { await this.printStats({ - logging: this.logging, + logger: this.logger, timeStart: this.timer.init, pageCount: pageNames.length, buildMode: this.settings.config.output, @@ -238,8 +237,7 @@ class AstroBuilder { if (config.build.split === true) { if (config.output === 'static') { - warn( - this.logging, + this.logger.warn( 'configuration', 'The option `build.split` won\'t take effect, because `output` is not `"server"` or `"hybrid"`.' ); @@ -247,8 +245,7 @@ class AstroBuilder { } if (config.build.excludeMiddleware === true) { if (config.output === 'static') { - warn( - this.logging, + this.logger.warn( 'configuration', 'The option `build.excludeMiddleware` won\'t take effect, because `output` is not `"server"` or `"hybrid"`.' ); @@ -266,12 +263,12 @@ class AstroBuilder { /** Stats */ private async printStats({ - logging, + logger, timeStart, pageCount, buildMode, }: { - logging: LogOptions; + logger: Logger; timeStart: number; pageCount: number; buildMode: AstroConfig['output']; @@ -285,7 +282,7 @@ class AstroBuilder { messages = ['Server built in', colors.bold(total)]; } - info(logging, 'build', messages.join(' ')); - info(logging, 'build', `${colors.bold('Complete!')}`); + logger.info('build', messages.join(' ')); + logger.info('build', `${colors.bold('Complete!')}`); } } diff --git a/packages/astro/src/core/build/page-data.ts b/packages/astro/src/core/build/page-data.ts index 0a53745c55b1..ea75a0c0b716 100644 --- a/packages/astro/src/core/build/page-data.ts +++ b/packages/astro/src/core/build/page-data.ts @@ -1,6 +1,5 @@ import type { AstroSettings, ManifestData } from '../../@types/astro'; -import type { LogOptions } from '../logger/core'; -import { info } from '../logger/core.js'; +import type { Logger } from '../logger/core'; import type { AllPagesData } from './types'; import * as colors from 'kleur/colors'; @@ -8,7 +7,7 @@ import { debug } from '../logger/core.js'; export interface CollectPagesDataOptions { settings: AstroSettings; - logging: LogOptions; + logger: Logger; manifest: ManifestData; } @@ -27,7 +26,7 @@ export async function collectPagesData( const allPages: AllPagesData = {}; const builtPaths = new Set(); const dataCollectionLogTimeout = setInterval(() => { - info(opts.logging, 'build', 'The data collection step may take longer for larger projects...'); + opts.logger.info('build', 'The data collection step may take longer for larger projects...'); clearInterval(dataCollectionLogTimeout); }, 30000); @@ -39,8 +38,7 @@ export async function collectPagesData( // static route: if (route.pathname) { const routeCollectionLogTimeout = setInterval(() => { - info( - opts.logging, + opts.logger.info( 'build', `${colors.bold( route.component diff --git a/packages/astro/src/core/build/plugins/plugin-manifest.ts b/packages/astro/src/core/build/plugins/plugin-manifest.ts index a7b254554372..7f6846a63284 100644 --- a/packages/astro/src/core/build/plugins/plugin-manifest.ts +++ b/packages/astro/src/core/build/plugins/plugin-manifest.ts @@ -93,14 +93,19 @@ export function pluginManifest( } const manifest = await createManifest(options, internals); + const shouldPassMiddlewareEntryPoint = + // TODO: remove in Astro 4.0 + options.settings.config.build.excludeMiddleware || + options.settings.adapter?.adapterFeatures?.edgeMiddleware; await runHookBuildSsr({ config: options.settings.config, manifest, - logging: options.logging, + logger: options.logger, entryPoints: internals.entryPoints, - middlewareEntryPoint: internals.middlewareEntryPoint, + middlewareEntryPoint: shouldPassMiddlewareEntryPoint + ? internals.middlewareEntryPoint + : undefined, }); - // TODO: use the manifest entry chunk instead const code = injectManifest(manifest, internals.manifestEntryChunk); mutate(internals.manifestEntryChunk, 'server', code); }, @@ -232,6 +237,9 @@ function buildManifest( // Set this to an empty string so that the runtime knows not to try and load this. entryModules[BEFORE_HYDRATION_SCRIPT_ID] = ''; } + const isEdgeMiddleware = + // TODO: remove in Astro 4.0 + settings.config.build.excludeMiddleware || settings.adapter?.adapterFeatures?.edgeMiddleware; const ssrManifest: SerializedSSRManifest = { adapterName: opts.settings.adapter?.name ?? '', @@ -245,6 +253,9 @@ function buildManifest( clientDirectives: Array.from(settings.clientDirectives), entryModules, assets: staticFiles.map(prefixAssetPath), + middlewareEntryPoint: !isEdgeMiddleware + ? internals.middlewareEntryPoint?.toString() + : undefined, }; return ssrManifest; diff --git a/packages/astro/src/core/build/plugins/plugin-middleware.ts b/packages/astro/src/core/build/plugins/plugin-middleware.ts index 5ed532d5e88e..99853c7b1f61 100644 --- a/packages/astro/src/core/build/plugins/plugin-middleware.ts +++ b/packages/astro/src/core/build/plugins/plugin-middleware.ts @@ -1,4 +1,5 @@ import type { Plugin as VitePlugin } from 'vite'; +import { getOutputDirectory } from '../../../prerender/utils.js'; import { MIDDLEWARE_PATH_SEGMENT_NAME } from '../../constants.js'; import { addRollupInput } from '../add-rollup-input.js'; import type { BuildInternals } from '../internal'; @@ -56,8 +57,9 @@ export function vitePluginMiddleware( if (chunk.type === 'asset') { continue; } - if (chunk.fileName === 'middleware.mjs' && opts.settings.config.build.excludeMiddleware) { - internals.middlewareEntryPoint = new URL(chunkName, opts.settings.config.build.server); + if (chunk.fileName === 'middleware.mjs') { + const outputDirectory = getOutputDirectory(opts.settings.config); + internals.middlewareEntryPoint = new URL(chunkName, outputDirectory); } } }, diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts index 6227e99c6d5c..0bfb4e3ae0ae 100644 --- a/packages/astro/src/core/build/static-build.ts +++ b/packages/astro/src/core/build/static-build.ts @@ -19,7 +19,6 @@ import { runHookBuildSetup } from '../../integrations/index.js'; import { getOutputDirectory, isServerLikeOutput } from '../../prerender/utils.js'; import { PAGE_SCRIPT_ID } from '../../vite-plugin-scripts/index.js'; import { AstroError, AstroErrorData } from '../errors/index.js'; -import { info } from '../logger/core.js'; import { routeIsRedirect } from '../redirects/index.js'; import { getOutDirWithinCwd } from './common.js'; import { generatePages } from './generate.js'; @@ -80,9 +79,9 @@ export async function viteBuild(opts: StaticBuildOptions) { // Build your project (SSR application code, assets, client JS, etc.) const ssrTime = performance.now(); - info(opts.logging, 'build', `Building ${settings.config.output} entrypoints...`); + opts.logger.info('build', `Building ${settings.config.output} entrypoints...`); const ssrOutput = await ssrBuild(opts, internals, pageInput, container); - info(opts.logging, 'build', dim(`Completed in ${getTimeStat(ssrTime, performance.now())}.`)); + opts.logger.info('build', dim(`Completed in ${getTimeStat(ssrTime, performance.now())}.`)); settings.timer.end('SSR build'); settings.timer.start('Client build'); @@ -132,7 +131,7 @@ export async function staticBuild(opts: StaticBuildOptions, internals: BuildInte settings.timer.start('Server generate'); await generatePages(opts, internals); await cleanStaticOutput(opts, internals); - info(opts.logging, null, `\n${bgMagenta(black(' finalizing server assets '))}\n`); + opts.logger.info(null, `\n${bgMagenta(black(' finalizing server assets '))}\n`); await ssrMoveAssets(opts); settings.timer.end('Server generate'); return; @@ -214,7 +213,7 @@ async function ssrBuild( pages: internals.pagesByComponent, vite: viteBuildConfig, target: 'server', - logging: opts.logging, + logger: opts.logger, }); return await vite.build(updatedViteBuildConfig); @@ -242,7 +241,7 @@ async function clientBuild( } const { lastVitePlugins, vitePlugins } = container.runBeforeHook('client', input); - info(opts.logging, null, `\n${bgGreen(black(' building client '))}`); + opts.logger.info(null, `\n${bgGreen(black(' building client '))}`); const viteBuildConfig: vite.InlineConfig = { ...viteConfig, @@ -276,11 +275,11 @@ async function clientBuild( pages: internals.pagesByComponent, vite: viteBuildConfig, target: 'client', - logging: opts.logging, + logger: opts.logger, }); const buildResult = await vite.build(viteBuildConfig); - info(opts.logging, null, dim(`Completed in ${getTimeStat(timer, performance.now())}.\n`)); + opts.logger.info(null, dim(`Completed in ${getTimeStat(timer, performance.now())}.\n`)); return buildResult; } @@ -403,7 +402,7 @@ async function copyFiles(fromFolder: URL, toFolder: URL, includeDotfiles = false } async function ssrMoveAssets(opts: StaticBuildOptions) { - info(opts.logging, 'build', 'Rearranging server assets...'); + opts.logger.info('build', 'Rearranging server assets...'); const serverRoot = opts.settings.config.output === 'static' ? opts.settings.config.build.client diff --git a/packages/astro/src/core/build/types.ts b/packages/astro/src/core/build/types.ts index 472dc4b34272..c45249e7e5c9 100644 --- a/packages/astro/src/core/build/types.ts +++ b/packages/astro/src/core/build/types.ts @@ -4,12 +4,11 @@ import type { AstroSettings, ComponentInstance, ManifestData, - MiddlewareHandler, RouteData, RuntimeMode, SSRLoadedRenderer, } from '../../@types/astro'; -import type { LogOptions } from '../logger/core'; +import type { Logger } from '../logger/core'; import type { RouteCache } from '../render/route-cache'; export type ComponentPath = string; @@ -35,7 +34,7 @@ export type AllPagesData = Record; export interface StaticBuildOptions { allPages: AllPagesData; settings: AstroSettings; - logging: LogOptions; + logger: Logger; manifest: ManifestData; mode: RuntimeMode; origin: string; @@ -52,7 +51,6 @@ export interface SinglePageBuiltModule { /** * The `onRequest` hook exported by the middleware */ - onRequest?: MiddlewareHandler; renderers: SSRLoadedRenderer[]; } diff --git a/packages/astro/src/core/config/index.ts b/packages/astro/src/core/config/index.ts index 4699a624c84a..758e832bfe92 100644 --- a/packages/astro/src/core/config/index.ts +++ b/packages/astro/src/core/config/index.ts @@ -1,5 +1,5 @@ export { resolveConfig, resolveConfigPath, resolveFlags, resolveRoot } from './config.js'; -export { createNodeLogging } from './logging.js'; +export { createNodeLogger } from './logging.js'; export { mergeConfig } from './merge.js'; export type { AstroConfigType } from './schema'; export { createSettings } from './settings.js'; diff --git a/packages/astro/src/core/config/logging.ts b/packages/astro/src/core/config/logging.ts index ea0b29b8820f..004283f85f0c 100644 --- a/packages/astro/src/core/config/logging.ts +++ b/packages/astro/src/core/config/logging.ts @@ -1,13 +1,12 @@ import type { AstroInlineConfig } from '../../@types/astro.js'; -import type { LogOptions } from '../logger/core.js'; +import { Logger } from '../logger/core.js'; import { nodeLogDestination } from '../logger/node.js'; -export function createNodeLogging(inlineConfig: AstroInlineConfig): LogOptions { - // For internal testing, the inline config can pass the raw `logging` object directly - if (inlineConfig.logging) return inlineConfig.logging; +export function createNodeLogger(inlineConfig: AstroInlineConfig): Logger { + if (inlineConfig.logger) return inlineConfig.logger; - return { + return new Logger({ dest: nodeLogDestination, level: inlineConfig.logLevel ?? 'info', - }; + }); } diff --git a/packages/astro/src/core/config/schema.ts b/packages/astro/src/core/config/schema.ts index 183b2eb446eb..21d15314336d 100644 --- a/packages/astro/src/core/config/schema.ts +++ b/packages/astro/src/core/config/schema.ts @@ -405,7 +405,7 @@ export function createRelativeSchema(cmd: string, fileProtocolRoot: string) { }) .refine((obj) => !obj.outDir.toString().startsWith(obj.publicDir.toString()), { message: - '`outDir` must not be placed inside `publicDir` to prevent an infinite loop. Please adjust the directory configuration and try again', + 'The value of `outDir` must not point to a path within the folder set as `publicDir`, this will cause an infinite loop', }); return AstroConfigRelativeSchema; diff --git a/packages/astro/src/core/create-vite.ts b/packages/astro/src/core/create-vite.ts index 6af07562d34f..8adf3cac868b 100644 --- a/packages/astro/src/core/create-vite.ts +++ b/packages/astro/src/core/create-vite.ts @@ -1,5 +1,5 @@ import type { AstroSettings } from '../@types/astro'; -import type { LogOptions } from './logger/core'; +import type { Logger } from './logger/core'; import nodeFs from 'node:fs'; import { fileURLToPath } from 'node:url'; @@ -32,7 +32,7 @@ import { joinPaths } from './path.js'; interface CreateViteOptions { settings: AstroSettings; - logging: LogOptions; + logger: Logger; mode: 'dev' | 'build' | string; // will be undefined when using `getViteConfig` command?: 'dev' | 'build'; @@ -66,7 +66,7 @@ const ONLY_DEV_EXTERNAL = [ /** Return a common starting point for all Vite actions */ export async function createVite( commandConfig: vite.InlineConfig, - { settings, logging, mode, command, fs = nodeFs }: CreateViteOptions + { settings, logger, mode, command, fs = nodeFs }: CreateViteOptions ): Promise { const astroPkgsConfig = await crawlFrameworkPkgs({ root: fileURLToPath(settings.config.root), @@ -113,26 +113,26 @@ export async function createVite( plugins: [ configAliasVitePlugin({ settings }), astroLoadFallbackPlugin({ fs, root: settings.config.root }), - astroVitePlugin({ settings, logging }), + astroVitePlugin({ settings, logger }), astroScriptsPlugin({ settings }), // The server plugin is for dev only and having it run during the build causes // the build to run very slow as the filewatcher is triggered often. - mode !== 'build' && vitePluginAstroServer({ settings, logging, fs }), + mode !== 'build' && vitePluginAstroServer({ settings, logger, fs }), envVitePlugin({ settings }), - markdownVitePlugin({ settings, logging }), + markdownVitePlugin({ settings, logger }), htmlVitePlugin(), - mdxVitePlugin({ settings, logging }), + mdxVitePlugin({ settings, logger }), astroPostprocessVitePlugin(), - astroIntegrationsContainerPlugin({ settings, logging }), + astroIntegrationsContainerPlugin({ settings, logger }), astroScriptsPageSSRPlugin({ settings }), astroHeadPlugin(), - astroScannerPlugin({ settings, logging }), - astroInjectEnvTsPlugin({ settings, logging, fs }), + astroScannerPlugin({ settings, logger }), + astroInjectEnvTsPlugin({ settings, logger, fs }), astroContentVirtualModPlugin({ settings }), astroContentImportPlugin({ fs, settings }), astroContentAssetPropagationPlugin({ mode, settings }), vitePluginSSRManifest(), - astroAssetsPlugin({ settings, logging, mode }), + astroAssetsPlugin({ settings, logger, mode }), astroTransitions(), ], publicDir: fileURLToPath(settings.config.publicDir), diff --git a/packages/astro/src/core/dev/container.ts b/packages/astro/src/core/dev/container.ts index cd7ca3b0ed97..ed318622f9b8 100644 --- a/packages/astro/src/core/dev/container.ts +++ b/packages/astro/src/core/dev/container.ts @@ -12,12 +12,12 @@ import { runHookServerStart, } from '../../integrations/index.js'; import { createVite } from '../create-vite.js'; -import type { LogOptions } from '../logger/core.js'; +import type { Logger } from '../logger/core.js'; import { apply as applyPolyfill } from '../polyfill.js'; export interface Container { fs: typeof nodeFs; - logging: LogOptions; + logger: Logger; settings: AstroSettings; viteServer: vite.ViteDevServer; inlineConfig: AstroInlineConfig; @@ -27,7 +27,7 @@ export interface Container { } export interface CreateContainerParams { - logging: LogOptions; + logger: Logger; settings: AstroSettings; inlineConfig?: AstroInlineConfig; isRestart?: boolean; @@ -36,7 +36,7 @@ export interface CreateContainerParams { export async function createContainer({ isRestart = false, - logging, + logger, inlineConfig, settings, fs = nodeFs, @@ -46,7 +46,7 @@ export async function createContainer({ settings = await runHookConfigSetup({ settings, command: 'dev', - logging, + logger: logger, isRestart, }); @@ -73,15 +73,15 @@ export async function createContainer({ include: rendererClientEntries, }, }, - { settings, logging, mode: 'dev', command: 'dev', fs } + { settings, logger, mode: 'dev', command: 'dev', fs } ); - await runHookConfigDone({ settings, logging }); + await runHookConfigDone({ settings, logger }); const viteServer = await vite.createServer(viteConfig); const container: Container = { inlineConfig: inlineConfig ?? {}, fs, - logging, + logger, restartInFlight: false, settings, viteServer, @@ -97,18 +97,18 @@ export async function createContainer({ return container; } -async function closeContainer({ viteServer, settings, logging }: Container) { +async function closeContainer({ viteServer, settings, logger }: Container) { await viteServer.close(); await runHookServerDone({ config: settings.config, - logging, + logger, }); } export async function startContainer({ settings, viteServer, - logging, + logger, }: Container): Promise { const { port } = settings.config.server; await viteServer.listen(port); @@ -116,7 +116,7 @@ export async function startContainer({ await runHookServerStart({ config: settings.config, address: devServerAddressInfo, - logging, + logger, }); return devServerAddressInfo; diff --git a/packages/astro/src/core/dev/dev.ts b/packages/astro/src/core/dev/dev.ts index 115cbe825827..95555a533e91 100644 --- a/packages/astro/src/core/dev/dev.ts +++ b/packages/astro/src/core/dev/dev.ts @@ -6,7 +6,6 @@ import type * as vite from 'vite'; import type { AstroInlineConfig } from '../../@types/astro'; import { attachContentServerListeners } from '../../content/index.js'; import { telemetry } from '../../events/index.js'; -import { info, warn } from '../logger/core.js'; import * as msg from '../messages.js'; import { startContainer } from './container.js'; import { createContainerWithAutomaticRestart } from './restart.js'; @@ -30,13 +29,12 @@ export default async function dev(inlineConfig: AstroInlineConfig): Promise { - const { logging, fs, inlineConfig } = container; + const { logger, fs, inlineConfig } = container; const newContainer = await createContainer({ isRestart: true, - logging, + logger: logger, settings, inlineConfig, fs, @@ -60,7 +59,7 @@ export function shouldRestartContainer( } export async function restartContainer(container: Container): Promise { - const { logging, close, settings: existingSettings } = container; + const { logger, close, settings: existingSettings } = container; container.restartInFlight = true; try { @@ -72,7 +71,7 @@ export async function restartContainer(container: Container): Promise { - const logging = createNodeLogging(inlineConfig ?? {}); + const logger = createNodeLogger(inlineConfig ?? {}); const { userConfig, astroConfig } = await resolveConfig(inlineConfig ?? {}, 'dev', fs); telemetry.record(eventCliSession('dev', userConfig)); const settings = createSettings(astroConfig, fileURLToPath(astroConfig.root)); - const initialContainer = await createContainer({ settings, logging, inlineConfig, fs }); + const initialContainer = await createContainer({ settings, logger: logger, inlineConfig, fs }); let resolveRestart: (value: Error | null) => void; let restartComplete = new Promise((resolve) => { @@ -123,7 +122,7 @@ export async function createContainerWithAutomaticRestart({ }; async function handleServerRestart(logMsg: string) { - info(logging, 'astro', logMsg + '\n'); + logger.info('astro', logMsg + '\n'); const container = restart.container; const result = await restartContainer(container); if (result instanceof Error) { diff --git a/packages/astro/src/core/endpoint/index.ts b/packages/astro/src/core/endpoint/index.ts index 11ade7f335fc..a6e0627270c7 100644 --- a/packages/astro/src/core/endpoint/index.ts +++ b/packages/astro/src/core/endpoint/index.ts @@ -11,7 +11,6 @@ import { renderEndpoint } from '../../runtime/server/index.js'; import { ASTRO_VERSION } from '../constants.js'; import { AstroCookies, attachCookiesToResponse } from '../cookies/index.js'; import { AstroError, AstroErrorData } from '../errors/index.js'; -import { warn } from '../logger/core.js'; import { callMiddleware } from '../middleware/callMiddleware.js'; import type { Environment, RenderContext } from '../render/index'; @@ -132,23 +131,22 @@ export async function callEndpoint let response; if (onRequest) { response = await callMiddleware( - env.logging, + env.logger, onRequest as MiddlewareEndpointHandler, context, async () => { - return await renderEndpoint(mod, context, env.ssr, env.logging); + return await renderEndpoint(mod, context, env.ssr, env.logger); } ); } else { - response = await renderEndpoint(mod, context, env.ssr, env.logging); + response = await renderEndpoint(mod, context, env.ssr, env.logger); } const isEndpointSSR = env.ssr && !ctx.route?.prerender; if (response instanceof Response) { if (isEndpointSSR && response.headers.get('X-Astro-Encoding')) { - warn( - env.logging, + env.logger.warn( 'ssr', '`ResponseWithEncoding` is ignored in SSR. Please return an instance of Response. See https://docs.astro.build/en/core-concepts/endpoints/#server-endpoints-api-routes for more information.' ); @@ -160,24 +158,21 @@ export async function callEndpoint // The endpoint returned a simple object, convert it to a Response // TODO: Remove in Astro 4.0 - warn( - env.logging, + env.logger.warn( 'astro', `${ctx.route.component} returns a simple object which is deprecated. Please return an instance of Response. See https://docs.astro.build/en/core-concepts/endpoints/#server-endpoints-api-routes for more information.` ); if (isEndpointSSR) { if (response.hasOwnProperty('headers')) { - warn( - env.logging, + env.logger.warn( 'ssr', 'Setting headers is not supported when returning an object. Please return an instance of Response. See https://docs.astro.build/en/core-concepts/endpoints/#server-endpoints-api-routes for more information.' ); } if (response.encoding) { - warn( - env.logging, + env.logger.warn( 'ssr', '`encoding` is ignored in SSR. To return a charset other than UTF-8, please return an instance of Response. See https://docs.astro.build/en/core-concepts/endpoints/#server-endpoints-api-routes for more information.' ); diff --git a/packages/astro/src/core/logger/core.ts b/packages/astro/src/core/logger/core.ts index c92cdbb249ae..a797848356ac 100644 --- a/packages/astro/src/core/logger/core.ts +++ b/packages/astro/src/core/logger/core.ts @@ -142,8 +142,16 @@ export class Logger { error(label: string | null, message: string) { error(this.options, label, message); } - debug(label: string | null, message: string) { - debug(this.options, label, message); + debug(label: string | null, message: string, ...args: any[]) { + debug(this.options, label, message, args); + } + + level() { + return this.options.level; + } + + forkIntegrationLogger(label: string) { + return new AstroIntegrationLogger(this.options, label); } } diff --git a/packages/astro/src/core/middleware/callMiddleware.ts b/packages/astro/src/core/middleware/callMiddleware.ts index afa0156c99b1..b83fa4322d49 100644 --- a/packages/astro/src/core/middleware/callMiddleware.ts +++ b/packages/astro/src/core/middleware/callMiddleware.ts @@ -6,7 +6,6 @@ import type { MiddlewareNext, } from '../../@types/astro'; import { AstroError, AstroErrorData } from '../errors/index.js'; -import { warn } from '../logger/core.js'; import type { Environment } from '../render'; /** @@ -44,7 +43,7 @@ import type { Environment } from '../render'; * @param responseFunction A callback function that should return a promise with the response */ export async function callMiddleware( - logging: Environment['logging'], + logger: Environment['logger'], onRequest: MiddlewareHandler, apiContext: APIContext, responseFunction: () => Promise @@ -61,8 +60,7 @@ export async function callMiddleware( return await Promise.resolve(middlewarePromise).then(async (value) => { if (isEndpointOutput(value)) { - warn( - logging, + logger.warn( 'middleware', 'Using simple endpoints can cause unexpected issues in the chain of middleware functions.' + `\nIt's strongly suggested to use full ${bold('Response')} objects.` diff --git a/packages/astro/src/core/pipeline.ts b/packages/astro/src/core/pipeline.ts index 238f19fbf6cb..0042c288ceaf 100644 --- a/packages/astro/src/core/pipeline.ts +++ b/packages/astro/src/core/pipeline.ts @@ -116,7 +116,7 @@ export class Pipeline { case 'redirect': { if (onRequest) { return await callMiddleware( - env.logging, + env.logger, onRequest as MiddlewareResponseHandler, apiContext, () => { diff --git a/packages/astro/src/core/preview/index.ts b/packages/astro/src/core/preview/index.ts index d5b2b2db76bf..a4e7e7a7b099 100644 --- a/packages/astro/src/core/preview/index.ts +++ b/packages/astro/src/core/preview/index.ts @@ -5,7 +5,7 @@ import { telemetry } from '../../events/index.js'; import { eventCliSession } from '../../events/session.js'; import { runHookConfigDone, runHookConfigSetup } from '../../integrations/index.js'; import { resolveConfig } from '../config/config.js'; -import { createNodeLogging } from '../config/logging.js'; +import { createNodeLogger } from '../config/logging.js'; import { createSettings } from '../config/settings.js'; import createStaticPreviewServer from './static-preview-server.js'; import { getResolvedHostForHttpServer } from './util.js'; @@ -17,7 +17,7 @@ import { getResolvedHostForHttpServer } from './util.js'; * @experimental The JavaScript API is experimental */ export default async function preview(inlineConfig: AstroInlineConfig): Promise { - const logging = createNodeLogging(inlineConfig); + const logger = createNodeLogger(inlineConfig); const { userConfig, astroConfig } = await resolveConfig(inlineConfig ?? {}, 'preview'); telemetry.record(eventCliSession('preview', userConfig)); @@ -26,12 +26,12 @@ export default async function preview(inlineConfig: AstroInlineConfig): Promise< const settings = await runHookConfigSetup({ settings: _settings, command: 'preview', - logging: logging, + logger: logger, }); - await runHookConfigDone({ settings: settings, logging: logging }); + await runHookConfigDone({ settings: settings, logger: logger }); if (settings.config.output === 'static') { - const server = await createStaticPreviewServer(settings, logging); + const server = await createStaticPreviewServer(settings, logger); return server; } if (!settings.adapter) { diff --git a/packages/astro/src/core/preview/static-preview-server.ts b/packages/astro/src/core/preview/static-preview-server.ts index 1fdc90c2aad3..b1c882714ef3 100644 --- a/packages/astro/src/core/preview/static-preview-server.ts +++ b/packages/astro/src/core/preview/static-preview-server.ts @@ -4,8 +4,7 @@ import { performance } from 'perf_hooks'; import enableDestroy from 'server-destroy'; import { preview, type PreviewServer as VitePreviewServer } from 'vite'; import type { AstroSettings } from '../../@types/astro'; -import type { LogOptions } from '../logger/core'; -import { error, info } from '../logger/core.js'; +import type { Logger } from '../logger/core'; import * as msg from '../messages.js'; import { getResolvedHostForHttpServer } from './util.js'; import { vitePluginAstroPreview } from './vite-plugin-astro-preview.js'; @@ -20,7 +19,7 @@ export interface PreviewServer { export default async function createStaticPreviewServer( settings: AstroSettings, - logging: LogOptions + logger: Logger ): Promise { const startServerTime = performance.now(); @@ -43,7 +42,7 @@ export default async function createStaticPreviewServer( }); } catch (err) { if (err instanceof Error) { - error(logging, 'astro', err.stack || err.message); + logger.error('astro', err.stack || err.message); } throw err; } @@ -51,8 +50,7 @@ export default async function createStaticPreviewServer( enableDestroy(previewServer.httpServer); // Log server start URLs - info( - logging, + logger.info( null, msg.serverStart({ startupTime: performance.now() - startServerTime, diff --git a/packages/astro/src/core/redirects/component.ts b/packages/astro/src/core/redirects/component.ts index d10cae4feed5..93b1aa732ca0 100644 --- a/packages/astro/src/core/redirects/component.ts +++ b/packages/astro/src/core/redirects/component.ts @@ -12,6 +12,5 @@ export const RedirectComponentInstance: ComponentInstance = { export const RedirectSinglePageBuiltModule: SinglePageBuiltModule = { page: () => Promise.resolve(RedirectComponentInstance), - onRequest: (ctx, next) => next(), renderers: [], }; diff --git a/packages/astro/src/core/render/context.ts b/packages/astro/src/core/render/context.ts index d767d7910675..d6806d682dcb 100644 --- a/packages/astro/src/core/render/context.ts +++ b/packages/astro/src/core/render/context.ts @@ -48,7 +48,7 @@ export async function createRenderContext( route: options.route, routeCache: options.env.routeCache, pathname: pathname, - logging: options.env.logging, + logger: options.env.logger, ssr: options.env.ssr, }); diff --git a/packages/astro/src/core/render/core.ts b/packages/astro/src/core/render/core.ts index fb10ccc8067f..3efbddb99595 100644 --- a/packages/astro/src/core/render/core.ts +++ b/packages/astro/src/core/render/core.ts @@ -8,7 +8,6 @@ import type { import { renderPage as runtimeRenderPage } from '../../runtime/server/index.js'; import { attachCookiesToResponse } from '../cookies/index.js'; import { callEndpoint, createAPIContext } from '../endpoint/index.js'; -import { warn } from '../logger/core.js'; import { callMiddleware } from '../middleware/callMiddleware.js'; import { redirectRouteGenerate, redirectRouteStatus, routeIsRedirect } from '../redirects/index.js'; import type { RenderContext } from './context.js'; @@ -41,7 +40,7 @@ export async function renderPage({ mod, renderContext, env, cookies }: RenderPag adapterName: env.adapterName, links: renderContext.links, styles: renderContext.styles, - logging: env.logging, + logger: env.logger, params: renderContext.params, pathname: renderContext.pathname, componentMetadata: renderContext.componentMetadata, @@ -60,8 +59,7 @@ export async function renderPage({ mod, renderContext, env, cookies }: RenderPag // TODO: Remove in Astro 4.0 if (mod.frontmatter && typeof mod.frontmatter === 'object' && 'draft' in mod.frontmatter) { - warn( - env.logging, + env.logger.warn( 'astro', `The drafts feature is deprecated and used in ${renderContext.route.component}. You should migrate to content collections instead. See https://docs.astro.build/en/guides/content-collections/#filtering-collection-queries for more information.` ); @@ -115,7 +113,7 @@ export async function tryRenderRoute( case 'redirect': { if (onRequest) { return await callMiddleware( - env.logging, + env.logger, onRequest as MiddlewareResponseHandler, apiContext, () => { diff --git a/packages/astro/src/core/render/environment.ts b/packages/astro/src/core/render/environment.ts index f38d98551d46..a41a0b09e9a1 100644 --- a/packages/astro/src/core/render/environment.ts +++ b/packages/astro/src/core/render/environment.ts @@ -1,5 +1,5 @@ import type { RuntimeMode, SSRLoadedRenderer } from '../../@types/astro'; -import type { LogOptions } from '../logger/core.js'; +import type { Logger } from '../logger/core.js'; import type { RouteCache } from './route-cache.js'; /** @@ -13,7 +13,7 @@ export interface Environment { */ adapterName?: string; /** logging options */ - logging: LogOptions; + logger: Logger; /** "development" or "production" */ mode: RuntimeMode; compressHTML: boolean; diff --git a/packages/astro/src/core/render/params-and-props.ts b/packages/astro/src/core/render/params-and-props.ts index fc08c495e71f..2cde3379c688 100644 --- a/packages/astro/src/core/render/params-and-props.ts +++ b/packages/astro/src/core/render/params-and-props.ts @@ -1,6 +1,6 @@ import type { ComponentInstance, Params, Props, RouteData } from '../../@types/astro'; import { AstroError, AstroErrorData } from '../errors/index.js'; -import type { LogOptions } from '../logger/core.js'; +import type { Logger } from '../logger/core.js'; import { getParams } from '../routing/params.js'; import { RouteCache, callGetStaticPaths, findPathItemByKey } from './route-cache.js'; @@ -9,12 +9,12 @@ interface GetParamsAndPropsOptions { route?: RouteData | undefined; routeCache: RouteCache; pathname: string; - logging: LogOptions; + logger: Logger; ssr: boolean; } export async function getParamsAndProps(opts: GetParamsAndPropsOptions): Promise<[Params, Props]> { - const { logging, mod, route, routeCache, pathname, ssr } = opts; + const { logger, mod, route, routeCache, pathname, ssr } = opts; // If there's no route, or if there's a pathname (e.g. a static `src/pages/normal.astro` file), // then we know for sure they don't have params and props, return a fallback value. @@ -33,11 +33,11 @@ export async function getParamsAndProps(opts: GetParamsAndPropsOptions): Promise mod, route, routeCache, - logging, + logger, ssr, }); - const matchedStaticPath = findPathItemByKey(staticPaths, params, route); + const matchedStaticPath = findPathItemByKey(staticPaths, params, route, logger); if (!matchedStaticPath && (ssr ? route.prerender : true)) { throw new AstroError({ ...AstroErrorData.NoMatchingStaticPathFound, diff --git a/packages/astro/src/core/render/result.ts b/packages/astro/src/core/render/result.ts index 72fa4ddcfe31..4d6e4cca5553 100644 --- a/packages/astro/src/core/render/result.ts +++ b/packages/astro/src/core/render/result.ts @@ -11,7 +11,7 @@ import { renderJSX } from '../../runtime/server/jsx.js'; import { chunkToString } from '../../runtime/server/render/index.js'; import { AstroCookies } from '../cookies/index.js'; import { AstroError, AstroErrorData } from '../errors/index.js'; -import { warn, type LogOptions } from '../logger/core.js'; +import type { Logger } from '../logger/core.js'; const clientAddressSymbol = Symbol.for('astro.clientAddress'); const responseSentSymbol = Symbol.for('astro.responseSent'); @@ -25,7 +25,7 @@ export interface CreateResultArgs { * Value of Astro config's `output` option, true if "server" or "hybrid" */ ssr: boolean; - logging: LogOptions; + logger: Logger; params: Params; pathname: string; renderers: SSRLoadedRenderer[]; @@ -55,12 +55,12 @@ function getFunctionExpression(slot: any) { class Slots { #result: SSRResult; #slots: ComponentSlots | null; - #loggingOpts: LogOptions; + #logger: Logger; - constructor(result: SSRResult, slots: ComponentSlots | null, logging: LogOptions) { + constructor(result: SSRResult, slots: ComponentSlots | null, logger: Logger) { this.#result = result; this.#slots = slots; - this.#loggingOpts = logging; + this.#logger = logger; if (slots) { for (const key of Object.keys(slots)) { @@ -90,8 +90,7 @@ class Slots { const result = this.#result; if (!Array.isArray(args)) { - warn( - this.#loggingOpts, + this.#logger.warn( 'Astro.slots.render', `Expected second parameter to be an array, received a ${typeof args}. If you're trying to pass an array as a single argument and getting unexpected results, make sure you're passing your array as a item of an array. Ex: Astro.slots.render('default', [["Hello", "World"]])` ); @@ -164,7 +163,7 @@ export function createResult(args: CreateResultArgs): SSRResult { props: Record, slots: Record | null ) { - const astroSlots = new Slots(result, slots, args.logging); + const astroSlots = new Slots(result, slots, args.logger); const Astro: AstroGlobal = { // @ts-expect-error diff --git a/packages/astro/src/core/render/route-cache.ts b/packages/astro/src/core/render/route-cache.ts index 804f09183847..f607cf5d9629 100644 --- a/packages/astro/src/core/render/route-cache.ts +++ b/packages/astro/src/core/render/route-cache.ts @@ -8,7 +8,7 @@ import type { RuntimeMode, } from '../../@types/astro'; import { AstroError, AstroErrorData } from '../errors/index.js'; -import { debug, warn, type LogOptions } from '../logger/core.js'; +import type { Logger } from '../logger/core.js'; import { stringifyParams } from '../routing/params.js'; import { validateDynamicRouteModule, validateGetStaticPathsResult } from '../routing/validation.js'; @@ -18,7 +18,7 @@ interface CallGetStaticPathsOptions { mod: ComponentInstance; route: RouteData; routeCache: RouteCache; - logging: LogOptions; + logger: Logger; ssr: boolean; } @@ -26,7 +26,7 @@ export async function callGetStaticPaths({ mod, route, routeCache, - logging, + logger, ssr, }: CallGetStaticPathsOptions): Promise { const cached = routeCache.get(route); @@ -56,7 +56,7 @@ export async function callGetStaticPaths({ }, }); - validateGetStaticPathsResult(staticPaths, logging, route); + validateGetStaticPathsResult(staticPaths, logger, route); const keyedStaticPaths = staticPaths as GetStaticPathsResultKeyed; keyedStaticPaths.keyed = new Map(); @@ -80,12 +80,12 @@ interface RouteCacheEntry { * responses during dev and only ever called once during build. */ export class RouteCache { - private logging: LogOptions; + private logger: Logger; private cache: Record = {}; private mode: RuntimeMode; - constructor(logging: LogOptions, mode: RuntimeMode = 'production') { - this.logging = logging; + constructor(logger: Logger, mode: RuntimeMode = 'production') { + this.logger = logger; this.mode = mode; } @@ -99,8 +99,7 @@ export class RouteCache { // Warn here so that an unexpected double-call of getStaticPaths() // isn't invisible and developer can track down the issue. if (this.mode === 'production' && this.cache[route.component]?.staticPaths) { - warn( - this.logging, + this.logger.warn( 'routeCache', `Internal Warning: route cache overwritten. (${route.component})` ); @@ -116,12 +115,13 @@ export class RouteCache { export function findPathItemByKey( staticPaths: GetStaticPathsResultKeyed, params: Params, - route: RouteData + route: RouteData, + logger: Logger ) { const paramsKey = stringifyParams(params, route); const matchedStaticPath = staticPaths.keyed.get(paramsKey); if (matchedStaticPath) { return matchedStaticPath; } - debug('findPathItemByKey', `Unexpected cache miss looking for ${paramsKey}`); + logger.debug('findPathItemByKey', `Unexpected cache miss looking for ${paramsKey}`); } diff --git a/packages/astro/src/core/request.ts b/packages/astro/src/core/request.ts index 36222c7b6080..5c671a3fc0ef 100644 --- a/packages/astro/src/core/request.ts +++ b/packages/astro/src/core/request.ts @@ -1,6 +1,5 @@ import type { IncomingHttpHeaders } from 'node:http'; -import type { LogOptions } from './logger/core'; -import { warn } from './logger/core.js'; +import type { Logger } from './logger/core'; type HeaderType = Headers | Record | IncomingHttpHeaders; type RequestBody = ArrayBuffer | Blob | ReadableStream | URLSearchParams | FormData; @@ -11,7 +10,7 @@ export interface CreateRequestOptions { headers: HeaderType; method?: string; body?: RequestBody | undefined; - logging: LogOptions; + logger: Logger; ssr: boolean; locals?: object | undefined; } @@ -25,7 +24,7 @@ export function createRequest({ clientAddress, method = 'GET', body = undefined, - logging, + logger, ssr, locals, }: CreateRequestOptions): Request { @@ -43,7 +42,7 @@ export function createRequest({ Object.defineProperties(request, { params: { get() { - warn(logging, 'deprecation', `Astro.request.params has been moved to Astro.params`); + logger.warn('deprecation', `Astro.request.params has been moved to Astro.params`); return undefined; }, }, @@ -56,8 +55,7 @@ export function createRequest({ Object.defineProperty(request, 'headers', { ...headersDesc, get() { - warn( - logging, + logger.warn( 'ssg', `Headers are not exposed in static (SSG) output mode. To enable headers: set \`output: "server"\` in your config file.` ); diff --git a/packages/astro/src/core/routing/manifest/create.ts b/packages/astro/src/core/routing/manifest/create.ts index 124d870d9070..f08c2a585a47 100644 --- a/packages/astro/src/core/routing/manifest/create.ts +++ b/packages/astro/src/core/routing/manifest/create.ts @@ -6,7 +6,7 @@ import type { RouteData, RoutePart, } from '../../../@types/astro'; -import type { LogOptions } from '../../logger/core'; +import type { Logger } from '../../logger/core'; import { createRequire } from 'module'; import nodeFs from 'node:fs'; @@ -14,7 +14,6 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { getPrerenderDefault } from '../../../prerender/utils.js'; import { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from '../../constants.js'; -import { warn } from '../../logger/core.js'; import { removeLeadingForwardSlash, slash } from '../../path.js'; import { resolvePages } from '../../util.js'; import { getRouteGenerator } from './generator.js'; @@ -221,7 +220,7 @@ export interface CreateRouteManifestParams { /** Create manifest of all static routes */ export function createRouteManifest( { settings, cwd, fsMod }: CreateRouteManifestParams, - logging: LogOptions + logger: Logger ): ManifestData { const components: string[] = []; const routes: RouteData[] = []; @@ -261,7 +260,7 @@ export function createRouteManifest( if (!isDir && !validPageExtensions.has(ext) && !validEndpointExtensions.has(ext)) { if (!foundInvalidFileExtensions.has(ext)) { foundInvalidFileExtensions.add(ext); - warn(logging, 'astro', `Invalid file extension for Pages: ${ext}`); + logger.warn('astro', `Invalid file extension for Pages: ${ext}`); } return; @@ -359,7 +358,7 @@ export function createRouteManifest( } else if (settings.injectedRoutes.length === 0) { const pagesDirRootRelative = pages.href.slice(settings.config.root.href.length); - warn(logging, 'astro', `Missing pages directory: ${pagesDirRootRelative}`); + logger.warn('astro', `Missing pages directory: ${pagesDirRootRelative}`); } settings.injectedRoutes diff --git a/packages/astro/src/core/routing/validation.ts b/packages/astro/src/core/routing/validation.ts index b5c29b16e12e..e9f03eef87a3 100644 --- a/packages/astro/src/core/routing/validation.ts +++ b/packages/astro/src/core/routing/validation.ts @@ -1,7 +1,6 @@ import type { ComponentInstance, GetStaticPathsResult, RouteData } from '../../@types/astro'; import { AstroError, AstroErrorData } from '../errors/index.js'; -import type { LogOptions } from '../logger/core'; -import { warn } from '../logger/core.js'; +import type { Logger } from '../logger/core'; const VALID_PARAM_TYPES = ['string', 'number', 'undefined']; @@ -40,7 +39,7 @@ export function validateDynamicRouteModule( /** Throw error and log warnings for malformed getStaticPaths() response */ export function validateGetStaticPathsResult( result: GetStaticPathsResult, - logging: LogOptions, + logger: Logger, route: RouteData ) { if (!Array.isArray(result)) { @@ -79,8 +78,7 @@ export function validateGetStaticPathsResult( // TODO: Replace those with errors? They technically don't crash the build, but users might miss the warning. - erika, 2022-11-07 for (const [key, val] of Object.entries(pathObject.params)) { if (!(typeof val === 'undefined' || typeof val === 'string' || typeof val === 'number')) { - warn( - logging, + logger.warn( 'getStaticPaths', `invalid path param: ${key}. A string, number or undefined value was expected, but got \`${JSON.stringify( val @@ -88,8 +86,7 @@ export function validateGetStaticPathsResult( ); } if (typeof val === 'string' && val === '') { - warn( - logging, + logger.warn( 'getStaticPaths', `invalid path param: ${key}. \`undefined\` expected for an optional param, but got empty string.` ); diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts index e1d432465dda..3dea7c7148f3 100644 --- a/packages/astro/src/core/sync/index.ts +++ b/packages/astro/src/core/sync/index.ts @@ -12,11 +12,11 @@ import { runHookConfigSetup } from '../../integrations/index.js'; import { setUpEnvTs } from '../../vite-plugin-inject-env-ts/index.js'; import { getTimeStat } from '../build/util.js'; import { resolveConfig } from '../config/config.js'; -import { createNodeLogging } from '../config/logging.js'; +import { createNodeLogger } from '../config/logging.js'; import { createSettings } from '../config/settings.js'; import { createVite } from '../create-vite.js'; import { AstroError, AstroErrorData, createSafeError, isAstroError } from '../errors/index.js'; -import { info, type LogOptions } from '../logger/core.js'; +import type { Logger } from '../logger/core.js'; export type ProcessExit = 0 | 1; @@ -28,7 +28,7 @@ export type SyncOptions = { }; export type SyncInternalOptions = SyncOptions & { - logging: LogOptions; + logger: Logger; }; /** @@ -41,7 +41,7 @@ export default async function sync( inlineConfig: AstroInlineConfig, options?: SyncOptions ): Promise { - const logging = createNodeLogging(inlineConfig); + const logger = createNodeLogger(inlineConfig); const { userConfig, astroConfig } = await resolveConfig(inlineConfig ?? {}, 'sync'); telemetry.record(eventCliSession('sync', userConfig)); @@ -49,11 +49,11 @@ export default async function sync( const settings = await runHookConfigSetup({ settings: _settings, - logging: logging, + logger: logger, command: 'build', }); - return await syncInternal(settings, { ...options, logging }); + return await syncInternal(settings, { ...options, logger }); } /** @@ -72,7 +72,7 @@ export default async function sync( */ export async function syncInternal( settings: AstroSettings, - { logging, fs }: SyncInternalOptions + { logger, fs }: SyncInternalOptions ): Promise { const timerStart = performance.now(); // Needed to load content config @@ -84,7 +84,7 @@ export async function syncInternal( ssr: { external: [] }, logLevel: 'silent', }, - { settings, logging, mode: 'build', command: 'build', fs } + { settings, logger, mode: 'build', command: 'build', fs } ) ); @@ -101,7 +101,7 @@ export async function syncInternal( try { const contentTypesGenerator = await createContentTypesGenerator({ contentConfigObserver: globalContentConfigObserver, - logging, + logger: logger, fs: fs ?? fsMod, settings, viteServer: tempViteServer, @@ -117,7 +117,7 @@ export async function syncInternal( switch (typesResult.reason) { case 'no-content-dir': default: - info(logging, 'content', 'No content directory found. Skipping type generation.'); + logger.info('content', 'No content directory found. Skipping type generation.'); return 0; } } @@ -137,8 +137,8 @@ export async function syncInternal( await tempViteServer.close(); } - info(logging, 'content', `Types generated ${dim(getTimeStat(timerStart, performance.now()))}`); - await setUpEnvTs({ settings, logging, fs: fs ?? fsMod }); + logger.info('content', `Types generated ${dim(getTimeStat(timerStart, performance.now()))}`); + await setUpEnvTs({ settings, logger, fs: fs ?? fsMod }); return 0; } diff --git a/packages/astro/src/integrations/astroFeaturesValidation.ts b/packages/astro/src/integrations/astroFeaturesValidation.ts index c494b35f45f4..eb388745d61f 100644 --- a/packages/astro/src/integrations/astroFeaturesValidation.ts +++ b/packages/astro/src/integrations/astroFeaturesValidation.ts @@ -4,7 +4,7 @@ import type { AstroFeatureMap, SupportsKind, } from '../@types/astro'; -import { error, warn, type LogOptions } from '../core/logger/core.js'; +import type { Logger } from '../core/logger/core.js'; const STABLE = 'stable'; const DEPRECATED = 'deprecated'; @@ -40,7 +40,7 @@ export function validateSupportedFeatures( adapterName: string, featureMap: AstroFeatureMap = ALL_UNSUPPORTED, config: AstroConfig, - logging: LogOptions + logger: Logger ): ValidationResult { const { assets = UNSUPPORTED_ASSETS_FEATURE, @@ -53,7 +53,7 @@ export function validateSupportedFeatures( validationResult.staticOutput = validateSupportKind( staticOutput, adapterName, - logging, + logger, 'staticOutput', () => config?.output === 'static' ); @@ -61,7 +61,7 @@ export function validateSupportedFeatures( validationResult.hybridOutput = validateSupportKind( hybridOutput, adapterName, - logging, + logger, 'hybridOutput', () => config?.output === 'hybrid' ); @@ -69,11 +69,11 @@ export function validateSupportedFeatures( validationResult.serverOutput = validateSupportKind( serverOutput, adapterName, - logging, + logger, 'serverOutput', () => config?.output === 'server' ); - validationResult.assets = validateAssetsFeature(assets, adapterName, config, logging); + validationResult.assets = validateAssetsFeature(assets, adapterName, config, logger); return validationResult; } @@ -81,44 +81,39 @@ export function validateSupportedFeatures( function validateSupportKind( supportKind: SupportsKind, adapterName: string, - logging: LogOptions, + logger: Logger, featureName: string, hasCorrectConfig: () => boolean ): boolean { if (supportKind === STABLE) { return true; } else if (supportKind === DEPRECATED) { - featureIsDeprecated(adapterName, logging); + featureIsDeprecated(adapterName, logger); } else if (supportKind === EXPERIMENTAL) { - featureIsExperimental(adapterName, logging); + featureIsExperimental(adapterName, logger); } if (hasCorrectConfig() && supportKind === UNSUPPORTED) { - featureIsUnsupported(adapterName, logging, featureName); + featureIsUnsupported(adapterName, logger, featureName); return false; } else { return true; } } -function featureIsUnsupported(adapterName: string, logging: LogOptions, featureName: string) { - error( - logging, +function featureIsUnsupported(adapterName: string, logger: Logger, featureName: string) { + logger.error( `${adapterName}`, `The feature ${featureName} is not supported by the adapter ${adapterName}.` ); } -function featureIsExperimental(adapterName: string, logging: LogOptions) { - warn(logging, `${adapterName}`, 'The feature is experimental and subject to issues or changes.'); +function featureIsExperimental(adapterName: string, logger: Logger) { + logger.warn(`${adapterName}`, 'The feature is experimental and subject to issues or changes.'); } -function featureIsDeprecated(adapterName: string, logging: LogOptions) { - warn( - logging, - `${adapterName}`, - 'The feature is deprecated and will be moved in the next release.' - ); +function featureIsDeprecated(adapterName: string, logger: Logger) { + logger.warn(`${adapterName}`, 'The feature is deprecated and will be moved in the next release.'); } const SHARP_SERVICE = 'astro/assets/services/sharp'; @@ -128,7 +123,7 @@ function validateAssetsFeature( assets: AstroAssetsFeature, adapterName: string, config: AstroConfig, - logging: LogOptions + logger: Logger ): boolean { const { supportKind = UNSUPPORTED, @@ -136,8 +131,7 @@ function validateAssetsFeature( isSquooshCompatible = false, } = assets; if (config?.image?.service?.entrypoint === SHARP_SERVICE && !isSharpCompatible) { - error( - logging, + logger.error( 'astro', `The currently selected adapter \`${adapterName}\` is not compatible with the image service "Sharp".` ); @@ -145,13 +139,12 @@ function validateAssetsFeature( } if (config?.image?.service?.entrypoint === SQUOOSH_SERVICE && !isSquooshCompatible) { - error( - logging, + logger.error( 'astro', `The currently selected adapter \`${adapterName}\` is not compatible with the image service "Squoosh".` ); return false; } - return validateSupportKind(supportKind, adapterName, logging, 'assets', () => true); + return validateSupportKind(supportKind, adapterName, logger, 'assets', () => true); } diff --git a/packages/astro/src/integrations/index.ts b/packages/astro/src/integrations/index.ts index 71c5a5e63484..9b7770c1cef6 100644 --- a/packages/astro/src/integrations/index.ts +++ b/packages/astro/src/integrations/index.ts @@ -18,7 +18,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 { AstroIntegrationLogger, error, info, warn, type LogOptions } from '../core/logger/core.js'; +import { AstroIntegrationLogger, type Logger } from '../core/logger/core.js'; import { isServerLikeOutput } from '../prerender/utils.js'; import { validateSupportedFeatures } from './astroFeaturesValidation.js'; @@ -26,15 +26,15 @@ async function withTakingALongTimeMsg({ name, hookResult, timeoutMs = 3000, - logging, + logger, }: { name: string; hookResult: T | Promise; timeoutMs?: number; - logging: LogOptions; + logger: Logger; }): Promise { const timeout = setTimeout(() => { - info(logging, 'build', `Waiting for the ${bold(name)} integration...`); + logger.info('build', `Waiting for the ${bold(name)} integration...`); }, timeoutMs); const result = await hookResult; clearTimeout(timeout); @@ -44,25 +44,25 @@ async function withTakingALongTimeMsg({ // Used internally to store instances of loggers. const Loggers = new WeakMap(); -function getLogger(integration: AstroIntegration, logging: LogOptions) { +function getLogger(integration: AstroIntegration, logger: Logger) { if (Loggers.has(integration)) { // SAFETY: we check the existence in the if block return Loggers.get(integration)!; } - const logger = new AstroIntegrationLogger(logging, integration.name); - Loggers.set(integration, logger); - return logger; + const integrationLogger = logger.forkIntegrationLogger(integration.name); + Loggers.set(integration, integrationLogger); + return integrationLogger; } export async function runHookConfigSetup({ settings, command, - logging, + logger, isRestart = false, }: { settings: AstroSettings; command: 'dev' | 'build' | 'preview'; - logging: LogOptions; + logger: Logger; isRestart?: boolean; }): Promise { // An adapter is an integration, so if one is provided push it. @@ -88,7 +88,7 @@ export async function runHookConfigSetup({ * ``` */ if (integration.hooks?.['astro:config:setup']) { - const logger = getLogger(integration, logging); + const integrationLogger = getLogger(integration, logger); const hooks: HookParameters<'astro:config:setup'> = { config: updatedConfig, @@ -125,7 +125,7 @@ export async function runHookConfigSetup({ } addedClientDirectives.set(name, buildClientDirectiveEntrypoint(name, entrypoint)); }, - logger, + logger: integrationLogger, }; // --- @@ -164,7 +164,7 @@ export async function runHookConfigSetup({ await withTakingALongTimeMsg({ name: integration.name, hookResult: integration.hooks['astro:config:setup'](hooks), - logging, + logger, }); // Add custom client directives to settings, waiting for compiled code by esbuild @@ -180,13 +180,12 @@ export async function runHookConfigSetup({ export async function runHookConfigDone({ settings, - logging, + logger, }: { settings: AstroSettings; - logging: LogOptions; + logger: Logger; }) { for (const integration of settings.config.integrations) { - const logger = getLogger(integration, logging); if (integration?.hooks?.['astro:config:done']) { await withTakingALongTimeMsg({ name: integration.name, @@ -200,8 +199,7 @@ export async function runHookConfigDone({ } if (!adapter.supportedAstroFeatures) { // NOTE: throw an error in Astro 4.0 - warn( - logging, + logger.warn( 'astro', `The adapter ${adapter.name} doesn't provide a feature map. From Astro 3.0, an adapter can provide a feature map. Not providing a feature map will cause an error in Astro 4.0.` ); @@ -210,20 +208,18 @@ export async function runHookConfigDone({ adapter.name, adapter.supportedAstroFeatures, settings.config, - logging + logger ); for (const [featureName, supported] of Object.entries(validationResult)) { if (!supported) { - error( - logging, + logger.error( 'astro', `The adapter ${adapter.name} doesn't support the feature ${featureName}. Your project won't be built. You should not use it.` ); } } if (!validationResult.assets) { - info( - logging, + logger.info( 'astro', `The selected adapter ${adapter.name} does not support Sharp or Squoosh for image processing. To ensure your project is still able to build, image processing has been disabled.` ); @@ -235,9 +231,9 @@ export async function runHookConfigDone({ } settings.adapter = adapter; }, - logger, + logger: getLogger(integration, logger), }), - logging, + logger, }); } } @@ -246,19 +242,21 @@ export async function runHookConfigDone({ export async function runHookServerSetup({ config, server, - logging, + logger, }: { config: AstroConfig; server: ViteDevServer; - logging: LogOptions; + logger: Logger; }) { for (const integration of config.integrations) { if (integration?.hooks?.['astro:server:setup']) { - const logger = getLogger(integration, logging); await withTakingALongTimeMsg({ name: integration.name, - hookResult: integration.hooks['astro:server:setup']({ server, logger }), - logging, + hookResult: integration.hooks['astro:server:setup']({ + server, + logger: getLogger(integration, logger), + }), + logger, }); } } @@ -267,20 +265,21 @@ export async function runHookServerSetup({ export async function runHookServerStart({ config, address, - logging, + logger, }: { config: AstroConfig; address: AddressInfo; - logging: LogOptions; + logger: Logger; }) { for (const integration of config.integrations) { - const logger = getLogger(integration, logging); - if (integration?.hooks?.['astro:server:start']) { await withTakingALongTimeMsg({ name: integration.name, - hookResult: integration.hooks['astro:server:start']({ address, logger }), - logging, + hookResult: integration.hooks['astro:server:start']({ + address, + logger: getLogger(integration, logger), + }), + logger, }); } } @@ -288,19 +287,19 @@ export async function runHookServerStart({ export async function runHookServerDone({ config, - logging, + logger, }: { config: AstroConfig; - logging: LogOptions; + logger: Logger; }) { for (const integration of config.integrations) { - const logger = getLogger(integration, logging); - if (integration?.hooks?.['astro:server:done']) { await withTakingALongTimeMsg({ name: integration.name, - hookResult: integration.hooks['astro:server:done']({ logger }), - logging, + hookResult: integration.hooks['astro:server:done']({ + logger: getLogger(integration, logger), + }), + logger, }); } } @@ -311,7 +310,7 @@ export async function runHookBuildStart({ logging, }: { config: AstroConfig; - logging: LogOptions; + logging: Logger; }) { for (const integration of config.integrations) { if (integration?.hooks?.['astro:build:start']) { @@ -320,7 +319,7 @@ export async function runHookBuildStart({ await withTakingALongTimeMsg({ name: integration.name, hookResult: integration.hooks['astro:build:start']({ logger }), - logging, + logger: logging, }); } } @@ -331,20 +330,18 @@ export async function runHookBuildSetup({ vite, pages, target, - logging, + logger, }: { config: AstroConfig; vite: InlineConfig; pages: Map; target: 'server' | 'client'; - logging: LogOptions; + logger: Logger; }): Promise { let updatedConfig = vite; for (const integration of config.integrations) { if (integration?.hooks?.['astro:build:setup']) { - const logger = getLogger(integration, logging); - await withTakingALongTimeMsg({ name: integration.name, hookResult: integration.hooks['astro:build:setup']({ @@ -354,9 +351,9 @@ export async function runHookBuildSetup({ updateConfig: (newConfig) => { updatedConfig = mergeConfig(updatedConfig, newConfig); }, - logger, + logger: getLogger(integration, logger), }), - logging, + logger, }); } } @@ -367,7 +364,7 @@ export async function runHookBuildSetup({ type RunHookBuildSsr = { config: AstroConfig; manifest: SerializedSSRManifest; - logging: LogOptions; + logger: Logger; entryPoints: Map; middlewareEntryPoint: URL | undefined; }; @@ -375,23 +372,21 @@ type RunHookBuildSsr = { export async function runHookBuildSsr({ config, manifest, - logging, + logger, entryPoints, middlewareEntryPoint, }: RunHookBuildSsr) { for (const integration of config.integrations) { if (integration?.hooks?.['astro:build:ssr']) { - const logger = getLogger(integration, logging); - await withTakingALongTimeMsg({ name: integration.name, hookResult: integration.hooks['astro:build:ssr']({ manifest, entryPoints, middlewareEntryPoint, - logger, + logger: getLogger(integration, logger), }), - logging, + logger, }); } } @@ -399,21 +394,22 @@ export async function runHookBuildSsr({ export async function runHookBuildGenerated({ config, - logging, + logger, }: { config: AstroConfig; - logging: LogOptions; + logger: Logger; }) { const dir = isServerLikeOutput(config) ? config.build.client : config.outDir; for (const integration of config.integrations) { - const logger = getLogger(integration, logging); - if (integration?.hooks?.['astro:build:generated']) { await withTakingALongTimeMsg({ name: integration.name, - hookResult: integration.hooks['astro:build:generated']({ dir, logger }), - logging, + hookResult: integration.hooks['astro:build:generated']({ + dir, + logger: getLogger(integration, logger), + }), + logger, }); } } @@ -423,7 +419,7 @@ type RunHookBuildDone = { config: AstroConfig; pages: string[]; routes: RouteData[]; - logging: LogOptions; + logging: Logger; }; export async function runHookBuildDone({ config, pages, routes, logging }: RunHookBuildDone) { @@ -442,7 +438,7 @@ export async function runHookBuildDone({ config, pages, routes, logging }: RunHo routes, logger, }), - logging, + logger: logging, }); } } diff --git a/packages/astro/src/runtime/server/endpoint.ts b/packages/astro/src/runtime/server/endpoint.ts index 91f19fd57aa7..686e3795fcbe 100644 --- a/packages/astro/src/runtime/server/endpoint.ts +++ b/packages/astro/src/runtime/server/endpoint.ts @@ -1,13 +1,12 @@ import type { APIContext, EndpointHandler, Params } from '../../@types/astro'; -import { warn, type LogOptions } from '../../core/logger/core.js'; +import type { Logger } from '../../core/logger/core.js'; -function getHandlerFromModule(mod: EndpointHandler, method: string, logging: LogOptions) { +function getHandlerFromModule(mod: EndpointHandler, method: string, logger: Logger) { const lowerCaseMethod = method.toLowerCase(); // TODO: remove in Astro 4.0 if (mod[lowerCaseMethod]) { - warn( - logging, + logger.warn( 'astro', `Lower case endpoint names are deprecated and will not be supported in Astro 4.0. Rename the endpoint ${lowerCaseMethod} to ${method}.` ); @@ -43,12 +42,12 @@ export async function renderEndpoint( mod: EndpointHandler, context: APIContext, ssr: boolean, - logging: LogOptions + logger: Logger ) { const { request } = context; const chosenMethod = request.method?.toUpperCase(); - const handler = getHandlerFromModule(mod, chosenMethod, logging); + const handler = getHandlerFromModule(mod, chosenMethod, logger); // TODO: remove the 'get' check in Astro 4.0 if (!ssr && ssr === false && chosenMethod && chosenMethod !== 'GET' && chosenMethod !== 'get') { // eslint-disable-next-line no-console diff --git a/packages/astro/src/vite-plugin-astro-server/base.ts b/packages/astro/src/vite-plugin-astro-server/base.ts index f9e1c3fe487d..bb6a8a009c99 100644 --- a/packages/astro/src/vite-plugin-astro-server/base.ts +++ b/packages/astro/src/vite-plugin-astro-server/base.ts @@ -2,14 +2,14 @@ import type * as vite from 'vite'; import type { AstroSettings } from '../@types/astro'; import * as fs from 'node:fs'; -import { warn, type LogOptions } from '../core/logger/core.js'; +import type { Logger } from '../core/logger/core.js'; import notFoundTemplate, { subpathNotUsedTemplate } from '../template/4xx.js'; import { log404 } from './common.js'; import { writeHtmlResponse } from './response.js'; export function baseMiddleware( settings: AstroSettings, - logging: LogOptions + logger: Logger ): vite.Connect.NextHandleFunction { const { config } = settings; const site = config.site ? new URL(config.base, config.site) : undefined; @@ -28,13 +28,13 @@ export function baseMiddleware( } if (pathname === '/' || pathname === '/index.html') { - log404(logging, pathname); + log404(logger, pathname); const html = subpathNotUsedTemplate(devRoot, pathname); return writeHtmlResponse(res, 404, html); } if (req.headers.accept?.includes('text/html')) { - log404(logging, pathname); + log404(logger, pathname); const html = notFoundTemplate({ statusCode: 404, title: 'Not found', @@ -49,8 +49,7 @@ export function baseMiddleware( fs.stat(publicPath, (_err, stats) => { if (stats) { const expectedLocation = new URL('.' + url, devRootURL).pathname; - warn( - logging, + logger.warn( 'dev', `Requests for items in your public folder must also include your base. ${url} should be ${expectedLocation}. Omitting the base will break in production.` ); diff --git a/packages/astro/src/vite-plugin-astro-server/common.ts b/packages/astro/src/vite-plugin-astro-server/common.ts index 9739bebc5bb6..9e331232c1c7 100644 --- a/packages/astro/src/vite-plugin-astro-server/common.ts +++ b/packages/astro/src/vite-plugin-astro-server/common.ts @@ -1,6 +1,6 @@ -import { info, type LogOptions } from '../core/logger/core.js'; +import type { Logger } from '../core/logger/core.js'; import * as msg from '../core/messages.js'; -export function log404(logging: LogOptions, pathname: string) { - info(logging, 'serve', msg.req({ url: pathname, statusCode: 404 })); +export function log404(logger: Logger, pathname: string) { + logger.info('serve', msg.req({ url: pathname, statusCode: 404 })); } diff --git a/packages/astro/src/vite-plugin-astro-server/devPipeline.ts b/packages/astro/src/vite-plugin-astro-server/devPipeline.ts index 6bb8e86ebeb3..fbe9c0429646 100644 --- a/packages/astro/src/vite-plugin-astro-server/devPipeline.ts +++ b/packages/astro/src/vite-plugin-astro-server/devPipeline.ts @@ -5,8 +5,7 @@ import type { SSRLoadedRenderer, SSRManifest, } from '../@types/astro'; -import type { LogOptions } from '../core/logger/core'; -import { Logger } from '../core/logger/core.js'; +import type { Logger } from '../core/logger/core'; import type { ModuleLoader } from '../core/module-loader'; import { Pipeline } from '../core/pipeline.js'; import type { Environment } from '../core/render'; @@ -22,18 +21,18 @@ export default class DevPipeline extends Pipeline { constructor({ manifest, - logging, + logger, settings, loader, }: { manifest: SSRManifest; - logging: LogOptions; + logger: Logger; settings: AstroSettings; loader: ModuleLoader; }) { - const env = DevPipeline.createDevelopmentEnvironment(manifest, settings, logging, loader); + const env = DevPipeline.createDevelopmentEnvironment(manifest, settings, logger, loader); super(env); - this.#devLogger = new Logger(logging); + this.#devLogger = logger; this.#settings = settings; this.#loader = loader; this.setEndpointHandler(this.#handleEndpointResult); @@ -69,21 +68,20 @@ export default class DevPipeline extends Pipeline { static createDevelopmentEnvironment( manifest: SSRManifest, settings: AstroSettings, - logging: LogOptions, + logger: Logger, loader: ModuleLoader ): Environment { const mode: RuntimeMode = 'development'; - return createEnvironment({ adapterName: manifest.adapterName, - logging, + logger, mode, // This will be overridden in the dev server renderers: [], clientDirectives: manifest.clientDirectives, compressHTML: manifest.compressHTML, resolve: createResolve(loader, settings.config.root), - routeCache: new RouteCache(logging, mode), + routeCache: new RouteCache(logger, mode), site: manifest.site, ssr: isServerLikeOutput(settings.config), streaming: true, diff --git a/packages/astro/src/vite-plugin-astro-server/environment.ts b/packages/astro/src/vite-plugin-astro-server/environment.ts deleted file mode 100644 index 010c1b96af77..000000000000 --- a/packages/astro/src/vite-plugin-astro-server/environment.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { AstroSettings, RuntimeMode, SSRManifest } from '../@types/astro.js'; -import type { LogOptions } from '../core/logger/core.js'; -import type { ModuleLoader } from '../core/module-loader'; -import type { Environment } from '../core/render'; -import { createEnvironment } from '../core/render/index.js'; -import { RouteCache } from '../core/render/route-cache.js'; -import { isServerLikeOutput } from '../prerender/utils.js'; -import { createResolve } from './resolve.js'; - -export function createDevelopmentEnvironment( - manifest: SSRManifest, - settings: AstroSettings, - logging: LogOptions, - loader: ModuleLoader -): Environment { - const mode: RuntimeMode = 'development'; - return createEnvironment({ - adapterName: manifest.adapterName, - logging, - mode, - // This will be overridden in the dev server - renderers: [], - clientDirectives: manifest.clientDirectives, - compressHTML: manifest.compressHTML, - resolve: createResolve(loader, settings.config.root), - routeCache: new RouteCache(logging, mode), - site: manifest.site, - ssr: isServerLikeOutput(settings.config), - streaming: true, - }); -} diff --git a/packages/astro/src/vite-plugin-astro-server/plugin.ts b/packages/astro/src/vite-plugin-astro-server/plugin.ts index f7f7b0068256..26568715ba9a 100644 --- a/packages/astro/src/vite-plugin-astro-server/plugin.ts +++ b/packages/astro/src/vite-plugin-astro-server/plugin.ts @@ -2,7 +2,7 @@ import type fs from 'node:fs'; import type * as vite from 'vite'; import type { AstroSettings, ManifestData, SSRManifest } from '../@types/astro'; import { patchOverlay } from '../core/errors/overlay.js'; -import type { LogOptions } from '../core/logger/core.js'; +import type { Logger } from '../core/logger/core.js'; import { createViteLoader } from '../core/module-loader/index.js'; import { createRouteManifest } from '../core/routing/index.js'; import { baseMiddleware } from './base.js'; @@ -12,13 +12,13 @@ import { handleRequest } from './request.js'; export interface AstroPluginOptions { settings: AstroSettings; - logging: LogOptions; + logger: Logger; fs: typeof fs; } export default function createVitePluginAstroServer({ settings, - logging, + logger, fs: fsMod, }: AstroPluginOptions): vite.Plugin { return { @@ -26,15 +26,15 @@ export default function createVitePluginAstroServer({ configureServer(viteServer) { const loader = createViteLoader(viteServer); const manifest = createDevelopmentManifest(settings); - const pipeline = new DevPipeline({ logging, manifest, settings, loader }); - let manifestData: ManifestData = createRouteManifest({ settings, fsMod }, logging); + const pipeline = new DevPipeline({ logger, manifest, settings, loader }); + let manifestData: ManifestData = createRouteManifest({ settings, fsMod }, logger); const controller = createController({ loader }); /** rebuild the route cache + manifest, as needed. */ function rebuildManifest(needsManifestRebuild: boolean) { pipeline.clearRouteCache(); if (needsManifestRebuild) { - manifestData = createRouteManifest({ settings }, logging); + manifestData = createRouteManifest({ settings }, logger); } } // Rebuild route manifest on file change, if needed. @@ -47,7 +47,7 @@ export default function createVitePluginAstroServer({ // fix(#6067): always inject this to ensure zombie base handling is killed after restarts viteServer.middlewares.stack.unshift({ route: '', - handle: baseMiddleware(settings, logging), + handle: baseMiddleware(settings, logger), }); // Note that this function has a name so other middleware can find it. viteServer.middlewares.use(async function astroDevHandler(request, response) { @@ -99,5 +99,6 @@ export function createDevelopmentManifest(settings: AstroSettings): SSRManifest ? new URL(settings.config.base, settings.config.site).toString() : settings.config.site, componentMetadata: new Map(), + middlewareEntryPoint: undefined, }; } diff --git a/packages/astro/src/vite-plugin-astro-server/route.ts b/packages/astro/src/vite-plugin-astro-server/route.ts index d4f0118ee2b0..af02da4108ef 100644 --- a/packages/astro/src/vite-plugin-astro-server/route.ts +++ b/packages/astro/src/vite-plugin-astro-server/route.ts @@ -51,7 +51,7 @@ export async function matchRoute( pipeline: DevPipeline ): Promise { const env = pipeline.getEnvironment(); - const { routeCache, logging } = env; + const { routeCache, logger } = env; const matches = matchAllRoutes(pathname, manifestData); const preloadedMatches = await getSortedPreloadedMatches({ pipeline, @@ -68,7 +68,7 @@ export async function matchRoute( route: maybeRoute, routeCache, pathname: pathname, - logging, + logger, ssr: isServerLikeOutput(pipeline.getConfig()), }); return { @@ -106,7 +106,7 @@ export async function matchRoute( ); } - log404(logging, pathname); + log404(logger, pathname); const custom404 = getCustom404Route(manifestData); if (custom404) { @@ -156,7 +156,7 @@ export async function handleRoute({ const settings = pipeline.getSettings(); const config = pipeline.getConfig(); const moduleLoader = pipeline.getModuleLoader(); - const { logging } = env; + const { logger } = env; if (!matchedRoute) { return handle404Response(origin, incomingRequest, incomingResponse); } @@ -171,7 +171,7 @@ export async function handleRoute({ headers: buildingToSSR ? incomingRequest.headers : new Headers(), method: incomingRequest.method, body, - logging, + logger, ssr: buildingToSSR, clientAddress: buildingToSSR ? incomingRequest.socket.remoteAddress : undefined, locals: Reflect.get(incomingRequest, clientLocalsSymbol), // Allows adapters to pass in locals in dev mode. diff --git a/packages/astro/src/vite-plugin-astro/compile.ts b/packages/astro/src/vite-plugin-astro/compile.ts index b8704c81b4e6..6c6b797ec4c6 100644 --- a/packages/astro/src/vite-plugin-astro/compile.ts +++ b/packages/astro/src/vite-plugin-astro/compile.ts @@ -1,12 +1,12 @@ import { transformWithEsbuild, type ESBuildTransformResult } from 'vite'; import type { AstroConfig } from '../@types/astro'; import { cachedCompilation, type CompileProps, type CompileResult } from '../core/compile/index.js'; -import type { LogOptions } from '../core/logger/core.js'; +import type { Logger } from '../core/logger/core.js'; import { getFileInfo } from '../vite-plugin-utils/index.js'; interface CachedFullCompilation { compileProps: CompileProps; - logging: LogOptions; + logger: Logger; } interface FullCompileResult extends Omit { @@ -18,14 +18,14 @@ interface EnhanceCompilerErrorOptions { id: string; source: string; config: AstroConfig; - logging: LogOptions; + logger: Logger; } const FRONTMATTER_PARSE_REGEXP = /^\-\-\-(.*)^\-\-\-/ms; export async function cachedFullCompilation({ compileProps, - logging, + logger, }: CachedFullCompilation): Promise { let transformResult: CompileResult; let esbuildResult: ESBuildTransformResult; @@ -52,7 +52,7 @@ export async function cachedFullCompilation({ id: compileProps.filename, source: compileProps.source, config: compileProps.astroConfig, - logging: logging, + logger: logger, }); throw err; } diff --git a/packages/astro/src/vite-plugin-astro/hmr.ts b/packages/astro/src/vite-plugin-astro/hmr.ts index f123dba3ab96..6502ae513d2a 100644 --- a/packages/astro/src/vite-plugin-astro/hmr.ts +++ b/packages/astro/src/vite-plugin-astro/hmr.ts @@ -7,8 +7,7 @@ import { isCached, type CompileResult, } from '../core/compile/index.js'; -import type { LogOptions } from '../core/logger/core.js'; -import { info } from '../core/logger/core.js'; +import type { Logger } from '../core/logger/core.js'; import * as msg from '../core/messages.js'; import { isAstroScript } from './query.js'; @@ -20,14 +19,15 @@ const isPkgFile = (id: string | null) => { export interface HandleHotUpdateOptions { config: AstroConfig; - logging: LogOptions; + logger: Logger; + compile: () => ReturnType; source: string; } export async function handleHotUpdate( ctx: HmrContext, - { config, logging, compile, source }: HandleHotUpdateOptions + { config, logger, compile, source }: HandleHotUpdateOptions ) { let isStyleOnlyChange = false; if (ctx.file.endsWith('.astro') && isCached(config, ctx.file)) { @@ -95,7 +95,7 @@ export async function handleHotUpdate( // If only styles are changed, remove the component file from the update list if (isStyleOnlyChange) { - info(logging, 'astro', msg.hmr({ file, style: true })); + logger.info('astro', msg.hmr({ file, style: true })); // remove base file and hoisted scripts return mods.filter((mod) => mod.id !== ctx.file && !mod.id?.endsWith('.ts')); } @@ -124,9 +124,9 @@ export async function handleHotUpdate( const isSelfAccepting = mods.every((m) => m.isSelfAccepting || m.url.endsWith('.svelte')); if (isSelfAccepting) { if (/astro\.config\.[cm][jt]s$/.test(file)) return mods; - info(logging, 'astro', msg.hmr({ file })); + logger.info('astro', msg.hmr({ file })); } else { - info(logging, 'astro', msg.reload({ file })); + logger.info('astro', msg.reload({ file })); } return mods; diff --git a/packages/astro/src/vite-plugin-astro/index.ts b/packages/astro/src/vite-plugin-astro/index.ts index c5523d71231c..371ae9657d17 100644 --- a/packages/astro/src/vite-plugin-astro/index.ts +++ b/packages/astro/src/vite-plugin-astro/index.ts @@ -1,7 +1,7 @@ import type { SourceDescription } from 'rollup'; import type * as vite from 'vite'; import type { AstroSettings } from '../@types/astro'; -import type { LogOptions } from '../core/logger/core.js'; +import type { Logger } from '../core/logger/core.js'; import type { PluginMetadata as AstroPluginMetadata } from './types'; import { normalizePath } from 'vite'; @@ -20,11 +20,11 @@ export type { AstroPluginMetadata }; interface AstroPluginOptions { settings: AstroSettings; - logging: LogOptions; + logger: Logger; } /** Transform .astro files for Vite */ -export default function astro({ settings, logging }: AstroPluginOptions): vite.Plugin[] { +export default function astro({ settings, logger }: AstroPluginOptions): vite.Plugin[] { const { config } = settings; let resolvedConfig: vite.ResolvedConfig; @@ -143,7 +143,7 @@ export default function astro({ settings, logging }: AstroPluginOptions): vite.P source, }; - const transformResult = await cachedFullCompilation({ compileProps, logging }); + const transformResult = await cachedFullCompilation({ compileProps, logger }); for (const dep of transformResult.cssDeps) { this.addWatchFile(dep); @@ -182,7 +182,7 @@ export default function astro({ settings, logging }: AstroPluginOptions): vite.P const compile = () => cachedCompilation(compileProps); return handleHotUpdate(context, { config, - logging, + logger, compile, source: compileProps.source, }); diff --git a/packages/astro/src/vite-plugin-inject-env-ts/index.ts b/packages/astro/src/vite-plugin-inject-env-ts/index.ts index a97ca134dcd7..d884075ab09e 100644 --- a/packages/astro/src/vite-plugin-inject-env-ts/index.ts +++ b/packages/astro/src/vite-plugin-inject-env-ts/index.ts @@ -5,7 +5,7 @@ import { fileURLToPath } from 'node:url'; import { normalizePath, type Plugin } from 'vite'; import type { AstroSettings } from '../@types/astro.js'; import { getContentPaths, getDotAstroTypeReference } from '../content/index.js'; -import { info, type LogOptions } from '../core/logger/core.js'; +import { type Logger } from '../core/logger/core.js'; export function getEnvTsPath({ srcDir }: { srcDir: URL }) { return new URL('env.d.ts', srcDir); @@ -13,11 +13,11 @@ export function getEnvTsPath({ srcDir }: { srcDir: URL }) { export function astroInjectEnvTsPlugin({ settings, - logging, + logger, fs, }: { settings: AstroSettings; - logging: LogOptions; + logger: Logger; fs: typeof fsMod; }): Plugin { return { @@ -26,18 +26,18 @@ export function astroInjectEnvTsPlugin({ // Ex. `.astro` types have been written enforce: 'post', async config() { - await setUpEnvTs({ settings, logging, fs }); + await setUpEnvTs({ settings, logger, fs }); }, }; } export async function setUpEnvTs({ settings, - logging, + logger, fs, }: { settings: AstroSettings; - logging: LogOptions; + logger: Logger; fs: typeof fsMod; }) { const envTsPath = getEnvTsPath(settings.config); @@ -57,7 +57,7 @@ export async function setUpEnvTs({ 'types="astro/client"' ); await fs.promises.writeFile(envTsPath, typesEnvContents, 'utf-8'); - info(logging, 'assets', `Removed ${bold(envTsPathRelativetoRoot)} types`); + logger.info('assets', `Removed ${bold(envTsPathRelativetoRoot)} types`); } if (!fs.existsSync(dotAstroDir)) @@ -68,7 +68,7 @@ export async function setUpEnvTs({ if (!typesEnvContents.includes(expectedTypeReference)) { typesEnvContents = `${expectedTypeReference}\n${typesEnvContents}`; await fs.promises.writeFile(envTsPath, typesEnvContents, 'utf-8'); - info(logging, 'content', `Added ${bold(envTsPathRelativetoRoot)} types`); + logger.info('content', `Added ${bold(envTsPathRelativetoRoot)} types`); } } else { // Otherwise, inject the `env.d.ts` file @@ -81,6 +81,6 @@ export async function setUpEnvTs({ await fs.promises.mkdir(settings.config.srcDir, { recursive: true }); await fs.promises.writeFile(envTsPath, referenceDefs.join('\n'), 'utf-8'); - info(logging, 'astro', `Added ${bold(envTsPathRelativetoRoot)} types`); + logger.info('astro', `Added ${bold(envTsPathRelativetoRoot)} types`); } } diff --git a/packages/astro/src/vite-plugin-integrations-container/index.ts b/packages/astro/src/vite-plugin-integrations-container/index.ts index 6cc2da1528ad..b18d2e5e9956 100644 --- a/packages/astro/src/vite-plugin-integrations-container/index.ts +++ b/packages/astro/src/vite-plugin-integrations-container/index.ts @@ -1,7 +1,7 @@ import type { PluginContext } from 'rollup'; import type { Plugin as VitePlugin } from 'vite'; import type { AstroSettings, InjectedRoute, ResolvedInjectedRoute } from '../@types/astro.js'; -import type { LogOptions } from '../core/logger/core.js'; +import type { Logger } from '../core/logger/core.js'; import { normalizePath } from 'vite'; import { runHookServerSetup } from '../integrations/index.js'; @@ -9,16 +9,16 @@ import { runHookServerSetup } from '../integrations/index.js'; /** Connect Astro integrations into Vite, as needed. */ export default function astroIntegrationsContainerPlugin({ settings, - logging, + logger, }: { settings: AstroSettings; - logging: LogOptions; + logger: Logger; }): VitePlugin { return { name: 'astro:integration-container', async configureServer(server) { if (server.config.isProduction) return; - await runHookServerSetup({ config: settings.config, server, logging }); + await runHookServerSetup({ config: settings.config, server, logger }); }, async buildStart() { if (settings.injectedRoutes.length === settings.resolvedInjectedRoutes.length) return; diff --git a/packages/astro/src/vite-plugin-markdown/index.ts b/packages/astro/src/vite-plugin-markdown/index.ts index ae26bfb420f2..cc86d1fc1bcc 100644 --- a/packages/astro/src/vite-plugin-markdown/index.ts +++ b/packages/astro/src/vite-plugin-markdown/index.ts @@ -11,15 +11,14 @@ import type { Plugin } from 'vite'; import { normalizePath } from 'vite'; import type { AstroSettings } from '../@types/astro'; import { AstroError, AstroErrorData, MarkdownError } from '../core/errors/index.js'; -import type { LogOptions } from '../core/logger/core.js'; -import { warn } from '../core/logger/core.js'; +import type { Logger } from '../core/logger/core.js'; import { isMarkdownFile, rootRelativePath } from '../core/util.js'; import type { PluginMetadata } from '../vite-plugin-astro/types.js'; import { escapeViteEnvReferences, getFileInfo } from '../vite-plugin-utils/index.js'; interface AstroPluginOptions { settings: AstroSettings; - logging: LogOptions; + logger: Logger; } function safeMatter(source: string, id: string) { @@ -57,7 +56,7 @@ const astroErrorModulePath = normalizePath( fileURLToPath(new URL('../core/errors/index.js', import.meta.url)) ); -export default function markdown({ settings, logging }: AstroPluginOptions): Plugin { +export default function markdown({ settings, logger }: AstroPluginOptions): Plugin { return { enforce: 'pre', name: 'astro:markdown', @@ -101,8 +100,7 @@ export default function markdown({ settings, logging }: AstroPluginOptions): Plu const { layout } = frontmatter; if (frontmatter.setup) { - warn( - logging, + logger.warn( 'markdown', `[${id}] Astro now supports MDX! Support for components in ".md" (or alternative extensions like ".markdown") files using the "setup" frontmatter is no longer enabled by default. Migrate this file to MDX.` ); diff --git a/packages/astro/src/vite-plugin-mdx/index.ts b/packages/astro/src/vite-plugin-mdx/index.ts index f2b06806831a..b640d6c80170 100644 --- a/packages/astro/src/vite-plugin-mdx/index.ts +++ b/packages/astro/src/vite-plugin-mdx/index.ts @@ -1,7 +1,7 @@ import type { TransformResult } from 'rollup'; import { transformWithEsbuild, type Plugin, type ResolvedConfig } from 'vite'; import type { AstroRenderer, AstroSettings } from '../@types/astro'; -import type { LogOptions } from '../core/logger/core.js'; +import type { Logger } from '../core/logger/core.js'; import type { PluginMetadata } from '../vite-plugin-astro/types'; import babel from '@babel/core'; @@ -73,7 +73,7 @@ async function transformJSX({ interface AstroPluginJSXOptions { settings: AstroSettings; - logging: LogOptions; + logger: Logger; } // Format inspired by https://github.com/vitejs/vite/blob/main/packages/vite/src/node/constants.ts#L54 diff --git a/packages/astro/src/vite-plugin-scanner/index.ts b/packages/astro/src/vite-plugin-scanner/index.ts index 106a2f08d4b5..d48aed203dde 100644 --- a/packages/astro/src/vite-plugin-scanner/index.ts +++ b/packages/astro/src/vite-plugin-scanner/index.ts @@ -1,25 +1,23 @@ -import type { Plugin as VitePlugin } from 'vite'; -import type { AstroSettings } from '../@types/astro.js'; -import { type LogOptions } from '../core/logger/core.js'; - import { bold } from 'kleur/colors'; import { extname } from 'node:path'; +import type { Plugin as VitePlugin } from 'vite'; import { normalizePath } from 'vite'; -import { warn } from '../core/logger/core.js'; +import type { AstroSettings } from '../@types/astro.js'; +import { type Logger } from '../core/logger/core.js'; import { isEndpoint, isPage, rootRelativePath } from '../core/util.js'; import { getPrerenderDefault, isServerLikeOutput } from '../prerender/utils.js'; import { scan } from './scan.js'; export interface AstroPluginScannerOptions { settings: AstroSettings; - logging: LogOptions; + logger: Logger; } const KNOWN_FILE_EXTENSIONS = ['.astro', '.js', '.ts']; export default function astroScannerPlugin({ settings, - logging, + logger, }: AstroPluginScannerOptions): VitePlugin { return { name: 'astro:scanner', @@ -55,8 +53,7 @@ export default function astroScannerPlugin({ KNOWN_FILE_EXTENSIONS.includes(extname(filename)) ) { const reason = ` because \`output: "${settings.config.output}"\` is set`; - warn( - logging, + logger.warn( 'getStaticPaths', `The getStaticPaths() statement in ${bold( rootRelativePath(settings.config.root, fileURL, true) diff --git a/packages/astro/test/core-image.test.js b/packages/astro/test/core-image.test.js index 26fafe41aaf7..c2f9b698276a 100644 --- a/packages/astro/test/core-image.test.js +++ b/packages/astro/test/core-image.test.js @@ -6,6 +6,7 @@ import { removeDir } from '../dist/core/fs/index.js'; import testAdapter from './test-adapter.js'; import { testImageService } from './test-image-service.js'; import { loadFixture } from './test-utils.js'; +import { Logger } from '../dist/core/logger/core.js'; describe('astro:image', () => { /** @type {import('./test-utils').Fixture} */ @@ -27,7 +28,7 @@ describe('astro:image', () => { }); devServer = await fixture.startDevServer({ - logging: { + logger: new Logger({ level: 'error', dest: new Writable({ objectMode: true, @@ -36,7 +37,7 @@ describe('astro:image', () => { callback(); }, }), - }, + }), }); }); @@ -447,7 +448,7 @@ describe('astro:image', () => { }); devServer = await fixture.startDevServer({ - logging: { + logger: new Logger({ level: 'error', dest: new Writable({ objectMode: true, @@ -456,7 +457,7 @@ describe('astro:image', () => { callback(); }, }), - }, + }), }); }); diff --git a/packages/astro/test/fixtures/middleware-dev/src/middleware.js b/packages/astro/test/fixtures/middleware-dev/src/middleware.js index 854c997c1099..eeb902fb81cf 100644 --- a/packages/astro/test/fixtures/middleware-dev/src/middleware.js +++ b/packages/astro/test/fixtures/middleware-dev/src/middleware.js @@ -18,18 +18,20 @@ const first = defineMiddleware(async (context, next) => { return new Response(JSON.stringify(object), { headers: response.headers, }); - } else if(context.url.pathname === '/clone') { + } else if (context.url.pathname === '/clone') { const response = await next(); const newResponse = response.clone(); const /** @type {string} */ html = await newResponse.text(); const newhtml = html.replace('

testing

', '

it works

'); return new Response(newhtml, { status: 200, headers: response.headers }); } else { - if(context.url.pathname === '/') { + if (context.url.pathname === '/') { context.cookies.set('foo', 'bar'); } - context.locals.name = 'bar'; + context.locals = { + name: 'bar', + }; } return await next(); }); diff --git a/packages/astro/test/fixtures/middleware-dev/src/pages/404.astro b/packages/astro/test/fixtures/middleware-dev/src/pages/404.astro new file mode 100644 index 000000000000..ea85d240cece --- /dev/null +++ b/packages/astro/test/fixtures/middleware-dev/src/pages/404.astro @@ -0,0 +1,6 @@ +--- +const name = Astro.locals.name; +--- + +Error +

{name}

diff --git a/packages/astro/test/middleware.test.js b/packages/astro/test/middleware.test.js index 3796a341fcab..9ca4841d4e4e 100644 --- a/packages/astro/test/middleware.test.js +++ b/packages/astro/test/middleware.test.js @@ -211,6 +211,16 @@ describe('Middleware API in PROD mode, SSR', () => { expect(text.includes('REDACTED')).to.be.true; }); + it('should correctly call the middleware function for 404', async () => { + const app = await fixture.loadTestAdapterApp(); + const request = new Request('http://example.com/funky-url'); + const routeData = app.match(request, { matchNotFound: true }); + const response = await app.render(request, routeData); + const text = await response.text(); + expect(text.includes('Error')).to.be.true; + expect(text.includes('bar')).to.be.true; + }); + it('the integration should receive the path to the middleware', async () => { fixture = await loadFixture({ root: './fixtures/middleware-dev/', @@ -219,10 +229,8 @@ describe('Middleware API in PROD mode, SSR', () => { excludeMiddleware: true, }, adapter: testAdapter({ - setEntryPoints(entryPointsOrMiddleware) { - if (entryPointsOrMiddleware instanceof URL) { - middlewarePath = entryPointsOrMiddleware; - } + setMiddlewareEntryPoint(entryPointsOrMiddleware) { + middlewarePath = entryPointsOrMiddleware; }, }), }); diff --git a/packages/astro/test/static-build.test.js b/packages/astro/test/static-build.test.js index 77c24f398c0b..54701fee1b17 100644 --- a/packages/astro/test/static-build.test.js +++ b/packages/astro/test/static-build.test.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { load as cheerioLoad } from 'cheerio'; import { loadFixture } from './test-utils.js'; +import { Logger } from '../dist/core/logger/core.js'; function addLeadingSlash(path) { return path.startsWith('/') ? path : '/' + path; @@ -22,22 +23,22 @@ describe('Static build', () => { let logs = []; before(async () => { - /** @type {import('../src/core/logger/core').LogOptions} */ - const logging = { + /** @type {import('../src/core/logger/core').Logger} */ + const logger = new Logger({ dest: { write(chunk) { logs.push(chunk); }, }, level: 'warn', - }; + }); fixture = await loadFixture({ root: './fixtures/static-build/', // test suite was authored when inlineStylesheets defaulted to never build: { inlineStylesheets: 'never' }, }); - await fixture.build({ logging }); + await fixture.build({ logger }); }); it('Builds out .astro pages', async () => { diff --git a/packages/astro/test/test-adapter.js b/packages/astro/test/test-adapter.js index 67058023ddd6..0090b6d9da92 100644 --- a/packages/astro/test/test-adapter.js +++ b/packages/astro/test/test-adapter.js @@ -5,7 +5,13 @@ import { viteID } from '../dist/core/util.js'; * @returns {import('../src/@types/astro').AstroIntegration} */ export default function ( - { provideAddress = true, extendAdapter, setEntryPoints = undefined, setRoutes = undefined } = { + { + provideAddress = true, + extendAdapter, + setEntryPoints = undefined, + setMiddlewareEntryPoint = undefined, + setRoutes = undefined, + } = { provideAddress: true, } ) { @@ -86,7 +92,9 @@ export default function ( 'astro:build:ssr': ({ entryPoints, middlewareEntryPoint }) => { if (setEntryPoints) { setEntryPoints(entryPoints); - setEntryPoints(middlewareEntryPoint); + } + if (setMiddlewareEntryPoint) { + setMiddlewareEntryPoint(middlewareEntryPoint); } }, 'astro:build:done': ({ routes }) => { diff --git a/packages/astro/test/units/config/config-validate.test.js b/packages/astro/test/units/config/config-validate.test.js index 604836747d45..48088468ae45 100644 --- a/packages/astro/test/units/config/config-validate.test.js +++ b/packages/astro/test/units/config/config-validate.test.js @@ -74,8 +74,7 @@ describe('Config Validation', () => { ); expect(configError instanceof z.ZodError).to.equal(true); expect(configError.errors[0].message).to.equal( - '`outDir` must not be placed inside `publicDir` to prevent an infinite loop. \ -Please adjust the directory configuration and try again' + 'The value of `outDir` must not point to a path within the folder set as `publicDir`, this will cause an infinite loop' ); }); }); diff --git a/packages/astro/test/units/integrations/api.test.js b/packages/astro/test/units/integrations/api.test.js index a420dd6c97bb..0a7d57929c27 100644 --- a/packages/astro/test/units/integrations/api.test.js +++ b/packages/astro/test/units/integrations/api.test.js @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { runHookBuildSetup } from '../../../dist/integrations/index.js'; import { validateSupportedFeatures } from '../../../dist/integrations/astroFeaturesValidation.js'; -import { defaultLogging } from '../test-utils.js'; +import { defaultLogger } from '../test-utils.js'; describe('Integration API', () => { it('runHookBuildSetup should work', async () => { @@ -23,7 +23,7 @@ describe('Integration API', () => { ], }, vite: {}, - logging: {}, + logger: defaultLogger, pages: new Map(), target: 'server', }); @@ -41,7 +41,7 @@ describe('Astro feature map', function () { { output: 'hybrid', }, - defaultLogging + defaultLogger ); expect(result['hybridOutput']).to.be.true; }); @@ -53,7 +53,7 @@ describe('Astro feature map', function () { { output: 'hybrid', }, - defaultLogging + defaultLogger ); expect(result['hybridOutput']).to.be.false; }); @@ -65,7 +65,7 @@ describe('Astro feature map', function () { { output: 'hybrid', }, - defaultLogging + defaultLogger ); expect(result['hybridOutput']).to.be.false; }); @@ -78,7 +78,7 @@ describe('Astro feature map', function () { { output: 'static', }, - defaultLogging + defaultLogger ); expect(result['staticOutput']).to.be.true; }); @@ -90,7 +90,7 @@ describe('Astro feature map', function () { { output: 'static', }, - defaultLogging + defaultLogger ); expect(result['staticOutput']).to.be.false; }); @@ -103,7 +103,7 @@ describe('Astro feature map', function () { { output: 'hybrid', }, - defaultLogging + defaultLogger ); expect(result['hybridOutput']).to.be.true; }); @@ -117,7 +117,7 @@ describe('Astro feature map', function () { { output: 'hybrid', }, - defaultLogging + defaultLogger ); expect(result['hybridOutput']).to.be.false; }); @@ -130,7 +130,7 @@ describe('Astro feature map', function () { { output: 'server', }, - defaultLogging + defaultLogger ); expect(result['serverOutput']).to.be.true; }); @@ -144,7 +144,7 @@ describe('Astro feature map', function () { { output: 'server', }, - defaultLogging + defaultLogger ); expect(result['serverOutput']).to.be.false; }); @@ -167,7 +167,7 @@ describe('Astro feature map', function () { }, }, }, - defaultLogging + defaultLogger ); expect(result['assets']).to.be.true; }); @@ -187,7 +187,7 @@ describe('Astro feature map', function () { }, }, }, - defaultLogging + defaultLogger ); expect(result['assets']).to.be.true; }); @@ -208,7 +208,7 @@ describe('Astro feature map', function () { }, }, }, - defaultLogging + defaultLogger ); expect(result['assets']).to.be.false; }); diff --git a/packages/astro/test/units/routing/manifest.test.js b/packages/astro/test/units/routing/manifest.test.js index cf3fb0bf135e..a7014d853152 100644 --- a/packages/astro/test/units/routing/manifest.test.js +++ b/packages/astro/test/units/routing/manifest.test.js @@ -2,7 +2,7 @@ import { expect } from 'chai'; import { fileURLToPath } from 'node:url'; import { createRouteManifest } from '../../../dist/core/routing/manifest/create.js'; -import { createBasicSettings, createFs, defaultLogging } from '../test-utils.js'; +import { createBasicSettings, createFs, defaultLogger } from '../test-utils.js'; const root = new URL('../../fixtures/alias/', import.meta.url); @@ -77,7 +77,7 @@ describe('routing - createRouteManifest', () => { settings, fsMod: fs, }, - defaultLogging + defaultLogger ); expect(manifest.routes[0].route).to.equal('/foo'); diff --git a/packages/astro/test/units/routing/route-matching.test.js b/packages/astro/test/units/routing/route-matching.test.js index c0bf7d6e6c7c..6f4724893c4a 100644 --- a/packages/astro/test/units/routing/route-matching.test.js +++ b/packages/astro/test/units/routing/route-matching.test.js @@ -2,7 +2,7 @@ import { createBasicSettings, createFs, createRequestAndResponse, - defaultLogging, + defaultLogger, } from '../test-utils.js'; import { createRouteManifest, matchAllRoutes } from '../../../dist/core/routing/index.js'; import { fileURLToPath } from 'node:url'; @@ -140,19 +140,19 @@ describe('Route matching', () => { container = await createContainer({ fs, settings, - logging: defaultLogging, + logger: defaultLogger, }); const loader = createViteLoader(container.viteServer); const manifest = createDevelopmentManifest(container.settings); - pipeline = new DevPipeline({ manifest, logging: defaultLogging, settings, loader }); + pipeline = new DevPipeline({ manifest, logger: defaultLogger, settings, loader }); manifestData = createRouteManifest( { cwd: fileURLToPath(root), settings, fsMod: fs, }, - defaultLogging + defaultLogger ); }); diff --git a/packages/astro/test/units/shiki/shiki.test.js b/packages/astro/test/units/shiki/shiki.test.js index e5b78963f187..d88f3c31e631 100644 --- a/packages/astro/test/units/shiki/shiki.test.js +++ b/packages/astro/test/units/shiki/shiki.test.js @@ -2,7 +2,7 @@ import { expect } from 'chai'; import { fileURLToPath } from 'node:url'; import { createContainer } from '../../../dist/core/dev/index.js'; import { createViteLoader } from '../../../dist/core/module-loader/index.js'; -import { createBasicSettings, defaultLogging } from '../test-utils.js'; +import { createBasicSettings, defaultLogger } from '../test-utils.js'; const root = new URL('../../fixtures/alias/', import.meta.url); @@ -12,7 +12,7 @@ describe('', () => { let mod; before(async () => { const settings = await createBasicSettings({ root: fileURLToPath(root) }); - container = await createContainer({ settings, logging: defaultLogging }); + container = await createContainer({ settings, logger: defaultLogger }); const loader = createViteLoader(container.viteServer); mod = await loader.import('astro/components/Shiki.js'); }); diff --git a/packages/astro/test/units/test-utils.js b/packages/astro/test/units/test-utils.js index 80dff5ddb7b7..f8d6dce2487b 100644 --- a/packages/astro/test/units/test-utils.js +++ b/packages/astro/test/units/test-utils.js @@ -12,12 +12,13 @@ import { resolveConfig } from '../../dist/core/config/index.js'; import { createBaseSettings } from '../../dist/core/config/settings.js'; import { createContainer } from '../../dist/core/dev/container.js'; import { unixify } from './correct-path.js'; +import { Logger } from '../../dist/core/logger/core.js'; -/** @type {import('../../src/core/logger/core').LogOptions} */ -export const defaultLogging = { +/** @type {import('../../src/core/logger/core').Logger} */ +export const defaultLogger = new Logger({ dest: nodeLogDestination, level: 'error', -}; +}); /** @type {import('../../src/core/logger/core').LogOptions} */ export const silentLogging = { @@ -187,7 +188,7 @@ export function createBasicEnvironment(options = {}) { clientDirectives: getDefaultClientDirectives(), resolve: options.resolve ?? ((s) => Promise.resolve(s)), routeCache: new RouteCache(options.logging, mode), - logging: options.logging ?? defaultLogging, + logger: options.logger ?? defaultLogger, ssr: options.ssr ?? true, streaming: options.streaming ?? true, }); @@ -223,7 +224,7 @@ export async function runInContainer(options = {}, callback) { fs: options?.fs ?? realFS, settings, inlineConfig: options.inlineConfig ?? {}, - logging: defaultLogging, + logger: defaultLogger, }); try { await callback(container); diff --git a/packages/astro/test/units/vite-plugin-astro-server/request.test.js b/packages/astro/test/units/vite-plugin-astro-server/request.test.js index 58ad404fd339..d3472c56b59b 100644 --- a/packages/astro/test/units/vite-plugin-astro-server/request.test.js +++ b/packages/astro/test/units/vite-plugin-astro-server/request.test.js @@ -8,7 +8,7 @@ import { createBasicSettings, createFs, createRequestAndResponse, - defaultLogging, + defaultLogger, } from '../test-utils.js'; import { createDevelopmentManifest } from '../../../dist/vite-plugin-astro-server/plugin.js'; import DevPipeline from '../../../dist/vite-plugin-astro-server/devPipeline.js'; @@ -21,7 +21,7 @@ async function createDevPipeline(overrides = {}) { return new DevPipeline({ manifest, settings, - logging: defaultLogging, + logging: defaultLogger, loader, }); } @@ -53,7 +53,7 @@ describe('vite-plugin-astro-server', () => { fsMod: fs, settings: pipeline.getSettings(), }, - defaultLogging + defaultLogger ); try {