diff --git a/.changeset/sweet-coins-shop.md b/.changeset/sweet-coins-shop.md new file mode 100644 index 000000000000..02d69af15c6e --- /dev/null +++ b/.changeset/sweet-coins-shop.md @@ -0,0 +1,5 @@ +--- +"astro": patch +--- + +Prevent getCollection breaking in vitest diff --git a/packages/astro/src/assets/utils/emitAsset.ts b/packages/astro/src/assets/utils/emitAsset.ts index 545299605c7f..b6fc14efa791 100644 --- a/packages/astro/src/assets/utils/emitAsset.ts +++ b/packages/astro/src/assets/utils/emitAsset.ts @@ -3,12 +3,18 @@ import path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { prependForwardSlash, slash } from '../../core/path.js'; import type { ImageMetadata } from '../types.js'; +import type * as vite from 'vite'; import { imageMetadata } from './metadata.js'; +type FileEmitter = vite.Rollup.EmitFile; + export async function emitESMImage( id: string | undefined, - watchMode: boolean, - fileEmitter: any + /** @deprecated */ + _watchMode: boolean, + // FIX: in Astro 5, this function should not be passed in dev mode at all. + // Or rethink the API so that a function that throws isn't passed through. + fileEmitter?: FileEmitter, ): Promise { if (!id) { return undefined; @@ -37,18 +43,26 @@ export async function emitESMImage( }); // Build - if (!watchMode) { + let isBuild = typeof fileEmitter === 'function'; + if(isBuild) { const pathname = decodeURI(url.pathname); const filename = path.basename(pathname, path.extname(pathname) + `.${fileMetadata.format}`); - const handle = fileEmitter({ - name: filename, - source: await fs.readFile(url), - type: 'asset', - }); + try { + // fileEmitter throws in dev + const handle = fileEmitter!({ + name: filename, + source: await fs.readFile(url), + type: 'asset', + }); + + emittedImage.src = `__ASTRO_ASSET_IMAGE__${handle}__`; + } catch { + isBuild = false; + } + } - emittedImage.src = `__ASTRO_ASSET_IMAGE__${handle}__`; - } else { + if(!isBuild) { // Pass the original file information through query params so we don't have to load the file twice url.searchParams.append('origWidth', fileMetadata.width.toString()); url.searchParams.append('origHeight', fileMetadata.height.toString()); diff --git a/packages/astro/src/assets/vite-plugin-assets.ts b/packages/astro/src/assets/vite-plugin-assets.ts index 03a9bef29fad..6114e7bf9bdc 100644 --- a/packages/astro/src/assets/vite-plugin-assets.ts +++ b/packages/astro/src/assets/vite-plugin-assets.ts @@ -95,6 +95,7 @@ export default function assets({ mode, }: AstroPluginOptions & { mode: string }): vite.Plugin[] { let resolvedConfig: vite.ResolvedConfig; + let shouldEmitFile = false; globalThis.astroAsset = { referencedImages: new Set(), @@ -194,6 +195,9 @@ export default function assets({ { name: 'astro:assets:esm', enforce: 'pre', + config(_, env) { + shouldEmitFile = env.command === 'build'; + }, configResolved(viteConfig) { resolvedConfig = viteConfig; }, @@ -214,7 +218,8 @@ export default function assets({ return; } - const imageMetadata = await emitESMImage(id, this.meta.watchMode, this.emitFile); + const emitFile = shouldEmitFile ? this.emitFile : undefined; + const imageMetadata = await emitESMImage(id, this.meta.watchMode, emitFile); if (!imageMetadata) { throw new AstroError({ diff --git a/packages/astro/src/config/index.ts b/packages/astro/src/config/index.ts index fc45ae983476..f48bf4b153b2 100644 --- a/packages/astro/src/config/index.ts +++ b/packages/astro/src/config/index.ts @@ -35,8 +35,8 @@ export function getViteConfig(inlineConfig: UserConfig) { level: 'info', }); const { astroConfig: config } = await resolveConfig({}, cmd); - const settings = await createSettings(config, inlineConfig.root); - await runHookConfigSetup({ settings, command: cmd, logger }); + let settings = await createSettings(config, inlineConfig.root); + settings = await runHookConfigSetup({ settings, command: cmd, logger }); const viteConfig = await createVite( { mode, diff --git a/packages/astro/src/content/runtime-assets.ts b/packages/astro/src/content/runtime-assets.ts index a34d71bab738..30f25ee9c16e 100644 --- a/packages/astro/src/content/runtime-assets.ts +++ b/packages/astro/src/content/runtime-assets.ts @@ -2,14 +2,14 @@ import type { PluginContext } from 'rollup'; import { z } from 'zod'; import { emitESMImage } from '../assets/utils/emitAsset.js'; -export function createImage(pluginContext: PluginContext, entryFilePath: string) { +export function createImage(pluginContext: PluginContext, shouldEmitFile: boolean, entryFilePath: string) { return () => { return z.string().transform(async (imagePath, ctx) => { const resolvedFilePath = (await pluginContext.resolve(imagePath, entryFilePath))?.id; const metadata = await emitESMImage( resolvedFilePath, pluginContext.meta.watchMode, - pluginContext.emitFile + shouldEmitFile ? pluginContext.emitFile : undefined, ); if (!metadata) { diff --git a/packages/astro/src/content/utils.ts b/packages/astro/src/content/utils.ts index 0f18e9c07e04..4bec3dd8d52d 100644 --- a/packages/astro/src/content/utils.ts +++ b/packages/astro/src/content/utils.ts @@ -92,6 +92,7 @@ export async function getEntryData( _internal: EntryInternal; }, collectionConfig: CollectionConfig, + shouldEmitFile: boolean, pluginContext: PluginContext ) { let data; @@ -105,7 +106,7 @@ export async function getEntryData( let schema = collectionConfig.schema; if (typeof schema === 'function') { schema = schema({ - image: createImage(pluginContext, entry._internal.filePath), + image: createImage(pluginContext, shouldEmitFile, entry._internal.filePath), }); } diff --git a/packages/astro/src/content/vite-plugin-content-imports.ts b/packages/astro/src/content/vite-plugin-content-imports.ts index be232eb07cf6..654235971d76 100644 --- a/packages/astro/src/content/vite-plugin-content-imports.ts +++ b/packages/astro/src/content/vite-plugin-content-imports.ts @@ -74,10 +74,14 @@ export function astroContentImportPlugin({ const contentEntryConfigByExt = getEntryConfigByExtMap(settings.contentEntryTypes); const dataEntryConfigByExt = getEntryConfigByExtMap(settings.dataEntryTypes); const { contentDir } = contentPaths; + let shouldEmitFile = false; const plugins: Plugin[] = [ { name: 'astro:content-imports', + config(_config, env) { + shouldEmitFile = env.command === 'build'; + }, async transform(_, viteId) { if (hasContentFlag(viteId, DATA_FLAG)) { const fileId = viteId.split('?')[0] ?? viteId; @@ -90,6 +94,7 @@ export function astroContentImportPlugin({ config: settings.config, fs, pluginContext: this, + shouldEmitFile, }); const code = ` @@ -112,6 +117,7 @@ export const _internal = { config: settings.config, fs, pluginContext: this, + shouldEmitFile, }); const code = ` @@ -190,6 +196,7 @@ type GetEntryModuleParams = pluginContext: PluginContext; entryConfigByExt: Map; config: AstroConfig; + shouldEmitFile: boolean; }; async function getContentEntryModule( @@ -222,6 +229,7 @@ async function getContentEntryModule( ? await getEntryData( { id, collection, _internal, unvalidatedData }, collectionConfig, + params.shouldEmitFile, pluginContext ) : unvalidatedData; @@ -256,7 +264,8 @@ async function getDataEntryModule( ? await getEntryData( { id, collection, _internal, unvalidatedData }, collectionConfig, - pluginContext + params.shouldEmitFile, + pluginContext, ) : unvalidatedData;