Skip to content

Commit 07c1002

Browse files
authored
Fix content collection JSON schema generation (#15901)
* Add test that distinguishes Zod input/output shape in JSON schema output * Use input shape when generating JSON schema from Zod * Add changeset * Fix one additional test
1 parent 817afb6 commit 07c1002

5 files changed

Lines changed: 33 additions & 5 deletions

File tree

.changeset/slick-pans-smash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'astro': patch
3+
---
4+
5+
Fixes JSON schema generation for content collection schemas that have differences between their input and output shapes.

packages/astro/src/content/types-generator.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,9 @@ async function generateJSONSchema(
648648
ctx.jsonSchema.format = 'date-time';
649649
}
650650
},
651+
// Collection schemas are used for parsing collection input, so we need to tell Zod to use the
652+
// input shape when generating a JSON schema.
653+
io: 'input',
651654
});
652655
const schemaStr = JSON.stringify(schema, null, 2);
653656
const schemaJsonPath = new URL(

packages/astro/test/content-intellisense.test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,16 @@ describe('Content Intellisense', () => {
107107
true,
108108
);
109109
});
110+
111+
it('uses the Zod input shape to generate the JSON schema', async () => {
112+
const schema = JSON.parse(
113+
await fixture.readFile('../.astro/collections/io-differences.schema.json'),
114+
);
115+
assert.deepEqual(schema.properties.optionalWithDefault, {
116+
type: 'string',
117+
default: 'default value',
118+
});
119+
// The optional field with a default should bot be required.
120+
assert.ok(!schema.required.includes('optionalWithDefault'));
121+
});
110122
});

packages/astro/test/data-collections-schema.test.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,12 @@ describe('Content Collections - data collections', () => {
4141
},
4242
},
4343
required: ['greeting', 'preamble'],
44-
additionalProperties: false,
4544
},
4645
$schema: {
4746
type: 'string',
4847
},
4948
},
5049
required: ['homepage'],
51-
additionalProperties: false,
5250
}),
5351
JSON.stringify(JSON.parse(rawJson)),
5452
);
@@ -80,14 +78,12 @@ describe('Content Collections - data collections', () => {
8078
},
8179
},
8280
required: ['greeting', 'preamble', 'image'],
83-
additionalProperties: false,
8481
},
8582
$schema: {
8683
type: 'string',
8784
},
8885
},
8986
required: ['homepage'],
90-
additionalProperties: false,
9187
}),
9288
JSON.stringify(JSON.parse(rawJson)),
9389
);

packages/astro/test/fixtures/content-intellisense/src/content.config.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,23 @@ const dataDates = defineCollection({
3333
})
3434
})
3535

36+
// Zod features like `.default()` create a difference in the input and output shape of a schema.
37+
// This schema helps us check we use the input shape in generated JSON schemas.
38+
const schemaWithIODifferences = z.object({
39+
optionalWithDefault: z.string().optional().default('default value'),
40+
requiredProperty: z.string(),
41+
});
42+
const ioDifferences = defineCollection({
43+
loader: () => [{ id: '1', requiredProperty: 'defined' }],
44+
schema: schemaWithIODifferences
45+
});
46+
3647
export const collections = {
3748
"blog-cc": blogCC,
3849
"blog-cl": blogCL,
3950
"data-cl": dataYML,
4051
"data-cl-json": dataJSON,
4152
"data-schema-misuse": dataWithSchemaMisuse,
42-
"data-dates": dataDates
53+
"data-dates": dataDates,
54+
"io-differences": ioDifferences,
4355
};

0 commit comments

Comments
 (0)