From 5a9c9a60e7c32aa461b86b5bc667cb955e23d4d9 Mon Sep 17 00:00:00 2001 From: Luiz Ferraz Date: Wed, 19 Jun 2024 12:52:34 -0300 Subject: [PATCH] fix(astro): type generation for empty collections (#11264) * fix(astro): type generation for empty collections * Update .changeset/light-bugs-shake.md Co-authored-by: Florian Lefebvre --------- Co-authored-by: Florian Lefebvre --- .changeset/light-bugs-shake.md | 5 ++ packages/astro/src/content/types-generator.ts | 50 +++++++++++-------- packages/astro/test/astro-sync.test.js | 26 ++++++++++ .../src/content/config.ts | 8 +++ 4 files changed, 68 insertions(+), 21 deletions(-) create mode 100644 .changeset/light-bugs-shake.md diff --git a/.changeset/light-bugs-shake.md b/.changeset/light-bugs-shake.md new file mode 100644 index 0000000000..c5fe720934 --- /dev/null +++ b/.changeset/light-bugs-shake.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fixes type generation for empty content collections diff --git a/packages/astro/src/content/types-generator.ts b/packages/astro/src/content/types-generator.ts index 296e10417f..66e5aef3c8 100644 --- a/packages/astro/src/content/types-generator.ts +++ b/packages/astro/src/content/types-generator.ts @@ -35,18 +35,18 @@ type DataEntryMetadata = Record; type ContentEntryMetadata = { slug: string }; type CollectionEntryMap = { [collection: string]: - | { - type: 'unknown'; - entries: Record; - } - | { - type: 'content'; - entries: Record; - } - | { - type: 'data'; - entries: Record; - }; + | { + type: 'unknown'; + entries: Record; + } + | { + type: 'content'; + entries: Record; + } + | { + type: 'data'; + entries: Record; + }; }; type CreateContentGeneratorParams = { @@ -425,16 +425,21 @@ async function writeContentFiles({ 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' + // This ensures `getCollection('empty-collection')` doesn't raise a type error + collectionConfig?.type ?? 'data' : collection.type; + const collectionEntryKeys = Object.keys(collection.entries).sort(); + const dataType = collectionConfig?.schema ? `InferEntrySchema<${collectionKey}>` : 'any'; switch (resolvedType) { case 'content': + if (collectionEntryKeys.length === 0) { + contentTypesStr += `${collectionKey}: Record;\n`; + break; + } contentTypesStr += `${collectionKey}: {\n`; - for (const entryKey of Object.keys(collection.entries).sort()) { + for (const entryKey of collectionEntryKeys) { const entryMetadata = collection.entries[entryKey]; - const dataType = collectionConfig?.schema ? `InferEntrySchema<${collectionKey}>` : 'any'; const renderType = `{ render(): Render[${JSON.stringify( path.extname(JSON.parse(entryKey)) )}] }`; @@ -445,10 +450,14 @@ async function writeContentFiles({ contentTypesStr += `};\n`; break; case 'data': - dataTypesStr += `${collectionKey}: {\n`; - for (const entryKey of Object.keys(collection.entries).sort()) { - const dataType = collectionConfig?.schema ? `InferEntrySchema<${collectionKey}>` : 'any'; - dataTypesStr += `${entryKey}: {\n id: ${entryKey};\n collection: ${collectionKey};\n data: ${dataType}\n};\n`; + if (collectionEntryKeys.length === 0) { + dataTypesStr += `${collectionKey}: Record;\n`; + } else { + dataTypesStr += `${collectionKey}: {\n`; + for (const entryKey of collectionEntryKeys) { + dataTypesStr += `${entryKey}: {\n id: ${entryKey};\n collection: ${collectionKey};\n data: ${dataType}\n};\n`; + } + dataTypesStr += `};\n`; } if (settings.config.experimental.contentCollectionJsonSchema && collectionConfig?.schema) { @@ -481,7 +490,6 @@ async function writeContentFiles({ ); } } - dataTypesStr += `};\n`; break; } } diff --git a/packages/astro/test/astro-sync.test.js b/packages/astro/test/astro-sync.test.js index bfa8ab8838..8cc15f7dd8 100644 --- a/packages/astro/test/astro-sync.test.js +++ b/packages/astro/test/astro-sync.test.js @@ -89,6 +89,32 @@ describe('astro sync', () => { ); }); + it('Writes types for empty collections', async () => { + await fixture.whenSyncing('./fixtures/content-collections-empty-dir/'); + fixture.thenFileShouldExist('.astro/types.d.ts'); + fixture.thenFileContentShouldInclude( + '.astro/types.d.ts', + `"blog": Record; + render(): Render[".md"]; +}>;`, + 'Types file does not include empty collection type' + ); + fixture.thenFileContentShouldInclude( + '.astro/types.d.ts', + `"blogMeta": Record; +}>;`, + 'Types file does not include empty collection type' + ); + }); + it('Adds type reference to `src/env.d.ts`', async () => { await fixture.whenSyncing('./fixtures/content-collections/'); fixture.thenFileShouldExist('src/env.d.ts'); diff --git a/packages/astro/test/fixtures/content-collections-empty-dir/src/content/config.ts b/packages/astro/test/fixtures/content-collections-empty-dir/src/content/config.ts index 502bd5170c..fc21917920 100644 --- a/packages/astro/test/fixtures/content-collections-empty-dir/src/content/config.ts +++ b/packages/astro/test/fixtures/content-collections-empty-dir/src/content/config.ts @@ -6,6 +6,14 @@ const blog = defineCollection({ }), }); +const blogMeta = defineCollection({ + type: 'data', + schema: z.object({ + title: z.string(), + }), +}); + export const collections = { blog, + blogMeta, };