fix(native-federation): shared-package source maps — hidden flag, per-file pre-link, splice-based chunk rewrite - #1128
Conversation
|
thanks for this, I verified the new rewriter directly (splice, accumulated column shift, escaped One part needs to come out, esbuild runs
There's no correct position for it, which I think is the tell: moved after The other half of that fix stands on its own. Post-bundle Minimum for merge
Please trim the commentsThe rewriter has ~35 lines of comment for ~150 lines of code, and most of it restates the // dynamic import: s/e include the quotes — step inside them
// starts inside the replaced span: clamp to its startThe rest I'd drop: the JSDoc blocks on After this I'll test this in a small setup and I think it's good to go. |
ab0db92 to
69b8644
Compare
|
Approved, will merge once the CI succeeds |
…ackages The adapter passed sourcemap: sourcemapOptions.scripts, ignoring the hidden flag normalizeSourceMaps() returns. Map hidden to esbuild's 'external' the way @angular/build's application builder does, so shared bundles come out comment-free while their .map files are still emitted.
…no partial declarations
The esbuild compiler plugin already links partial declarations per input
file (the adapter passes jit: false, so transformFile runs with
skipLinker false), so bundles normally leave esbuild fully linked. The
post-bundle link() pass then re-printed every bundle through babel
anyway - without a sourceMaps option, overwriting the file while the
.map beside it kept describing the old bytes, and dropping the
minifier's per-identifier NAME segments ("show original variables"
misbinds locals). Return early when no partial declaration remains: the
common case ships esbuild's source map byte-verbatim, and the babel
fallback still covers toolchains that bypass the compiler plugin. Same
check the Angular CLI uses.
…the map valid rewriteChunkImports re-printed every emitted file through the TypeScript printer, changing the byte layout while the source map next to it stayed untouched — every mapping in a rewritten file pointed at the wrong bytes, and files with no chunk imports were invalidated for nothing. Locate the './chunk-…' specifier literals with es-module-lexer (the engine es-module-shims and Vite run on minified bundles; already in consumers' trees via the builder's esbuild-plugin-commonjs dependency), rewrite them with a plain string splice, and shift the map to match by round-tripping the mappings through @jridgewell/sourcemap-codec: every decoded segment keeps all its fields — sources, original positions and NAME entries — with only its generated column adjusted. Nothing else in the map is read or written, so the shift is lossless by construction (re-emitting through TraceMap/GenMapping instead was measurably lossy: maybeAddMapping collapses consecutive segments sharing an original position and resolves sources against sourceRoot). Files without chunk imports are not written at all. Review follow-ups: declare es-module-lexer ^1.7.0 and @jridgewell/sourcemap-codec ^1.5.0 in the root package.json instead of relying on hoisting, wire an explicit @nx/vitest test target for the lib and drop the dead jest.config.ts, pass the file path to the lexer so parse errors name the file, and trim the rewriter comments to the load-bearing ones.
69b8644 to
cbbd8a2
Compare
the types do not checkout out in above statement and were replaced by let me know if you prefer another solution |
… guard esbuild's charset defaults to 'ascii', so emitted bundles carry the escape \u0275\u0275ngDeclareComponent rather than the two characters. The early return added in #1128 therefore fired for every shared package and link() never ran at all, instead of only when the compiler plugin had already linked everything. Match both spellings, and await the link calls now that the fallback is reachable again — they were fire-and-forget, so the adapter could return before linking finished and a linker failure surfaced as an unhandled rejection rather than a build error.
Problem
Building an app with source maps enabled leaves the maps of shared packages
(the bundles nf builds itself) broken:
sourceMap.hiddenis ignored — the adapter passessourcemap: sourcemapOptions.scripts, so shared bundles keep their//# sourceMappingURL=comment even when hidden maps were requested.babel re-prints the minified one-line bundle, anchoring mappings per AST
node and dropping the NAME segments the minifier recorded — DevTools
"show original variables" misbinds locals (real values under wrong names).
rewriteChunkImportsre-prints every emitted file through theTypeScript printer — unconditionally, even files with no
./chunk-…import — changing the byte layout while the
.mapnext to it staysuntouched: every mapping in a re-printed file points at the wrong bytes.
Change
libs/native-federation/src/utils/angular-esbuild-adapter.ts:sourceMap.hidden:sourcemap: scripts && (hidden ? 'external' : true)— same mapping the@angular/buildapplication builder uses.an esbuild
onLoadplugin (babel + linker over the small unminifiedfesm, with the package's own
.mapasinputSourceMap) — the samestrategy
@angular/builditself uses. The minifier stays the lastmap-writer and records per-identifier NAME segments. The post-bundle
link()is kept as a fallback and now returns early when the bundle hasno remaining
ɵɵngDeclare— the common case, since the pre-link pluginalready processed them — so esbuild's map ships byte-verbatim.
libs/native-federation-core/src/lib/utils/rewrite-chunk-imports.ts:./chunk-…specifier literals with es-module-lexer, replace them with a plainstring splice, and shift the map by round-tripping
mappingsthrough@jridgewell/sourcemap-codec— every decoded segment keeps all itsfields (sources, original positions, NAME entries) with only the
generated column adjusted;
names/sources/sourcesContent/sourceRootare never read or written, so the shift is lossless byconstruction. Files without chunk imports are not written at all.
Export surface unchanged.