Turbopack: resolve loader source map sources against the compilation root#95727
Open
martijnwalraven wants to merge 2 commits into
Open
Turbopack: resolve loader source map sources against the compilation root#95727martijnwalraven wants to merge 2 commits into
martijnwalraven wants to merge 2 commits into
Conversation
…root A webpack loader emits relative `sources` against the compilation root it is handed (`rootContext`, wired to the transform's cwd), not against the transformed file's own directory. Resolving them origin-relative doubled the path for any loader whose map carries root-relative sources (e.g. next-intl's extractor calling swc with `sourceFileName: path.relative(rootContext, resourcePath)`), and /__nextjs_original-stack-frames then failed those frames with "Cannot find source for asset ..." after a correct trace. resolve_source_map_sources gains an explicit base for relative sources (None keeps the spec default — the map's own location); the webpack-loader transform passes the same project_path it hands the loader runner as cwd/rootContext.
batchedTraceSource awaited project.getSourceForAsset inline, so a failed source read rejected the whole frame in /__nextjs_original-stack-frames — discarding a successful sourcemap trace and downgrading symbolication consumers to minified frames. The mapped frame is still valuable without its code frame: degrade to source = null instead.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What?
Two dev-time symbolication fixes for Turbopack + webpack-style loaders:
sourcesin a source map returned by a webpack loader are now resolved against the compilation root the loader was handed (rootContext), instead of against the transformed file's own directory./__nextjs_original-stack-framesno longer rejects a successfully traced frame when reading the original source content fails — the mapped frame is returned without a code frame instead.Why?
Motivating case: next-intl's message extractor in a pnpm monorepo. A Next 16 app lives at
apps/webwithturbopack.rootset to the workspace root. next-intl (4.13,experimental.extract) registers a webpack-style loader rule under Turbopack (next-intl/extractor/extractionLoader, content conditionuseExtracted|getExtracted). For each matching file the loader runs swc withsourceFileName: path.relative(rootContext, resourcePath)— e.g.src/components/i18n/language-toggle.tsx, relative to therootContextTurbopack handed it (apps/web) — so the returned map'ssourcesare rootContext-relative, the convention webpack itself uses (NormalModulecontextifies loader-returned map sources againstoptions.context).Turbopack then resolves those sources against a different base — the transformed file's own directory — in
resolve_source_map_sources, so the served chunk map carries a doubled path:Every dev-time consumer of that map degrades, but only for the loader-transformed files — sibling files in the same chunk map correctly, which makes it look flaky. A browser exception thrown in
language-toggle.tsxtraces correctly, then/__nextjs_original-stack-framesfails the whole frame withbecause the code-frame read failure rejects a frame whose mapping had already succeeded. Overlay code frames and external dev symbolication consumers (e.g. Sentry's dev symbolication processor, which falls back silently to minified frames on rejection) lose original locations for exactly the files the loader touched.
Turbopack defines both sides of the contract here: it passes the loader runner
cwd= the project path (exposed to loaders asrootContext,turbopack/crates/turbopack-node/js/src/transforms/webpack-loaders.ts), then interprets the returned map against the resource directory (turbopack/crates/turbopack-node/src/transforms/webpack.rs).Generalized reproduction (no next-intl needed):
next dev --turbopackin a workspace whereturbopack.rootis above the app dir, with any loader rule returning a map with rootContext-relativesources; fetch the chunk's.mapand observe the doubled entry, or POST the frame to/__nextjs_original-stack-framesand observe the rejection.How?
resolve_source_map_sourcestakes an explicit base for relative sources;Nonekeeps the source-map-spec default (the map's location, i.e. the origin's directory), used by the inline-map and.map-file callers. The webpack-loader transform passes the sameproject_pathit hands the loader runner.batchedTraceSourcecatchesgetSourceForAssetfailures and returns the traced frame withsource = null.test_resolve_source_map_sourcescovering the loader-shaped case (rootContext-relative source + explicit base).Test plan:
cargo test -p turbopack-core --lib source_map(new case + existing cases green);cargo checkclean onturbopack-nodeandturbopack-ecmascript; rustfmt clean. The defect itself is reproduced live againstv16.3.0-canary.82(these files are identical at the canary tip); the fixed binary has not been exercised against that live setup end to end.