Skip to content

refactor(onerror): inline error page template as string constant#5868

Open
killagu wants to merge 1 commit intoeggjs:nextfrom
killagu:split/02-onerror-inline-template
Open

refactor(onerror): inline error page template as string constant#5868
killagu wants to merge 1 commit intoeggjs:nextfrom
killagu:split/02-onerror-inline-template

Conversation

@killagu
Copy link
Copy Markdown
Contributor

@killagu killagu commented Apr 14, 2026

Summary

Inlines the 1336-line error page HTML template into `plugins/onerror/src/lib/onerror_page.ts` as a string constant. Removes the runtime `readFileSync` + `import.meta.dirname` lookup, sets the `templatePath` config default to an empty string, and updates `app.ts` to import the inlined constant.

Why

This is batch 1, part of a 19-PR split of #5863 (the egg-bundler PR). #5863 is kept open as a tracking reference. This PR is independent of the other batch-1 PRs.

Turbopack (and any static bundler) cannot follow `import.meta.dirname + readFileSync` to a template file, so the file would be missing in a bundled deployment. Inlining the HTML as a string constant makes the plugin statically bundleable.

This is a pure refactor — runtime behavior is identical, the user-supplied `templatePath` config still overrides the default.

Test plan

  • `pnpm --filter=@eggjs/onerror test` — 36/36 passed
  • typecheck clean

Stack context

Other batch-1 PRs (independent, can land in any order):

  • `feat(utils): add setBundleModuleLoader runtime hook`
  • `refactor(development): inline loader trace template as string constant`
  • `refactor(watcher): use direct class imports for event sources`

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a built-in error page template featuring stack frame visualization with filtering options, inline code preview, and syntax highlighting.
    • Error handling now defaults to a built-in template; custom templates remain configurable.
  • Chores

    • Updated package subpath exports.

Inline the mustache error page template as a string constant in
src/lib/onerror_page.ts so plugins can be statically bundled by
turbopack. Previously the template was read from disk via
`import.meta.dirname + readFileSync`, which breaks when modules are
embedded in a single bundled chunk.

`templatePath` default is now `''`; if set, the custom template file
is still loaded via fs, otherwise the inlined constant is used.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 14, 2026 03:59
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 14, 2026

📝 Walkthrough

Walkthrough

The onerror plugin is refactored to support optional custom error templates with a built-in fallback. A new exported constant containing the full HTML error page template is introduced, the default configuration changed to use an empty string instead of a hardcoded file path, and the app initialization updated to conditionally load custom templates or fall back to the built-in template. New subpath exports added to package.json for the template module.

Changes

Cohort / File(s) Summary
Package Configuration
plugins/onerror/package.json
Added subpath exports ./lib/onerror_page mapping to source TypeScript and compiled JavaScript files, enabling direct imports of the template module.
Configuration Updates
plugins/onerror/src/config/config.default.ts
Removed hardcoded file-system template path, changed default templatePath to empty string '', updated documentation to clarify that empty value triggers built-in template fallback.
Application Logic & Template
plugins/onerror/src/app.ts, plugins/onerror/src/lib/onerror_page.ts
Made template loading conditional in app initialization; added new exported constant ONERROR_PAGE_TEMPLATE containing full inlined HTML error page with embedded CSS, JavaScript, Prism.js syntax highlighting, stack frame filtering, and code preview functionality.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A template bundled in the core,
No disk reads that we need anymore!
When custom paths are none at hand,
Our fallback template takes its stand.
Error pages, pretty and bright,
Built-in beauty, oh what a sight! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: inlining the error page template as a string constant, which is the core refactor across all modified files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request inlines the error page template into a new TypeScript file, onerror_page.ts, and updates the onerror plugin to use this template by default when templatePath is not configured. This change simplifies the plugin by removing the need to read an external HTML file from the file system in the default case. Feedback was provided to correct minor string formatting issues within the inlined JavaScript template, specifically regarding line number alignment and the display of method locations.

var u = +o[0],
m = +o[1] || u,
h = document.createElement('div');
((h.textContent = Array(m - u + 2).join(' \\n')),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There is an extra space before the newline escape sequence in the join call. This will cause line numbers to be joined with a space and a newline (e.g., 1\n 2), which likely breaks the alignment in the error page UI. It should be join('\\n') to correctly join lines with only a newline character.

Suggested change
((h.textContent = Array(m - u + 2).join(' \\n')),
((h.textContent = Array(m - u + 2).join('\\n')),

$('#code-drop').setAttribute('class', 'language-' + $language);
$('#code-drop').innerHTML = $context;
$('#frame-file').innerHTML = $file;
$('#frame-method').innerHTML = $method + '' + $lineColumn;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The method name and line/column information are joined with an empty string, which will cause them to appear concatenated without a separator (e.g., myFunction10:5). Adding a space would improve the readability of the stack trace view.

Suggested change
$('#frame-method').innerHTML = $method + '' + $lineColumn;
$('#frame-method').innerHTML = $method + ' ' + $lineColumn;

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
plugins/onerror/src/lib/onerror_page.ts (1)

710-722: Consider limiting sensitive information exposure in error pages.

The error page displays appInfo.baseDir and appInfo.config in non-production environments. While this is useful for debugging, be mindful that this could expose sensitive configuration values if not properly filtered upstream.

Based on learnings: "Never expose sensitive configuration values in logs or error messages" - the filtering should be handled in ErrorView to ensure sensitive config values are redacted before rendering.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/onerror/src/lib/onerror_page.ts` around lines 710 - 722, The error
page currently renders raw appInfo.baseDir and appInfo.config which can leak
secrets; update ErrorView to sanitize appInfo before rendering by implementing a
redaction step (e.g., sanitizeAppInfo or redactConfig) that removes or masks
sensitive keys/values from appInfo.config and optionally obfuscates baseDir,
then ensure the template rendering in onerror_page.ts uses the sanitizedAppInfo
fields (sanitizedAppInfo.config and sanitizedAppInfo.baseDir) instead of the raw
appInfo; keep the redaction centralized in ErrorView so all error rendering
paths use the sanitized object.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@plugins/onerror/src/lib/onerror_page.ts`:
- Around line 710-722: The error page currently renders raw appInfo.baseDir and
appInfo.config which can leak secrets; update ErrorView to sanitize appInfo
before rendering by implementing a redaction step (e.g., sanitizeAppInfo or
redactConfig) that removes or masks sensitive keys/values from appInfo.config
and optionally obfuscates baseDir, then ensure the template rendering in
onerror_page.ts uses the sanitizedAppInfo fields (sanitizedAppInfo.config and
sanitizedAppInfo.baseDir) instead of the raw appInfo; keep the redaction
centralized in ErrorView so all error rendering paths use the sanitized object.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e249aee1-450d-499f-86e3-1f74048ca748

📥 Commits

Reviewing files that changed from the base of the PR and between 490f849 and f99e0e7.

📒 Files selected for processing (4)
  • plugins/onerror/package.json
  • plugins/onerror/src/app.ts
  • plugins/onerror/src/config/config.default.ts
  • plugins/onerror/src/lib/onerror_page.ts

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Refactors the onerror plugin to inline the error page HTML template as a TypeScript string constant so it can be statically bundled (e.g., by Turbopack), removing the runtime import.meta.dirname + readFileSync dependency.

Changes:

  • Adds an inlined ONERROR_PAGE_TEMPLATE constant containing the full error page template.
  • Changes the default templatePath config to '' (use built-in template when empty).
  • Updates the boot logic to use the inlined template unless a custom templatePath is provided, and exports the new module entry.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
plugins/onerror/src/lib/onerror_page.ts Adds the inlined HTML template constant used as the default error page.
plugins/onerror/src/config/config.default.ts Removes default filesystem template path; documents and defaults templatePath to empty string.
plugins/onerror/src/app.ts Switches to inlined template when templatePath is not set; retains file override behavior.
plugins/onerror/package.json Exposes the new lib/onerror_page entry for source and dist builds.

}
}
function showFrameContext(frame) {
$frameContext = frame.querySelector('.frame-context');
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$frameContext is assigned without var/let/const, which creates an implicit global in non-strict mode (and would throw in strict mode). Declare it as a block-scoped variable (e.g., const $frameContext = ...) to avoid global namespace pollution and potential collisions.

Suggested change
$frameContext = frame.querySelector('.frame-context');
const $frameContext = frame.querySelector('.frame-context');

Copilot uses AI. Check for mistakes.
Comment on lines +1282 to +1288
var activeFrame = $('.frame-row.active');
if (activeFrame.classList.contains('native-frame')) {
activeFrame.classList.remove('active');
var firstFrame = $$('.frame-row')[0];
firstFrame.classList.add('active');
showFrameContext(firstFrame);
}
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

activeFrame can be null if no element matches .frame-row.active (e.g., if frames is empty or no item is marked active), which will cause activeFrame.classList... to throw and break the error page. Add a guard for activeFrame (and also for firstFrame being undefined) before accessing properties/calling showFrameContext.

Copilot uses AI. Check for mistakes.
Comment on lines +1329 to +1331
displayFirstView();
showFrameContext($('.frame-row.active'));
})();
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$('.frame-row.active') may return null, which will cause showFrameContext to throw when it dereferences frame. Consider selecting a safe fallback (e.g., first .frame-row) or short-circuiting when no frame exists, to avoid the error page failing to render in edge cases.

Copilot uses AI. Check for mistakes.
// logging error
const config = this.app.config.onerror;
const viewTemplate = fs.readFileSync(config.templatePath, 'utf8');
const viewTemplate = config.templatePath ? fs.readFileSync(config.templatePath, 'utf8') : ONERROR_PAGE_TEMPLATE;
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because ONERROR_PAGE_TEMPLATE is imported at module load time, the runtime must parse/allocate the large inlined template even when users provide templatePath (and the built-in template is never used). If startup cost matters, consider deferring loading the default template (e.g., via a dynamic import or a getter function that’s only invoked when templatePath is falsy). Tradeoff: dynamic import adds a small async boundary/complexity but avoids paying the parse cost in override scenarios.

Copilot uses AI. Check for mistakes.
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 14, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.00%. Comparing base (490f849) to head (f99e0e7).

Additional details and impacted files
@@           Coverage Diff           @@
##             next    #5868   +/-   ##
=======================================
  Coverage   86.00%   86.00%           
=======================================
  Files         667      668    +1     
  Lines       18945    18946    +1     
  Branches     3652     3653    +1     
=======================================
+ Hits        16294    16295    +1     
  Misses       2297     2297           
  Partials      354      354           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

onerror: {
errorPageUrl: '',
appErrorFilter: undefined,
templatePath: path.join(import.meta.dirname, '../lib/onerror_page.mustache.html'),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should delete onerror_page.mustache.html file too

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants