diff --git a/.changeset/rare-buckets-kneel.md b/.changeset/rare-buckets-kneel.md new file mode 100644 index 000000000..74a05f545 --- /dev/null +++ b/.changeset/rare-buckets-kneel.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Improve the way YAML errors are surfaced diff --git a/packages/astro/src/core/errors.ts b/packages/astro/src/core/errors.ts index e295d9191..fa80414e1 100644 --- a/packages/astro/src/core/errors.ts +++ b/packages/astro/src/core/errors.ts @@ -69,6 +69,20 @@ export function collectErrorMetadata(e: any): ErrorWithMetadata { (e as any).stack = eol.lf((e as any).stack); } + + if (e.name === 'YAMLException') { + const err = e as SSRError; + err.loc = { file: (e as any).id, line: (e as any).mark.line, column: (e as any).mark.column } + err.message = (e as any).reason; + + if (!err.frame) { + try { + const fileContents = fs.readFileSync(err.loc.file!, 'utf8'); + err.frame = codeFrame(fileContents, err.loc); + } catch {} + } + } + // Astro error (thrown by esbuild so it needs to be formatted for Vite) if (Array.isArray((e as any).errors)) { const { location, pluginName, text } = (e as BuildResult).errors[0]; diff --git a/packages/astro/src/vite-plugin-markdown/index.ts b/packages/astro/src/vite-plugin-markdown/index.ts index 1a0b9f228..90b25625f 100644 --- a/packages/astro/src/vite-plugin-markdown/index.ts +++ b/packages/astro/src/vite-plugin-markdown/index.ts @@ -10,6 +10,7 @@ import type { AstroConfig } from '../@types/astro'; import { pagesVirtualModuleId } from '../core/app/index.js'; import { prependForwardSlash } from '../core/path.js'; import { resolvePages, viteID } from '../core/util.js'; +import { collectErrorMetadata } from '../core/errors.js'; import { PAGE_SSR_SCRIPT_ID } from '../vite-plugin-scripts/index.js'; import { getFileInfo } from '../vite-plugin-utils/index.js'; @@ -20,6 +21,15 @@ interface AstroPluginOptions { const MARKDOWN_IMPORT_FLAG = '?mdImport'; const MARKDOWN_CONTENT_FLAG = '?content'; +function safeMatter(source: string, id: string) { + try { + return matter(source); + } catch (e) { + (e as any).id = id; + throw collectErrorMetadata(e) + } +} + // TODO: Clean up some of the shared logic between this Markdown plugin and the Astro plugin. // Both end up connecting a `load()` hook to the Astro compiler, and share some copy-paste // logic in how that is done. @@ -82,7 +92,7 @@ export default function markdown({ config }: AstroPluginOptions): Plugin { const { fileId, fileUrl } = getFileInfo(id, config); const source = await fs.promises.readFile(fileId, 'utf8'); - const { data: frontmatter, content: rawContent } = matter(source); + const { data: frontmatter, content: rawContent } = safeMatter(source, fileId); return { code: ` // Static @@ -127,7 +137,7 @@ export default function markdown({ config }: AstroPluginOptions): Plugin { const hasInjectedScript = isPage && config._ctx.scripts.some((s) => s.stage === 'page-ssr'); // Extract special frontmatter keys - let { data: frontmatter, content: markdownContent } = matter(source); + let { data: frontmatter, content: markdownContent } = safeMatter(source, filename); // Turn HTML comments into JS comments while preventing nested `*/` sequences // from ending the JS comment by injecting a zero-width space