nx
nx copied to clipboard
perf(module-federation): optimize performance with caching and algorithm improvements
Current Behavior
The module federation utilities have several performance inefficiencies and code duplication:
Performance Issues
┌─────────────────────────────────────────────────────────────────────────────┐
│ BEFORE: Performance Bottlenecks │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Build Start │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────┐ │
│ │ For each remote (10+ times): │ │
│ │ • JSON.parse(env variable) ──────►│ Repeated parsing! │
│ │ • readRootPackageJson() ──────►│ Repeated file I/O! │
│ └──────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────┐ │
│ │ For each library in dependency tree: │ │
│ │ • readTsPathMappings() ──────►│ Called in loop! │
│ │ • Array.find() for lookup ──────►│ O(n) per lookup! │
│ └──────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────┐ │
│ │ For EVERY import during build: │ │
│ │ • dirname(library.path) ──────►│ Hot path! │
│ │ • normalize(...) ──────►│ Computed every time! │
│ └──────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Code Duplication
┌─────────────────────────────────────────────────────────────────────────────┐
│ BEFORE: Duplicated Code (~400 lines) │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ webpack/utils.ts ─────┐ │
│ │ │
│ rspack/utils.ts ─────┼─► getFunctionDeterminateRemoteUrl() (~38 lines x 3) │
│ │ • Same logic, different remoteEntry extension │
│ angular/utils.ts ─────┘ │
│ │
│ ──────────────────────────────────────────────────────────────────────── │
│ │
│ plugins/utils/start-remote-proxies.ts ─┐ │
│ │ │
│ utils/start-remote-proxies.ts ─────────┼─► Proxy server setup (~50 lines x 3)│
│ │ • SSL handling │
│ utils/start-ssr-remote-proxies.ts ─────┘ • Express + middleware │
│ • Process signal handlers │
│ │
│ ──────────────────────────────────────────────────────────────────────── │
│ │
│ parseStaticRemotesConfig() ────┐ │
│ ├─► 94% identical code (~35 lines x 2) │
│ parseStaticSsrRemotesConfig() ─┘ • Only diff: dirname(outputPath) for SSR │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Expected Behavior
┌─────────────────────────────────────────────────────────────────────────────┐
│ AFTER: Optimized Architecture │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Build Start │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────┐ │
│ │ Cached Operations (computed once): │ │
│ │ • getStaticRemotesFromEnv() ──────►│ Cached with invalidation │
│ │ • readRootPackageJson() ──────►│ Cached │
│ │ • readTsPathMappings() ──────►│ Hoisted outside loops │
│ │ • getDependentPackages() ──────►│ WeakMap memoization │
│ └──────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────┐ │
│ │ Pre-computed Values: │ │
│ │ • workspaceLibsByImportKey ──────►│ Map for O(1) lookup │
│ │ • keyDepths Map ──────►│ Pre-computed for sorting │
│ │ • pathMappings[].libFolder ──────►│ Pre-computed dirname/normalize │
│ └──────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────┐ │
│ │ Hot Path (every import): │ │
│ │ • Use pre-computed libFolder ─────►│ No computation needed! │
│ └──────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Shared Utilities
┌─────────────────────────────────────────────────────────────────────────────┐
│ AFTER: Consolidated Code │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ NEW: utils/remote-url.ts │
│ ┌─────────────────────────────────────────────────┐ │
│ │ createRemoteUrlResolver({ isServer, bundler }) │ │
│ │ • Cached getStaticRemotesFromEnv() │ │
│ │ • Bundler-specific remoteEntry extension │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ├─► webpack/utils.ts (3 lines) │
│ ├─► rspack/utils.ts (3 lines) │
│ └─► angular/utils.ts (4 lines) │
│ │
│ ──────────────────────────────────────────────────────────────────────── │
│ │
│ NEW: utils/proxy-server.ts │
│ ┌─────────────────────────────────────────────────┐ │
│ │ createRemoteProxyServers(remotes, options) │ │
│ │ • SSL certificate handling │ │
│ │ • Express server + proxy middleware │ │
│ │ • Process signal handlers │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ├─► plugins/utils/start-remote-proxies.ts (10 lines) │
│ ├─► utils/start-remote-proxies.ts (10 lines) │
│ └─► utils/start-ssr-remote-proxies.ts (10 lines) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Changes Made
Performance Optimizations
| # | Optimization | File(s) | Impact |
|---|---|---|---|
| 1 | Cache env variable parsing | webpack, rspack, angular utils | Eliminates 10+ JSON.parse per build |
| 2 | Pre-compute sort depths | share.ts | O(n) vs O(n log n) string splits |
| 3 | Map for O(1) lookups | share.ts | O(1) vs O(n) per lookup |
| 4 | Memoize secondary entry points | secondary-entry-points.ts | Avoids repeated dir scans |
| 5 | Cache readRootPackageJson | package-json.ts | Single file read per build |
| 6 | Hoist tsConfigPathMappings | dependencies.ts | Avoids repeated calls in loop |
| 7 | Memoize getDependentPackages | dependencies.ts | WeakMap with auto-invalidation |
| 8 | Pre-compute libFolder | share.ts | No hot-path computation |
Code Deduplication
| # | Consolidation | Lines Removed | New Shared Module |
|---|---|---|---|
| 9 | Remote URL resolver | ~80 lines | utils/remote-url.ts |
| 10 | Proxy server setup | ~120 lines | utils/proxy-server.ts |
| 11 | Parse remotes config | ~35 lines | Shared core function |
Bug Fix
| # | Fix | File | Issue |
|---|---|---|---|
| 12 | require.resolve error handling | share.ts | Was checking for null, but it throws |
Test Results
Test Suites: 10 passed, 10 total
Tests: 1 skipped, 56 passed, 57 total
Summary
13 files changed
+ 346 insertions
- 357 deletions
──────────────────
-11 lines net (with MORE functionality!)
New shared utilities:
packages/module-federation/src/utils/proxy-server.tspackages/module-federation/src/utils/remote-url.ts
Related Issue(s)
Performance improvements for module federation - no specific issue
Merge Dependencies
Must be merged AFTER: #33733 Must be merged BEFORE: #33735