From bc7c164bc34959ab393df89adf961f269f147d09 Mon Sep 17 00:00:00 2001 From: Happydev <81974850+MoustaphaDev@users.noreply.github.com> Date: Fri, 9 Jun 2023 20:21:32 +0000 Subject: [PATCH] Do not silence runtime MDX errors (#7089) * test: add fixture * chore: remove unneeded files in fixture * test: update test mdx filfe * test: add test case * mark component as mdx * add error builder utility * throw error when it comes from an MDX component * chore: update lock file * Add comment to refactor later * apply review * add missing comma --- packages/astro/src/jsx/server.ts | 32 ++++++++++++++- packages/integrations/mdx/src/index.ts | 3 ++ .../src/pages/invalid-content.mdx | 7 ++++ .../mdx/test/invalid-mdx-component.test.js | 40 +++++++++++++++++++ pnpm-lock.yaml | 9 +++++ 5 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 packages/integrations/mdx/test/fixtures/invalid-mdx-component/src/pages/invalid-content.mdx create mode 100644 packages/integrations/mdx/test/invalid-mdx-component.test.js diff --git a/packages/astro/src/jsx/server.ts b/packages/astro/src/jsx/server.ts index a666d20305..6374b1ebde 100644 --- a/packages/astro/src/jsx/server.ts +++ b/packages/astro/src/jsx/server.ts @@ -17,7 +17,20 @@ export async function check( try { const result = await Component({ ...props, ...slots, children }); return result[AstroJSX]; - } catch (e) {} + } catch (e) { + const error = e as Error; + // if the exception is from an mdx component + // throw an error + if (Component[Symbol.for('mdx-component')]) { + throw createFormattedError({ + message: error.message, + title: error.name, + hint: `This issue often occurs when your MDX component encounters runtime errors.`, + name: error.name, + stack: error.stack, + }); + } + } return false; } @@ -38,6 +51,23 @@ export async function renderToStaticMarkup( return { html }; } +type FormatErrorOptions = { + message: string; + name: string; + stack?: string; + hint: string; + title: string; +}; +// TODO: Remove this function and use `AstroError` when we refactor it to be usable without error codes +function createFormattedError({ message, name, stack, hint }: FormatErrorOptions) { + const error = new Error(message); + error.name = name; + error.stack = stack; + // @ts-expect-error - hint is not part of the Error interface but it will be picked up by the error overlay + error.hint = hint; + return error; +} + export default { check, renderToStaticMarkup, diff --git a/packages/integrations/mdx/src/index.ts b/packages/integrations/mdx/src/index.ts index 18c9acecae..1d03202136 100644 --- a/packages/integrations/mdx/src/index.ts +++ b/packages/integrations/mdx/src/index.ts @@ -165,6 +165,9 @@ export default function mdx(partialMdxOptions: Partial = {}): AstroI export default Content;`; } + // mark the component as an MDX component + code += `\nContent[Symbol.for('mdx-component')] = true`; + // Ensures styles and scripts are injected into a `` // When a layout is not applied code += `\nContent[Symbol.for('astro.needsHeadRendering')] = !Boolean(frontmatter.layout);`; diff --git a/packages/integrations/mdx/test/fixtures/invalid-mdx-component/src/pages/invalid-content.mdx b/packages/integrations/mdx/test/fixtures/invalid-mdx-component/src/pages/invalid-content.mdx new file mode 100644 index 0000000000..9ebaa695bc --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/invalid-mdx-component/src/pages/invalid-content.mdx @@ -0,0 +1,7 @@ +--- +title: Hello, World +--- + +# {A.VALID.JAVASCRIPT.EXPRESSION.THAT.RESULTS.IN.A.RUNTIME.ERROR} + +Invalid content in the frontmatter diff --git a/packages/integrations/mdx/test/invalid-mdx-component.test.js b/packages/integrations/mdx/test/invalid-mdx-component.test.js new file mode 100644 index 0000000000..a07401c607 --- /dev/null +++ b/packages/integrations/mdx/test/invalid-mdx-component.test.js @@ -0,0 +1,40 @@ +import { expect } from "chai"; +import { loadFixture } from "../../../astro/test/test-utils.js"; +import mdx from "../dist/index.js"; + +const FIXTURE_ROOT = new URL( + "./fixtures/invalid-mdx-component/", + import.meta.url, +); + +describe("MDX component with runtime error", () => { + let fixture; + + before(async () => { + fixture = await loadFixture({ + root: FIXTURE_ROOT, + integrations: [mdx()], + }); + }); + + describe("build", () => { + /** @type {Error | null} */ + let error; + + before(async () => { + error = null; + try { + await fixture.build(); + } catch (e) { + error = e; + } + }); + + it("Throws the right error", async () => { + expect(error).to.exist; + expect(error?.hint).to.match( + /This issue often occurs when your MDX component encounters runtime errors/, + ); + }); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f4d222da1b..e0c2e5d89e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4231,6 +4231,15 @@ importers: specifier: 0.2.3 version: 0.2.3 + packages/integrations/mdx/test/fixtures/invalid-mdx-component: + dependencies: + '@astrojs/mdx': + specifier: workspace:* + version: link:../../.. + astro: + specifier: workspace:* + version: link:../../../../../astro + packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection: dependencies: '@astrojs/mdx':