fix: symbolication and open-in-editor for Module Federation stack frames#1407
Draft
dannyhw wants to merge 7 commits into
Draft
fix: symbolication and open-in-editor for Module Federation stack frames#1407dannyhw wants to merge 7 commits into
dannyhw wants to merge 7 commits into
Conversation
Boots the tester-federation-v2 host and mini-app dev servers and covers stack-frame symbolication plus /open-stack-frame end to end. The mini-app config mirrors a real-world MFv2 setup where remote chunks are named [name].<remote>.chunk.bundle and served by a separate dev server. Currently-failing cases document known bugs: - a host-only stack on an MFv2 host fails with 'Invalid URL' because the MF runtime emits a corrupt webpack:// source name into the host map - a frame from a federated remote chunk fails the whole /symbolicate request with 500 because the host compiler has no map for it - one foreign frame poisons symbolication of host-owned frames - node_modules frames symbolicate to URL-encoded parent-escape tokens ([projectRoot%5E2]/...) which /open-stack-frame cannot resolve, so the editor silently never opens Passing cases pin the workflows that must not regress: symbolication of frames the server's own compiler built, and open-stack-frame for plain [projectRoot] paths.
- A failure to load one frame's source map (e.g. a Module Federation remote chunk served by another dev server) no longer fails the whole /symbolicate request with a 500 — the failed frame passes through unchanged and the failure is logged at warn with the bundle URL. - The response stack is now always 1:1 with the request, mirroring Metro; LogBox rejects symbolication results with a different number of frames, so dropped frames broke client-side symbolication. - Source names that are not URL-parseable are sanitized before constructing SourceMapConsumer. Module Federation v2 emits a corrupt webpack:// source name (raw runtime code) into host bundle maps, which previously threw 'Invalid URL' and killed symbolication of every stack on MFv2 hosts, including host-only stacks. - Frames whose position failed to resolve can no longer anchor the code frame, which previously rendered a garbage snippet from the raw minified bundle. - Frames are collapsed by resolved source path (mirroring React Native's default Metro config) and by the source map ignoreList / x_google_ignoreList field. - Generated column 0 is a valid position and now symbolicates instead of being treated as missing. - Editor launch failures in /open-stack-frame are logged with an actionable hint (set REACT_EDITOR) instead of being silently swallowed; unresolved file paths are logged too, since launch-editor silently ignores files that do not exist.
…ncoded project-root tokens
- getSourceMap delegates (rspack and webpack start commands) now fall
back to the source map a bundle declares for itself via its
//# sourceMappingURL= comment, resolved relative to the bundle URL,
when the local compiler has no map for it. This makes stacks from
Module Federation remote containers/chunks served by other dev
servers symbolicate on the host. Responses are validated as source
maps before use and lookups (including misses) are memoized with a
short TTL. Only the declared reference is trusted — no filename
guessing.
- coerceToHttpUrl handles scheme-less frame URLs (localhost:8081/...,
10.0.2.2:8081/...) — note new URL('localhost:8081/x') parses with
'localhost:' as the scheme, so coercion cannot rely on parse errors.
- resolveProjectPath decodes URL-encoded parent-escape tokens:
bundlers percent-encode ^ in source map paths, so /symbolicate
returns [projectRoot%5E2]/... which previously failed to resolve,
making open-stack-frame a silent no-op for node_modules frames.
- Fail fast with a clear error when the test ports are already in use: a stale dev server from an aborted run answers requests with the wrong environment and produces unexplainable failures. - Kill the detached server process groups on process exit, since afterAll does not run when the test runner is killed mid-run. - Give the fake-editor poll a generous timeout for fully parallel test runs on loaded machines.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 0f33990 The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
… E2E CI load - Reject open-stack-frame bodies where file is not a string, contains NUL bytes, or lineNumber is not numeric, and declare the plugin's @fastify/sensible dependency. - Run the E2E dev servers with --platform ios: the suite exercises a single platform and compiling both doubled the load on shared CI runners, pushing an unrelated tester-app bundle test past its timeout. - Stop test servers with SIGKILL — a SIGTERM caught mid-compilation left an orphan holding the port for later runs.
Repeated identical requests (double taps, a client stuck retrying) each spawned an editor process; launches of the same frame within 1s are now no-ops.
Collaborator
Author
|
CodeQL findings on the new
|
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.
Summary
Fixes stack-frame symbolication and open-in-editor for Module Federation setups, prompted by a customer report ("console shows the location in the bundle map instead of the actual source file") on an MFv2 shell + multiple MFE dev servers.
Tests first: the E2E suite in
tests/integration/e2e/symbolication.test.tsboots the real tester-federation-v2 host + mini-app dev servers and was committed failing (2 pass / 4 fail onmain); the fix commits turn it 6/6 green.Root causes fixed
SourceMapConsumer. The MF runtime emits a corruptwebpack://source name (containing kilobytes of raw runtime code) into the host bundle map;new URL()throwsInvalid URLduring consumer construction, so on an MFv2 host every stack — even host-only — failed with a 500.__federation_expose_X.<remote>.chunk.bundle) madecompiler.getSourceMapthrow, failing the entire/symbolicaterequest — host frames included./symbolicateand/open-stack-framedon't round-trip. Bundlers percent-encode^in source map paths, so symbolication returns parent-escape tokens as[projectRoot%5E2]/node_modules/....resolveProjectPathonly matched the literal^, andlaunch-editorsilently ignores nonexistent paths — tapping a node_modules frame did nothing, with no error anywhere.Also: the response stack was not 1:1 with the request (non-http frames were dropped). React Native's LogBox rejects symbolication results with a different number of frames, so any stack containing e.g.
[native code]frames failed client-side.Changes
@callstack/repack-dev-serverSourceMapConsumer(indices preserved so mappings stay aligned).warnlog naming the bundle; the response stack is always 1:1 with the request (Metro semantics).@react-native/metro-config'sINTERNAL_CALLSITES_REGEXapproach — plus the source mapignoreList/x_google_ignoreListfield. Replaces nothing (there was no collapsing before); never matches on method names./open-stack-frame: log unresolvable paths, and pass an error callback tolaunchEditorwith an actionable hint (set REACT_EDITOR) — both failure modes were previously silent.@callstack/repackfetchDeclaredSourceMapincommands/common, wired into both rspack and webpackstartdelegates: when the compiler has no map for a URL, fetch the map the bundle declares via its//# sourceMappingURL=comment (which Re.Pack appends to every served bundle, including MF containers/chunks), resolved relative to the bundle URL. Responses are validated as source maps before use; lookups (including misses) are memoized with a short TTL. Only the declared reference is trusted — no filename guessing, matching how browsers and symbolication services (e.g. Sentry) resolve maps.coerceToHttpUrlhandles scheme-less frame URLs (localhost:8081/...,10.0.2.2:8081/...); notenew URL('localhost:8081/x')parses successfully withlocalhost:as the scheme, so coercion cannot rely on parse errors.resolveProjectPathdecodes URL-encoded[projectRoot%5EN]tokens (token-only, not the whole path).Test plan
tests/integration/e2e/symbolication.test.ts(new, committed failing first): boots both tester-federation-v2 dev servers, derives frame coordinates from real source maps, observes editor launches via a fake-editor shim. Covers: host-only MFv2 stack, remote chunk frame on the host, mixed stack,[projectRoot]editor round-trip, node_modules (%5Etoken) editor round-trip, plus two control cases pinning pre-existing behavior.Symbolicator(sanitization, isolation, 1:1 pass-through, ignoreList collapse, column 0, code-frame anchoring, per-request map caching),fetchDeclaredSourceMap/coerceToHttpUrl(declared-reference resolution, validation, memoization, scheme-less URLs, bare-filename rejection),resolveProjectPath(encoded tokens).pnpm test: 10/10 tasks green.chunkFilename: '[name].MiniApp.chunk.bundle'to mirror the reported setup's chunk naming.Deferred follow-ups
[projectRoot]resolution (monorepos where remotes live in sibling packages — needs a config surface discussion).webpack://source name (root cause of fix: initialize React Devtools before React Refresh #1 above; the sanitization here is defensive).launch-editorfor resolving editor CLIs installed outside PATH (Homebrew, JetBrains Toolbox).