diff --git a/.changeset/olive-rabbits-rhyme.md b/.changeset/olive-rabbits-rhyme.md new file mode 100644 index 000000000000..7720c1ed541e --- /dev/null +++ b/.changeset/olive-rabbits-rhyme.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fix content collection build errors for empty collections or underscore files of type `.json`. diff --git a/packages/astro/src/content/types-generator.ts b/packages/astro/src/content/types-generator.ts index 0280fc08b672..7958c330feb7 100644 --- a/packages/astro/src/content/types-generator.ts +++ b/packages/astro/src/content/types-generator.ts @@ -414,7 +414,11 @@ async function writeContentFiles({ for (const collectionKey of Object.keys(collectionEntryMap).sort()) { const collectionConfig = contentConfig?.collections[JSON.parse(collectionKey)]; const collection = collectionEntryMap[collectionKey]; - if (collectionConfig?.type && collection.type !== collectionConfig.type) { + if ( + collectionConfig?.type && + collection.type !== 'unknown' && + collection.type !== collectionConfig.type + ) { viteServer.ws.send({ type: 'error', err: new AstroError({ @@ -433,7 +437,14 @@ async function writeContentFiles({ }); return; } - switch (collection.type) { + const resolvedType: 'content' | 'data' = + collection.type === 'unknown' + ? // Add empty / unknown collections to the data type map by default + // This ensures `getCollection('empty-collection')` doesn't raise a type error + collectionConfig?.type ?? 'data' + : collection.type; + + switch (resolvedType) { case 'content': contentTypesStr += `${collectionKey}: {\n`; for (const entryKey of Object.keys(collection.entries).sort()) { @@ -449,9 +460,6 @@ async function writeContentFiles({ contentTypesStr += `};\n`; break; case 'data': - // Add empty / unknown collections to the data type map by default - // This ensures `getCollection('empty-collection')` doesn't raise a type error - case 'unknown': dataTypesStr += `${collectionKey}: {\n`; for (const entryKey of Object.keys(collection.entries).sort()) { const dataType = collectionConfig?.schema ? `InferEntrySchema<${collectionKey}>` : 'any'; diff --git a/packages/astro/src/content/vite-plugin-content-virtual-mod.ts b/packages/astro/src/content/vite-plugin-content-virtual-mod.ts index 78a8cd24d2ee..838d6b425696 100644 --- a/packages/astro/src/content/vite-plugin-content-virtual-mod.ts +++ b/packages/astro/src/content/vite-plugin-content-virtual-mod.ts @@ -43,8 +43,12 @@ export function astroContentVirtualModPlugin({ new URL('reference-map.json', contentPaths.cacheDir).pathname ) .replace('@@CONTENT_DIR@@', relContentDir) - .replace('@@CONTENT_ENTRY_GLOB_PATH@@', `${relContentDir}**/*${getExtGlob(contentEntryExts)}`) - .replace('@@DATA_ENTRY_GLOB_PATH@@', `${relContentDir}**/*${getExtGlob(dataEntryExts)}`) + .replace( + '@@CONTENT_ENTRY_GLOB_PATH@@', + // [!_] = ignore files starting with "_" + `${relContentDir}**/[!_]*${getExtGlob(contentEntryExts)}` + ) + .replace('@@DATA_ENTRY_GLOB_PATH@@', `${relContentDir}**/[!_]*${getExtGlob(dataEntryExts)}`) .replace( '@@RENDER_ENTRY_GLOB_PATH@@', `${relContentDir}**/*${getExtGlob(/** Note: data collections excluded */ contentEntryExts)}` diff --git a/packages/astro/test/units/dev/collections-mixed-content-errors.test.js b/packages/astro/test/units/dev/collections-mixed-content-errors.test.js index 355f54a0fc23..d323946f8d5b 100644 --- a/packages/astro/test/units/dev/collections-mixed-content-errors.test.js +++ b/packages/astro/test/units/dev/collections-mixed-content-errors.test.js @@ -119,4 +119,32 @@ title: Post expect(e.hint).to.include("Try adding `type: 'data'`"); } }); + + it('does not raise error for empty collection with config', async () => { + const fs = createFsWithFallback( + { + // Add placeholder to ensure directory exists + '/src/content/i18n/_placeholder.txt': 'Need content here', + '/src/content/config.ts': ` + import { z, defineCollection } from 'astro:content'; + + const i18n = defineCollection({ + type: 'data', + schema: z.object({ + greeting: z.string(), + }), + }); + + export const collections = { i18n };`, + }, + root + ); + + try { + const res = await sync({ fs }); + expect(res).to.equal(0); + } catch (e) { + expect.fail(0, 1, `Did not expect sync to throw: ${e.message}`); + } + }); });