UNPKG

astro

Version:

Astro is a modern site builder with web best practices, performance, and DX front-of-mind.

95 lines (94 loc) • 5.15 kB
import { errorMap } from "../errors/index.js"; import { AstroConfigRefinedSchema, createRelativeSchema } from "./schemas/index.js"; /** Turn raw config values into normalized values */ export async function validateConfig(userConfig, root, cmd) { const AstroConfigRelativeSchema = createRelativeSchema(cmd, root); await coerceLegacyMarkdownPlugins(userConfig); warnDeprecatedMarkdownOptions(userConfig); // First-Pass Validation return await validateConfigRefined(await AstroConfigRelativeSchema.parseAsync(userConfig, { error(issue) { // If an experimental feature, give a more specific error message. if (issue.path?.[0] === "experimental") { return { message: `Invalid or outdated experimental feature.\nCheck for incorrect spelling or outdated Astro version.\nSee https://docs.astro.build/en/reference/experimental-flags/ for a list of all current experiments.` }; } return errorMap(issue); } })); } let didWarnAboutDeprecatedMarkdownOptions = false; function warnDeprecatedMarkdownOptions(config) { if (didWarnAboutDeprecatedMarkdownOptions) return; const md = config?.markdown; if (!md) return; const deprecated = ["gfm", "smartypants"].filter((k) => md[k] !== undefined); if (deprecated.length === 0) return; didWarnAboutDeprecatedMarkdownOptions = true; const names = deprecated.map((key) => `\`markdown.${key}\``).join(" and "); const isPlural = deprecated.length > 1; console.warn(`[astro] ${names} ${isPlural ? "are" : "is"} deprecated. Move ${isPlural ? "them" : "it"} onto your processor instead (e.g. \`satteri({ features: { gfm: false, smartPunctuation: false } })\`, or \`unified({ gfm: false, smartypants: false })\` from \`@astrojs/markdown-remark\`). Will be removed in a future major.`); } let didWarnAboutLegacyMarkdownPlugins = false; let didWarnAboutProcessorMismatch = false; const migratedLegacyPluginCounts = new WeakMap(); /** * Folds legacy `markdown.{remark,rehype}Plugins` / `remarkRehype` into the unified * processor (mutates `config`). Runs on every validate pass to catch integration-added * legacy plugins. */ async function coerceLegacyMarkdownPlugins(config) { const md = config?.markdown; if (!md) return; const remarkPlugins = md.remarkPlugins ?? []; const rehypePlugins = md.rehypePlugins ?? []; const remarkRehype = md.remarkRehype ?? {}; if (remarkPlugins.length === 0 && rehypePlugins.length === 0 && Object.keys(remarkRehype).length === 0) { return; } let unified; let isUnifiedProcessor; try { ({unified, isUnifiedProcessor} = await import("@astrojs/markdown-remark")); } catch { throw new Error("`markdown.remarkPlugins`, `markdown.rehypePlugins`, and `markdown.remarkRehype` run on the `unified` processor from `@astrojs/markdown-remark`, which is no longer installed by default now that Sätteri is the default Markdown processor. Install it with:\n npm install @astrojs/markdown-remark"); } const current = md.processor; if (!current || isUnifiedProcessor(current)) { // `createRenderer` reads `.options.*` at render time, so in-place mutation propagates. const target = current ?? (md.processor = unified()); const counts = migratedLegacyPluginCounts.get(target.options) ?? { remark: 0, rehype: 0 }; // Only push entries past what we've already folded; mergeConfig appends, so they sit at the tail. if (remarkPlugins.length > counts.remark) { target.options.remarkPlugins.push(...remarkPlugins.slice(counts.remark)); } if (rehypePlugins.length > counts.rehype) { target.options.rehypePlugins.push(...rehypePlugins.slice(counts.rehype)); } // `remarkRehype` is an object, so Object.assign is idempotent for unchanged keys // and absorbs any new keys integrations add. Object.assign(target.options.remarkRehype, remarkRehype); migratedLegacyPluginCounts.set(target.options, { remark: remarkPlugins.length, rehype: rehypePlugins.length }); if (!didWarnAboutLegacyMarkdownPlugins) { didWarnAboutLegacyMarkdownPlugins = true; console.warn("[astro] `markdown.remarkPlugins`, `markdown.rehypePlugins`, and `markdown.remarkRehype` are deprecated. Pass them to `unified({...})` from `@astrojs/markdown-remark` directly instead."); } } else if (!didWarnAboutProcessorMismatch) { // Third-party processors can't run remark/rehype plugins. And if they can, they should be passed directly to the processor (like it is for unified) instead of the legacy keys, so we warn either way. didWarnAboutProcessorMismatch = true; console.warn(`[astro] \`markdown.remarkPlugins\`/\`rehypePlugins\`/\`remarkRehype\` are set, but your ` + `\`${current.name}\` processor doesn't run them. Move them to \`unified({...})\` from ` + "`@astrojs/markdown-remark` and set `markdown.processor: unified({...})` if you want " + "them to apply."); } } /** * Used twice: * - To validate the user config * - To validate the config after all integrations (that may have updated it) */ export async function validateConfigRefined(updatedConfig) { await coerceLegacyMarkdownPlugins(updatedConfig); warnDeprecatedMarkdownOptions(updatedConfig); return await AstroConfigRefinedSchema.parseAsync(updatedConfig, { error: errorMap }); }