feat: better MDX error handling in CLI and browser#157
Conversation
Fail compilation with file, line, and column when content uses a component that is not a standard HTML/SVG element, a registered Chronicle component, or imported/defined in the MDX file. Tag lists come from html-tag-names/svg-tag-names; custom elements and member expressions on registered components are allowed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the bare 500/JSON response with a styled dev-only error page naming the failing file, position, and a highlighted code frame. Handles both compile failures (unclosed tags, unknown components) via the Nitro error handler — where they land because the eager frontmatter glob compiles all content at startup — and runtime MDX errors via the SSR catch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Catch build failures, print a formatted report (relative path, line:column, message, code frame) for MDX-related errors, and exit non-zero; unrelated errors are rethrown unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Track the failure message in page context and show a dedicated 'Failed to render page' state with the error detail, instead of the misleading 'Page not found' empty state. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The error page polls its own URL every second and reloads once the page compiles again, or when the error changes (e.g. the next broken file). No manual refresh or server restart needed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
Next review available in: 48 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds MDX component/tag validation via a new remark plugin, MDX error parsing utilities with code-frame generation, CLI build error reporting, dev-server and SSR MDX error responses with auto-reload, and client-side page context/UI changes to render non-404 errors via a new RenderError component. ChangesMDX validation and error reporting
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant DocsPage
participant PageProvider as page-context
participant EntryServer
participant DevErrorPage as dev-error-page
participant RenderError
PageProvider->>PageProvider: loadDocsPage() fails, catch(err)
PageProvider->>PageProvider: derive errorStatus, set errorMessage
DocsPage->>PageProvider: usePageContext() reads errorStatus/errorMessage
DocsPage->>RenderError: render RenderError(message) if errorStatus != NOT_FOUND
EntryServer->>DevErrorPage: renderMdxErrorResponse(err) in DEV
DevErrorPage-->>EntryServer: HTML error Response or null
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
@base-ui/react@1.6.0 renamed the OTPFieldPreview export to OTPField, but @raystack/apsara (rc.7 and 1.0.1) still imports OTPFieldPreview under a ^1.4.1 range. Fresh installs (Docker CI runner stage, new dev machines) resolved 1.6.0 and failed the build with MISSING_EXPORT. A direct >=1.4.1 <1.6.0 dependency makes resolvers dedupe onto 1.5.0. Remove once apsara ships a base-ui 1.6-compatible release. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/chronicle/src/lib/remark-validate-mdx.test.ts (1)
19-41: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider adding test coverage for local declarations beyond imports.
collectLocalNameshandlesExportNamedDeclaration, top-levelVariableDeclaration,FunctionDeclaration, andClassDeclaration, but onlyImportDeclarationis exercised viaimportNode. Adding a helper for local declarations (e.g.,export const Foo = ...orfunction Foo() {}) would verify those paths.🧪 Suggested test helpers
+function varDeclNode(name: string) { + return { + type: 'mdxjsEsm', + value: `const ${name} = () => null`, + data: { + estree: { + type: 'Program', + body: [ + { + type: 'VariableDeclaration', + declarations: [{ id: { name } }], + }, + ], + }, + }, + } as unknown as Root['children'][number]; +} + +function exportNamedNode(name: string) { + return { + type: 'mdxjsEsm', + value: `export const ${name} = () => null`, + data: { + estree: { + type: 'Program', + body: [ + { + type: 'ExportNamedDeclaration', + declaration: { + type: 'VariableDeclaration', + declarations: [{ id: { name } }], + }, + }, + ], + }, + }, + } as unknown as Root['children'][number]; +}Then add tests:
+ test('allows locally declared variables', () => { + expect(() => run([varDeclNode('Widget'), jsxNode('Widget')])).not.toThrow(); + }); + + test('allows exported named declarations', () => { + expect(() => run([exportNamedNode('Gadget'), jsxNode('Gadget')])).not.toThrow(); + });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/chronicle/src/lib/remark-validate-mdx.test.ts` around lines 19 - 41, The test suite currently covers only import-based local names via importNode, but collectLocalNames also handles ExportNamedDeclaration, top-level VariableDeclaration, FunctionDeclaration, and ClassDeclaration. Add a small helper in remark-validate-mdx.test.ts for building mdxjsEsm nodes with local declarations, then add cases that exercise those declaration paths through remarkValidateMdx() and run(), so the helper verifies collectLocalNames across imports, exports, variables, functions, and classes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/chronicle/src/lib/remark-validate-mdx.test.ts`:
- Around line 19-41: The test suite currently covers only import-based local
names via importNode, but collectLocalNames also handles ExportNamedDeclaration,
top-level VariableDeclaration, FunctionDeclaration, and ClassDeclaration. Add a
small helper in remark-validate-mdx.test.ts for building mdxjsEsm nodes with
local declarations, then add cases that exercise those declaration paths through
remarkValidateMdx() and run(), so the helper verifies collectLocalNames across
imports, exports, variables, functions, and classes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ba54c8ad-2463-4c17-9b17-c97484ef65f6
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (17)
packages/chronicle/package.jsonpackages/chronicle/src/cli/commands/build.tspackages/chronicle/src/cli/utils/mdx-error-report.tspackages/chronicle/src/components/mdx/index.tsxpackages/chronicle/src/lib/mdx-component-names.tspackages/chronicle/src/lib/mdx-error.test.tspackages/chronicle/src/lib/mdx-error.tspackages/chronicle/src/lib/page-context.tsxpackages/chronicle/src/lib/remark-validate-mdx.test.tspackages/chronicle/src/lib/remark-validate-mdx.tspackages/chronicle/src/pages/DocsPage.tsxpackages/chronicle/src/pages/NotFound.module.csspackages/chronicle/src/pages/RenderError.tsxpackages/chronicle/src/server/dev-error-page.tspackages/chronicle/src/server/entry-server.tsxpackages/chronicle/src/server/error.tspackages/chronicle/src/server/vite-config.ts
Summary
Content errors (unknown components, unclosed/mismatched tags) previously surfaced as a bare 500 JSON response in the browser and a raw stack trace from
chronicle build. This PR makes both name the exact file, position, and component.remark-validate-mdx): every JSX element in content is checked against standard HTML/SVG elements (html-tag-names/svg-tag-names), registered Chronicle components, and components imported/defined in the MDX file itself. Custom elements (<my-widget>) and member expressions (<Tabs.Tab>) are allowed. Unknown names fail compilation with file, line, and column.file:line:col, and a highlighted code frame. Hooked into the Nitro error handler (where content compile errors land, since the eager frontmatter glob compiles all content at startup) and the SSR catch. Polls every second and auto-reloads once the content is fixed — no server restart or manual refresh needed.chronicle build: fails with exit 1 and a formatted report (relative path, line:column, message, code frame) for MDX errors; unrelated errors are rethrown unchanged.Screenshots of behavior
Testing
remark-validate-mdx,mdx-errorparser/code frame); full suite 199 passexamples/basic: unknown component and unclosed tag both render the dev error page with correct file/position; healthy pages unaffected; fixing the file recovers without restart;chronicle buildexits 1 with formatted output🤖 Generated with Claude Code