Skip to content

Commit

Permalink
fix(astro): type generation for empty collections (#11264)
Browse files Browse the repository at this point in the history
* fix(astro): type generation for empty collections

* Update .changeset/light-bugs-shake.md

Co-authored-by: Florian Lefebvre <contact@florian-lefebvre.dev>

---------

Co-authored-by: Florian Lefebvre <contact@florian-lefebvre.dev>
  • Loading branch information
Fryuni and florian-lefebvre authored Jun 19, 2024
1 parent 246bd7a commit 5a9c9a6
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 21 deletions.
5 changes: 5 additions & 0 deletions .changeset/light-bugs-shake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fixes type generation for empty content collections
50 changes: 29 additions & 21 deletions packages/astro/src/content/types-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,18 @@ type DataEntryMetadata = Record<string, never>;
type ContentEntryMetadata = { slug: string };
type CollectionEntryMap = {
[collection: string]:
| {
type: 'unknown';
entries: Record<string, never>;
}
| {
type: 'content';
entries: Record<string, ContentEntryMetadata>;
}
| {
type: 'data';
entries: Record<string, DataEntryMetadata>;
};
| {
type: 'unknown';
entries: Record<string, never>;
}
| {
type: 'content';
entries: Record<string, ContentEntryMetadata>;
}
| {
type: 'data';
entries: Record<string, DataEntryMetadata>;
};
};

type CreateContentGeneratorParams = {
Expand Down Expand Up @@ -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<string, {\n id: string;\n slug: string;\n body: string;\n collection: ${collectionKey};\n data: ${dataType};\n render(): Render[".md"];\n}>;\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))
)}] }`;
Expand All @@ -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<string, {\n id: string;\n collection: ${collectionKey};\n data: ${dataType};\n}>;\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) {
Expand Down Expand Up @@ -481,7 +490,6 @@ async function writeContentFiles({
);
}
}
dataTypesStr += `};\n`;
break;
}
}
Expand Down
26 changes: 26 additions & 0 deletions packages/astro/test/astro-sync.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, {
id: string;
slug: string;
body: string;
collection: "blog";
data: InferEntrySchema<"blog">;
render(): Render[".md"];
}>;`,
'Types file does not include empty collection type'
);
fixture.thenFileContentShouldInclude(
'.astro/types.d.ts',
`"blogMeta": Record<string, {
id: string;
collection: "blogMeta";
data: InferEntrySchema<"blogMeta">;
}>;`,
'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');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ const blog = defineCollection({
}),
});

const blogMeta = defineCollection({
type: 'data',
schema: z.object({
title: z.string(),
}),
});

export const collections = {
blog,
blogMeta,
};

0 comments on commit 5a9c9a6

Please sign in to comment.