0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-03-24 23:21:57 -05:00

Fix issue with JSON schema generation when schema is a function (#10426)

This commit is contained in:
Mark Gaze 2024-03-18 13:14:18 +00:00 committed by GitHub
parent b5927d57fa
commit 6a9a35ee15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 120 additions and 9 deletions

View file

@ -0,0 +1,5 @@
---
"astro": patch
---
Fixes an issue with generating JSON schemas when the schema is a function

View file

@ -1,9 +1,9 @@
import glob from 'fast-glob';
import { bold, cyan } from 'kleur/colors';
import type fsMod from 'node:fs';
import * as path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import glob from 'fast-glob';
import { bold, cyan } from 'kleur/colors';
import { type ViteDevServer, normalizePath } from 'vite';
import { normalizePath, type ViteDevServer } from 'vite';
import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';
import type { AstroSettings, ContentEntryType } from '../@types/astro.js';
@ -13,9 +13,6 @@ import type { Logger } from '../core/logger/core.js';
import { isRelativePath } from '../core/path.js';
import { CONTENT_TYPES_FILE, VIRTUAL_MODULE_ID } from './consts.js';
import {
type ContentConfig,
type ContentObservable,
type ContentPaths,
getContentEntryIdAndSlug,
getContentPaths,
getDataEntryExts,
@ -25,6 +22,9 @@ import {
getEntrySlug,
getEntryType,
reloadContentConfigObserver,
type ContentConfig,
type ContentObservable,
type ContentPaths,
} from './utils.js';
type ChokidarEvent = 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir';
@ -455,7 +455,10 @@ async function writeContentFiles({
settings.config.experimental.contentCollectionJsonSchema &&
collectionConfig?.schema
) {
let zodSchemaForJson = collectionConfig.schema;
let zodSchemaForJson =
typeof collectionConfig.schema === 'function'
? collectionConfig.schema({ image: () => z.string() })
: collectionConfig.schema;
if (zodSchemaForJson instanceof z.ZodObject) {
zodSchemaForJson = zodSchemaForJson.extend({
$schema: z.string().optional(),
@ -477,7 +480,7 @@ async function writeContentFiles({
} catch (err) {
logger.warn(
'content',
`An error was encountered while creating the JSON schema. Proceeding without it. Error: ${err}`
`An error was encountered while creating the JSON schema for the ${entryKey} entry in ${collectionKey} collection. Proceeding without it. Error: ${err}`
);
}
}

View file

@ -15,6 +15,11 @@ describe('Content Collections - data collections', () => {
assert.equal(schemaExists, true);
});
it('Generates schema file when the schema is a function', async () => {
const schemaExists = await fixture.pathExists('../.astro/collections/func.schema.json');
assert.equal(schemaExists, true);
});
it('Generates valid schema file', async () => {
const rawJson = await fixture.readFile('../.astro/collections/i18n.schema.json');
assert.deepEqual(
@ -50,5 +55,49 @@ describe('Content Collections - data collections', () => {
JSON.stringify(JSON.parse(rawJson))
);
});
it('Generates schema file when the schema uses the image function', async () => {
const schemaExists = await fixture.pathExists('../.astro/collections/image.schema.json');
assert.equal(schemaExists, true);
});
it('Generates valid schema file for an image', async () => {
const rawJson = await fixture.readFile('../.astro/collections/image.schema.json');
assert.deepEqual(
JSON.stringify({
$ref: '#/definitions/image',
definitions: {
image: {
type: 'object',
properties: {
homepage: {
type: 'object',
properties: {
greeting: {
type: 'string',
},
preamble: {
type: 'string',
},
image: {
type: 'string',
},
},
required: ['greeting', 'preamble', 'image'],
additionalProperties: false,
},
$schema: {
type: 'string',
},
},
required: ['homepage'],
additionalProperties: false,
},
},
$schema: 'http://json-schema.org/draft-07/schema#',
}),
JSON.stringify(JSON.parse(rawJson))
);
});
});
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 KiB

View file

@ -17,4 +17,25 @@ const i18n = defineCollection({
}),
});
export const collections = { docs, i18n };
const func = defineCollection({
type: 'data',
schema: () => z.object({
homepage: z.object({
greeting: z.string(),
preamble: z.string(),
})
}),
});
const image = defineCollection({
type: 'data',
schema: ({ image }) => z.object({
homepage: z.object({
greeting: z.string(),
preamble: z.string(),
image: image(),
})
}),
});
export const collections = { docs, func, image, i18n };

View file

@ -0,0 +1,6 @@
{
"homepage": {
"greeting": "Hello World!",
"preamble": "Welcome to the future of content."
}
}

View file

@ -0,0 +1,6 @@
{
"homepage": {
"greeting": "¡Hola Mundo!",
"preamble": "Bienvenido al futuro del contenido."
}
}

View file

@ -0,0 +1,3 @@
homepage:
greeting: "Bonjour le monde!"
preamble: "Bienvenue dans le futur du contenu."

View file

@ -0,0 +1,7 @@
{
"homepage": {
"greeting": "Hello World!",
"preamble": "Welcome to the future of content.",
"image": "/src/assets/hero.jpg"
}
}

View file

@ -0,0 +1,7 @@
{
"homepage": {
"greeting": "¡Hola Mundo!",
"preamble": "Bienvenido al futuro del contenido.",
"image": "/src/assets/hero.jpg"
}
}

View file

@ -0,0 +1,4 @@
homepage:
greeting: "Bonjour le monde!"
preamble: "Bienvenue dans le futur du contenu."
image: "/src/assets/hero.jpg"