feat: add source map support for production stack trace rewriting#284
feat: add source map support for production stack trace rewriting#284aheissenberger wants to merge 7 commits intolazarv:mainfrom
Conversation
There was a problem hiding this comment.
Pull Request Overview
This pull request adds source map support for production error stack traces by dynamically loading and installing the source-map-support package when .map files are detected at server startup. This allows production errors to display original source locations instead of compiled bundle locations.
Key Changes
- Added
SOURCEMAP_ENABLEDruntime flag to track source map availability - Implemented source map detection by checking for
.mapfiles in the build output directory - Dynamically imports and installs
source-map-supportonly when source maps are present
Reviewed Changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Added source-map-support@0.5.21 dependency and updated peer dependency resolution for various packages |
| packages/react-server/server/symbols.mjs | Added new SOURCEMAP_ENABLED symbol for runtime flag tracking |
| packages/react-server/package.json | Added source-map-support dependency specification |
| packages/react-server/lib/start/ssr-handler.mjs | Implemented source map detection logic and dynamic loading of source-map-support at server startup |
| packages/react-server/lib/start/action.mjs | Added default initialization of SOURCEMAP_ENABLED flag in worker function |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Hi @aheissenberger! This is so nice to have finally, and sourcemap handling has been a missing feature for more than 2 years now! I totally forgot to add this to the production server. I made some suggested changes in review/sourcemap-error. The most prominent change I would suggest is to use the entry module code and check for the Also, I would rename |
|
When switching to source map detection by checking for the url, the hidden source maps will break. Have a look at my latest changes which require less code changes and no runtime detection:
I tested the rsc component by adding a throw error to |
|
maybe using the cli option
|
| import { ServerFunctionNotFoundError } from "./action-state.mjs"; | ||
| import { cwd } from "../lib/sys.mjs"; | ||
|
|
||
| /** |
There was a problem hiding this comment.
This wrapper could be moved to https://github.com/lazarv/react-server/blob/main/packages/react-server/lib/start/create-logger.mjs, so that the logger would use this everywhere by default. Also, the wrapper should only be enabled when using source maps.
|
|
||
| // Write build metadata with sourcemap option | ||
| await writeFile( | ||
| join(cwd, options.outDir, "server/build-meta.json"), |
There was a problem hiding this comment.
As all other manifests have the server/<type>-manifest.json name, I would suggest to have this also named server/build-manifest.json.
| const moduleCacheStorage = new ContextManager(); | ||
| await module_loader_init$(moduleLoader, moduleCacheStorage); | ||
|
|
||
| if (buildMetadata.sourcemap && buildMetadata.sourcemap !== false) { |
There was a problem hiding this comment.
As this is now related to manifests, I would move this into https://github.com/lazarv/react-server/blob/main/packages/react-server/lib/start/manifest.mjs#L52-L56 and also store it in the context.
…v#306) ## Summary Adds source map support for production error stack traces, enabling developers to see original source file locations in server-side errors instead of bundled code references. Closes lazarv#284 ## Features ### Source map modes All Vite sourcemap modes are supported via `--sourcemap`: | Mode | Server | Client | Description | |------|--------|--------|-------------| | `true` | `.map` files | `.map` files | Full source maps for both bundles | | `inline` | Inline | Inline | Embedded in output files | | `hidden` | `.map` files (no URL comment) | `.map` files (no URL comment) | Maps without `sourceMappingURL` | | `server` | `.map` files | None | Server-only, keeps client source private | | `server-inline` | Inline | None | Server-only inline, ideal for edge deployments | ### Runtime stack trace rewriting - Uses `source-map-support` package to automatically rewrite production error stack traces - Skips installation on edge runtimes (Vercel Edge, Cloudflare Workers, Netlify Edge) where the platform handles source maps natively - Skips installation when `NODE_OPTIONS='--enable-source-maps'` is already set to avoid conflicts - Mode-specific configuration: `hookRequire` for inline, custom `retrieveSourceMap` for hidden ### Configuration Via CLI: ```sh pnpm react-server build --sourcemap # true (file-based) pnpm react-server build --sourcemap=server # server bundles only pnpm react-server build --sourcemap=server-inline # server inline only ``` Via config file (`react-server.config.mjs`): ```js export default { sourcemap: true, // or "inline", "hidden", "server", "server-inline" }; ``` ### Adapter support - **Vercel** — Adds `NODE_OPTIONS: "--enable-source-maps"` to `.vc-config.json` environment - **Cloudflare** — Enables `upload_source_maps: true` in generated `wrangler.toml` - **Netlify** — Copies `.map` files alongside edge/serverless functions - **Core adapter** — Conditionally includes `server/**/*.map` in server file globs ## Implementation details ### Build-time - Emits `build-manifest.mjs` as an ES module (`export default { sourcemap: ... }`) for edge compatibility - Registers virtual module `.react-server/server/build-manifest` in all 3 resolution paths (RSC loader, SSR loader, edge bundler) - Server-only modes normalize the value for server/edge builds while passing `false` to client builds ### Runtime - `manifest.mjs` loads the build manifest and sets the `SOURCEMAP_SUPPORT` runtime symbol - `ssr-handler.mjs` reads the symbol and installs `source-map-support` with appropriate options - RSC `onError` handlers log errors with `logger?.error(e)` in production for visibility ## Files changed ### Core - `packages/react-server/server/symbols.mjs` — New `SOURCEMAP_SUPPORT` symbol - `packages/react-server/lib/build/action.mjs` — Build manifest emission, config file support - `packages/react-server/lib/build/server.mjs` — Server sourcemap normalization - `packages/react-server/lib/build/client.mjs` — Client sourcemap suppression for server-only modes - `packages/react-server/lib/build/edge.mjs` — Edge sourcemap normalization - `packages/react-server/lib/start/manifest.mjs` — Build manifest loading - `packages/react-server/lib/start/ssr-handler.mjs` — Source map support installation - `packages/react-server/lib/loader/node-loader.mjs` — Virtual module resolution - `packages/react-server/lib/loader/node-loader.react-server.mjs` — Virtual module resolution - `packages/react-server/server/render-rsc.jsx` — Production error logging - `packages/react-server/bin/commands/build.mjs` — CLI help text - `packages/react-server/package.json` — `source-map-support` dependency ### Adapters - `packages/react-server/adapters/core.mjs` — Conditional `.map` file inclusion - `packages/react-server/adapters/vercel/index.mjs` — `NODE_OPTIONS` in `.vc-config.json` - `packages/react-server/adapters/cloudflare/index.mjs` — `upload_source_maps` in wrangler config - `packages/react-server/adapters/netlify/index.mjs` — Edge function `.map` file copy ### Docs - `docs/src/pages/en/(pages)/framework/cli.mdx` — Restructured with per-option headings + sourcemap docs - `docs/src/pages/ja/(pages)/framework/cli.mdx` — Same restructuring in Japanese ### Tests - `test/__test__/sourcemap.spec.mjs` — Build output tests (manifest content, `.map` file presence, server-inline mode) - `test/fixtures/sourcemap.jsx` — Test fixture component ### VS Code - `.vscode/launch.json` — Debug configuration for production + sourcemap - `.vscode/tasks.json` — Build task with `--sourcemap`
Source Map Support for Production Error Stack Traces
Overview
This implementation adds support for converting production error stack traces back to their original source locations using source maps. When you build with the
--sourcemapflag, error messages in production will show the original file locations and line numbers instead of the compiled/bundled locations.How It Works
1. Source Map Detection
When the server starts in production mode, it checks if source map files (
.map) exist alongside the built files in the output directory (default:.react-server/server/). If found, theSOURCEMAP_ENABLEDflag is set in the runtime context.File:
lib/start/ssr-handler.mjs.mapfiles during initializationSOURCEMAP_ENABLEDruntime flag2. Stack Trace Conversion
The
source-map-supportpackage is dynamically imported and installed at server startup when source maps are detected. It automatically hooks into Node.js'sError.prepareStackTraceto convert stack traces:Package:
source-map-support(dynamically loaded).mapfiles are detected.mapfiles from the file system3. Error Logging Integration
Once
source-map-supportis installed, all errors throughout the application automatically have their stack traces converted. No manual conversion is needed at individual error points.Files Modified:
lib/start/ssr-handler.mjs- Installs source-map-support at startupUsage
Building with Source Maps
Starting the Production Server
The server will automatically detect if source maps are available and use them for error reporting.
Example
Without Source Maps
With Source Maps
Implementation Details
Modified Files
server/symbols.mjs
SOURCEMAP_ENABLEDsymbol for runtime flaglib/start/ssr-handler.mjs
.mapfiles)source-map-supportonly when source maps are detectedSOURCEMAP_ENABLEDruntime flagsource-map-supportwhen source maps are availableDependencies Added
source-map-support- Automatically converts stack traces using source maps (dynamically loaded only when needed)Performance Considerations
source-map-supportis only imported when source maps are detected, reducing memory footprint in development and when source maps aren't availablesource-map-supportcaches parsed source maps in memory after first loadConditional Execution
The source map conversion only happens when:
--sourcemapflag.mapfiles exist in the output directoryNotes
--sourcemap hiddenoption generates source maps but doesn't reference them in the code (useful for error tracking without exposing sources)