diff --git a/.changeset/seven-carpets-accept.md b/.changeset/seven-carpets-accept.md new file mode 100644 index 000000000..c3d418a8f --- /dev/null +++ b/.changeset/seven-carpets-accept.md @@ -0,0 +1,6 @@ +--- +'astro': patch +'@astrojs/markdoc': patch +--- + +Improve error message for unsupported Zod transforms from the content config. diff --git a/packages/astro/package.json b/packages/astro/package.json index 7e28c40a2..765206552 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -134,7 +134,7 @@ "cookie": "^0.5.0", "debug": "^4.3.4", "deepmerge-ts": "^4.2.2", - "devalue": "^4.2.0", + "devalue": "^4.3.2", "diff": "^5.1.0", "es-module-lexer": "^1.1.0", "esbuild": "^0.17.18", diff --git a/packages/astro/src/content/vite-plugin-content-imports.ts b/packages/astro/src/content/vite-plugin-content-imports.ts index 8e8ee0ba0..aa2fec179 100644 --- a/packages/astro/src/content/vite-plugin-content-imports.ts +++ b/packages/astro/src/content/vite-plugin-content-imports.ts @@ -94,7 +94,7 @@ export function astroContentImportPlugin({ const code = escapeViteEnvReferences(` export const id = ${JSON.stringify(id)}; export const collection = ${JSON.stringify(collection)}; -export const data = ${devalue.uneval(data) /* TODO: reuse astro props serializer */}; +export const data = ${stringifyEntryData(data)}; export const _internal = { type: 'data', filePath: ${JSON.stringify(_internal.filePath)}, @@ -118,7 +118,7 @@ export const _internal = { export const collection = ${JSON.stringify(collection)}; export const slug = ${JSON.stringify(slug)}; export const body = ${JSON.stringify(body)}; - export const data = ${devalue.uneval(data) /* TODO: reuse astro props serializer */}; + export const data = ${stringifyEntryData(data)}; export const _internal = { type: 'content', filePath: ${JSON.stringify(_internal.filePath)}, @@ -352,3 +352,28 @@ async function getContentConfigFromGlobal() { return contentConfig; } + +/** Stringify entry `data` at build time to be used as a Vite module */ +function stringifyEntryData(data: Record): string { + try { + return devalue.uneval(data, (value) => { + // Add support for URL objects + if (value instanceof URL) { + return `new URL(${JSON.stringify(value.href)})`; + } + }); + } catch (e) { + if (e instanceof Error) { + throw new AstroError({ + ...AstroErrorData.UnsupportedConfigTransformError, + message: AstroErrorData.UnsupportedConfigTransformError.message(e.message), + stack: e.stack, + }); + } else { + throw new AstroError({ + code: 99999, + message: 'Unexpected error processing content collection data.', + }); + } + } +} diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts index 0425eb22e..17a5db4fe 100644 --- a/packages/astro/src/core/errors/errors-data.ts +++ b/packages/astro/src/core/errors/errors-data.ts @@ -1113,6 +1113,21 @@ See https://docs.astro.build/en/guides/server-side-rendering/ for more informati hint: 'Ensure your data entry is an object with valid JSON (for `.json` entries) or YAML (for `.yaml` entries).', }, + /** + * @docs + * @see + * - [devalue library](https://github.com/rich-harris/devalue) + * @description + * `transform()` functions in your content config must return valid JSON, or data types compatible with the devalue library (including Dates, Maps, and Sets). + */ + UnsupportedConfigTransformError: { + title: 'Unsupported transform in content config.', + code: 9008, + message: (parseError: string) => + `\`transform()\` functions in your content config must return valid JSON, or data types compatible with the devalue library (including Dates, Maps, and Sets).\nFull error: ${parseError}`, + hint: 'See the devalue library for all supported types: https://github.com/rich-harris/devalue', + }, + // Generic catch-all - Only use this in extreme cases, like if there was a cosmic ray bit flip UnknownError: { title: 'Unknown Error.', diff --git a/packages/integrations/markdoc/package.json b/packages/integrations/markdoc/package.json index 061478b14..9c30d692b 100644 --- a/packages/integrations/markdoc/package.json +++ b/packages/integrations/markdoc/package.json @@ -63,13 +63,13 @@ "test:match": "mocha --timeout 20000 -g" }, "dependencies": { - "shiki": "^0.14.1", "@astrojs/prism": "^2.1.2", "@markdoc/markdoc": "^0.3.0", "esbuild": "^0.17.12", "github-slugger": "^2.0.0", "gray-matter": "^4.0.3", "kleur": "^4.1.5", + "shiki": "^0.14.1", "zod": "^3.17.3" }, "peerDependencies": { @@ -83,7 +83,7 @@ "astro": "workspace:*", "astro-scripts": "workspace:*", "chai": "^4.3.6", - "devalue": "^4.2.0", + "devalue": "^4.3.2", "linkedom": "^0.14.12", "mocha": "^9.2.2", "rollup": "^3.20.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c044069c8..308701418 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -559,8 +559,8 @@ importers: specifier: ^4.2.2 version: 4.2.2 devalue: - specifier: ^4.2.0 - version: 4.2.0 + specifier: ^4.3.2 + version: 4.3.2 diff: specifier: ^5.1.0 version: 5.1.0 @@ -3990,8 +3990,8 @@ importers: specifier: ^4.3.6 version: 4.3.6 devalue: - specifier: ^4.2.0 - version: 4.2.0 + specifier: ^4.3.2 + version: 4.3.2 linkedom: specifier: ^0.14.12 version: 0.14.17 @@ -10870,8 +10870,8 @@ packages: resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==} engines: {node: '>=8'} - /devalue@4.2.0: - resolution: {integrity: sha512-mbjoAaCL2qogBKgeFxFPOXAUsZchircF+B/79LD4sHH0+NHfYm8gZpQrskKDn5gENGt35+5OI1GUF7hLVnkPDw==} + /devalue@4.3.2: + resolution: {integrity: sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==} /didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}