Conversation
…el imports resolveSpecifier() looked up the top-level imports in the `else` branch of the scope loop. The first scope that did not contain the importing module therefore returned the host's entry, and the later scopes, among them the importing module's own scope, were never checked. With SSR, every remote except the first one of the manifest is linked against the host's version of each shared package, while the browser (es-module-shims) correctly uses the remote's scope. Server-rendered markup and hydrating code then come from different versions of the same library. Remove the `else` branch: only scopes containing the importing module are checked, and the top-level imports, already returned after the loop, remain the fallback.
The resolver written to federation-resolver.mjs at runtime is not import-map-loader.js itself but base64 copies of it: - native-federation-node/src/lib/utils/loader-as-data-url.js (node libs/native-federation-node/build/create-data-url.js) - the same string inlined in native-federation-node/src/lib/utils/fstart.mjs - native-federation/src/tools/fstart-as-data-url.ts, base64 of fstart.mjs (node libs/native-federation/build/create-data-url.js) Only the base64 string of fstart.mjs is replaced: re-bundling it with esbuild would also pull in unrelated runtime changes made since it was last bundled. Each copy decodes back to its source, see the verification script in the pull request.
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.
fix(native-federation-node): resolve import map scopes like the v4 Node loader
Problem
On SSR,
federation-resolver.mjslinks every remote except the first one of the manifest against the host's version of each shared package, ignoring the remote's own scope. The browser (es-module-shims) resolves the same import map correctly. So when a remote shares a package in a different version than the host, the server renders it with code it wasn't built against and the browser hydrates it with another version.Cause: 21.x.x differs from v4
v4's Node loader (orchestrator
node-loader.ts) checks every scope and only then falls back to top-levelimports. The 21.x.x loader has an extraelsethat returns the top-level entry at the first scope that doesn't match:for (… scopePrefix in importMap.scopes) { if (scopePrefix === currentBaseURL || (… currentBaseURL.startsWith(scopePrefix))) { const scopeImportsMatch = resolveImportsMatch(normalizedSpecifier, importMap.scopes[scopePrefix]); if (scopeImportsMatch) return scopeImportsMatch; - } else { - const topLevelImportsMatch = resolveImportsMatch(normalizedSpecifier, importMap.imports); - if (topLevelImportsMatch) return topLevelImportsMatch; // ← 21.x.x only, not in v4 } } return resolveImportsMatch(normalizedSpecifier, importMap.imports);This PR removes that branch, so the 21.x.x loop becomes identical to v4's, and to the import maps spec and es-module-shims.
Reproduction (no install)
From the repo root, run this on
21.x.x, then on this branch:21.x.xprintshttp://host/dep-1.0.0.js❌:remote-adoesn't match, so theelsereturns the host entry.http://remote-b/dep-2.0.0.js✅, like the browser and v4.Commits
fix:import-map-loader.js, 8 lines removed.chore: regenerates the base64 copies that actually run (loader-as-data-url.js, the string inlined infstart.mjs,fstart-as-data-url.ts), one line each. Infstart.mjsonly the embedded string is replaced, since re-bundling with esbuild would pull in unrelated changes.Verify commit 2 (no install): each copy decodes back to its source
All three should print
true.Risk
imports).nx format:checkandlint buildfornative-federation-nodeandnative-federationpass.Why it matters to us
In our app, a remote on our design system
3.7.0was rendered on the server with the host's3.4.1. After hydration, Angular removed the server-rendered styles, and buttons lost their styling. This happens silently, since hydration checks are dev-only. A 21.x patch release of@softarc/native-federation-nodewould unblock us.