aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Luiz Ferraz <luiz@lferraz.com> 2024-06-19 12:52:34 -0300
committerGravatar GitHub <noreply@github.com> 2024-06-19 16:52:34 +0100
commit5a9c9a60e7c32aa461b86b5bc667cb955e23d4d9 (patch)
tree0674373d954b7d4b6b863d3251a0b82e32e6d5fc
parent246bd7a516e533af1b6d60dc4f9c0e0350c3be1d (diff)
downloadastro-5a9c9a60e7c32aa461b86b5bc667cb955e23d4d9.tar.gz
astro-5a9c9a60e7c32aa461b86b5bc667cb955e23d4d9.tar.zst
astro-5a9c9a60e7c32aa461b86b5bc667cb955e23d4d9.zip
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 <contact@florian-lefebvre.dev> --------- Co-authored-by: Florian Lefebvre <contact@florian-lefebvre.dev>
-rw-r--r--.changeset/light-bugs-shake.md5
-rw-r--r--packages/astro/src/content/types-generator.ts50
-rw-r--r--packages/astro/test/astro-sync.test.js26
-rw-r--r--packages/astro/test/fixtures/content-collections-empty-dir/src/content/config.ts8
4 files changed, 68 insertions, 21 deletions
diff --git a/.changeset/light-bugs-shake.md b/.changeset/light-bugs-shake.md
new file mode 100644
index 000000000..c5fe72093
--- /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 296e10417..66e5aef3c 100644
--- a/packages/astro/src/content/types-generator.ts
+++ b/packages/astro/src/content/types-generator.ts
@@ -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 = {
@@ -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))
)}] }`;
@@ -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) {
@@ -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 bfa8ab883..8cc15f7dd 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<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');
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 502bd5170..fc2191792 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,
};