Skip to content
Open
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
1b194ad
fix: preserve worker runtimes with shared runtime chunk
ScriptedAlchemy Oct 4, 2025
8b328d9
fix: adjust runtime reassignment timing
ScriptedAlchemy Oct 4, 2025
decc21f
fix: retain runtime modules when reassigning chunks
ScriptedAlchemy Oct 4, 2025
75762c8
fix: carry runtime modules when moving worker runtime
ScriptedAlchemy Oct 4, 2025
16af11a
fix: copy runtime requirements to reassigned chunk
ScriptedAlchemy Oct 4, 2025
f137cb9
fix: guard remote runtime when bundler runtime missing
ScriptedAlchemy Oct 4, 2025
aa9c43a
fix: guard remotes helper when bundler runtime absent
ScriptedAlchemy Oct 4, 2025
0fada87
fix: guard remote runtime indentation
ScriptedAlchemy Oct 4, 2025
6b14527
fix: type guard runtime template indent
ScriptedAlchemy Oct 4, 2025
53301e6
fix: retain shared runtime modules for async entries
ScriptedAlchemy Oct 4, 2025
ac8fc81
chore: add changeset for async runtime fix
ScriptedAlchemy Oct 4, 2025
a30e43c
test: cover worker runtime chunk clone
ScriptedAlchemy Oct 4, 2025
b00b06b
test: capture worker runtime duplication
ScriptedAlchemy Oct 4, 2025
0cfff1b
test: cover compiler runtime hoisting
ScriptedAlchemy Oct 5, 2025
c5afda1
fix(module-federation): guard remote runtime chunk relocation
ScriptedAlchemy Oct 6, 2025
874341d
test(enhanced): add worker test case for module federation
ScriptedAlchemy Oct 7, 2025
1062bc9
fix(enhanced): prevent React version contamination in worker test
ScriptedAlchemy Oct 7, 2025
66325ee
fix(enhanced): ensure federation runtime in worker chunks and prevent…
ScriptedAlchemy Oct 7, 2025
49c44ab
chore(module-federation): scope root test script to packages (tag:typ…
ScriptedAlchemy Oct 7, 2025
e0226a0
Merge remote-tracking branch 'origin/main' into research/issue-4085
ScriptedAlchemy Oct 7, 2025
c9d39ff
chore(runtime-demo): add Worker wrapper demo mirroring gravity-ui pat…
ScriptedAlchemy Oct 7, 2025
95cef3f
fix(runtime-demo): enable runtime chunk and use WorkerWrapper for wor…
ScriptedAlchemy Oct 7, 2025
e242212
fix: format code to pass CI checks
ScriptedAlchemy Oct 7, 2025
dac0e60
chore(tests,docs): restore removed fixtures and docs from main
ScriptedAlchemy Oct 7, 2025
3e2713f
test(runtime-demo): skip WorkerWrapper in Cypress to avoid importScri…
ScriptedAlchemy Oct 7, 2025
09a4ff2
test(enhanced): add react stub under container/worker test fixture to…
ScriptedAlchemy Oct 7, 2025
730e00e
test(runtime-demo): skip WorkerDemo worker init under Cypress to avoi…
ScriptedAlchemy Oct 7, 2025
8bf41de
test(e2e): account for Cypress skip by expecting n/a for worker result
ScriptedAlchemy Oct 7, 2025
afc8e68
fix(runtime-demo): load compiled worker via .js URL so importScripts …
ScriptedAlchemy Oct 7, 2025
a5511fd
fix(runtime-demo): emit dedicated worker entry as worker.js and point…
ScriptedAlchemy Oct 7, 2025
8bdbc3d
fix(runtime-demo): force importScripts to load /worker.js from public…
ScriptedAlchemy Oct 7, 2025
a35626c
test(e2e): assert both native worker and worker-loader wrapper responses
ScriptedAlchemy Oct 7, 2025
4d685d0
fix(runtime-demo): preload runtime.js in worker loader and use static…
ScriptedAlchemy Oct 7, 2025
2779164
Merge branch 'main' into research/issue-4085
ScriptedAlchemy Oct 8, 2025
d0f805d
Merge remote-tracking branch 'origin/main' into research/issue-4085
ScriptedAlchemy Oct 9, 2025
c08e78e
test: add worker federation compiler integration
ScriptedAlchemy Oct 9, 2025
a98e9d1
refactor: use existing identifiers for async runtimes
ScriptedAlchemy Oct 9, 2025
bbb335f
fix: ensure runtime names are plain strings
ScriptedAlchemy Oct 9, 2025
8188210
test: inspect share plugin internals via private fields
ScriptedAlchemy Oct 9, 2025
6b5045a
refactor: switch runtime app to worker-loader
ScriptedAlchemy Oct 9, 2025
952bec0
refactor: split worker demo into native and loader flows
ScriptedAlchemy Oct 9, 2025
db01b16
test: disable dts in worker async runtime test
ScriptedAlchemy Oct 9, 2025
2177eac
chore: merge main into research issue 4085
ScriptedAlchemy Oct 10, 2025
58c0cf0
fix: reattach runtime modules via hooks
ScriptedAlchemy Oct 10, 2025
a6bede7
test: harden compiler unit suites
ScriptedAlchemy Oct 10, 2025
58b007c
chore: merge main
ScriptedAlchemy Oct 10, 2025
3357428
chore: run full enhanced test suite
ScriptedAlchemy Oct 10, 2025
b4f0a90
test: extend worker integration timeout
ScriptedAlchemy Oct 10, 2025
7cc4b1f
Merge branch 'main' into research/issue-4085
ScriptedAlchemy Oct 10, 2025
39c8d3a
fix: guard manifest package detection
ScriptedAlchemy Oct 10, 2025
ae883fd
Merge branch 'main' into research/issue-4085
ScriptedAlchemy Oct 10, 2025
8fd36a6
chore: disable nx tui and clean enhanced jest setup
ScriptedAlchemy Oct 10, 2025
da540ca
Merge remote-tracking branch 'origin/main' into research/issue-4085
ScriptedAlchemy Oct 10, 2025
a0e6864
fix(runtime): hoist federation runtime into worker child compilers
ScriptedAlchemy Oct 11, 2025
1d5497a
fix(enhanced): inject federation runtime into worker chunks
ScriptedAlchemy Oct 12, 2025
becdf94
fix: ensure runtime logger available in worker
ScriptedAlchemy Oct 12, 2025
0c9ddec
fix(enhanced): restore runtime plugin defaults
ScriptedAlchemy Oct 12, 2025
073d591
feat: include federation runtime for worker blocks
ScriptedAlchemy Oct 12, 2025
1814b86
refactor: hoist worker federation runtime chunks
ScriptedAlchemy Oct 12, 2025
5edb2a3
fix(runtime): ensure federation runtime initializes before chunk depe…
ScriptedAlchemy Oct 14, 2025
09a5e01
Merge branch 'main' into research/issue-4085
ScriptedAlchemy Oct 14, 2025
33922cf
test(enhanced): add comprehensive tests for worker runtime initializa…
ScriptedAlchemy Oct 14, 2025
152d3ff
fix(enhanced): fix compiler-unit tests for runtime module execution o…
ScriptedAlchemy Oct 14, 2025
950b123
fix(enhanced): fix worker runtime test configuration
ScriptedAlchemy Oct 14, 2025
7136535
fix(enhanced): require federation entry after webpack runtime init
ScriptedAlchemy Oct 14, 2025
d9a251f
fix(enhanced): simplify runtime initialization and add e2e test script
ScriptedAlchemy Oct 15, 2025
60e71cf
fix(enhanced): use dynamic stage selection for federation runtime ini…
ScriptedAlchemy Oct 15, 2025
317231c
fix(enhanced): update worker runtime test for new immediate execution…
ScriptedAlchemy Oct 15, 2025
9e9459f
chore: stabilize runtime e2e wait in ci
ScriptedAlchemy Oct 15, 2025
f8e48dc
test: harden embed federation runtime plugin coverage
ScriptedAlchemy Oct 15, 2025
a6f2ab2
fix: remove runtime console logging
ScriptedAlchemy Oct 15, 2025
c24815b
chore: delete runtime logs
ScriptedAlchemy Oct 15, 2025
cb7434b
fix: protect runtime e2e signal handling
ScriptedAlchemy Oct 15, 2025
2e79954
Merge branch 'main' into research/issue-4085
ScriptedAlchemy Oct 16, 2025
61eaa5c
fix: exit router host e2e runs in ci
ScriptedAlchemy Oct 17, 2025
a746f3a
fix: resolve router v7 dist aliases to proper files
ScriptedAlchemy Oct 17, 2025
810c250
fix: align router v7 alias with main
ScriptedAlchemy Oct 17, 2025
c193386
refactor: remove federation shim in runtime worker
ScriptedAlchemy Oct 17, 2025
62a76e1
docs: restore commented mf options in runtime webpack config
ScriptedAlchemy Oct 17, 2025
205acea
Merge remote-tracking branch 'origin/main' into research/issue-4085
ScriptedAlchemy Oct 20, 2025
ac986a9
Merge branch 'main' into research/issue-4085
ScriptedAlchemy Oct 21, 2025
fec5bad
chore(3005-runtime-host): cover blob-based worker scenario
ScriptedAlchemy Oct 21, 2025
c282a5f
chore: remove internal documentation file
ScriptedAlchemy Oct 22, 2025
5d28b46
Merge branch 'main' into research/issue-4085
ScriptedAlchemy Oct 22, 2025
f0713eb
fix(enhanced): add defensive bundlerRuntime guards for HMR chunks
ScriptedAlchemy Oct 22, 2025
04a5d08
chore: remove id-token permission from e2e-metro workflow
ScriptedAlchemy Oct 22, 2025
4a375c6
Merge branch 'main' into research/issue-4085
ScriptedAlchemy Oct 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/steady-workers-remotes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@module-federation/enhanced": patch
---

Keep async entry runtime helpers available when cloning runtimes for web workers and other dynamic entrypoints.
15 changes: 15 additions & 0 deletions apps/runtime-demo/3005-runtime-host/cypress/e2e/app.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,19 @@ describe('3005-runtime-host/', () => {
});
});
});

describe('web worker check', () => {
it('should display value returned from worker', () => {
cy.get('.worker-native-result').should('contain.text', '"answer": "1"');
cy.get('.worker-native-result').should(
'contain.text',
'"federationKeys"',
);
cy.get('.worker-loader-result').should('contain.text', '"answer": "1"');
cy.get('.worker-loader-result').should(
'contain.text',
'"federationKeys"',
);
});
});
});
9 changes: 5 additions & 4 deletions apps/runtime-demo/3005-runtime-host/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
"version": "0.0.0",
"devDependencies": {
"@module-federation/core": "workspace:*",
"@module-federation/dts-plugin": "workspace:*",
"@module-federation/enhanced": "workspace:*",
"@module-federation/runtime": "workspace:*",
"@module-federation/typescript": "workspace:*",
"@module-federation/enhanced": "workspace:*",
"@module-federation/dts-plugin": "workspace:*",
"@pmmmwh/react-refresh-webpack-plugin": "0.5.15",
"react-refresh": "0.14.2",
"@types/react": "18.3.11",
"@types/react-dom": "18.3.0"
"@types/react-dom": "18.3.0",
"react-refresh": "0.14.2",
"worker-loader": "^3.0.8"
},
"dependencies": {
"antd": "4.24.15",
Expand Down
36 changes: 36 additions & 0 deletions apps/runtime-demo/3005-runtime-host/src/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import WebpackPng from './webpack.png';
import WebpackSvg from './webpack.svg';
import { WebpackPngRemote, WebpackSvgRemote } from './Remote1';
import Remote2 from './Remote2';
import WorkerNativeDemo from './components/WorkerNativeDemo';
import WorkerLoaderDemo from './components/WorkerLoaderDemo';

const Root = () => (
<div>
Expand Down Expand Up @@ -89,6 +91,40 @@ const Root = () => (
</tr>
</tbody>
</table>

<h3>check workers</h3>
<table border={1} cellPadding={5}>
<thead>
<tr>
<td></td>
<td>Test case</td>
<td>Expected</td>
<td>Actual</td>
</tr>
</thead>
<tbody>
<tr>
<td>✅</td>
<td>Native new Worker(new URL(...))</td>
<td>
<div className="worker-expected">Expected worker response: 1</div>
</td>
<td>
<WorkerNativeDemo />
</td>
</tr>
<tr>
<td>✅</td>
<td>worker-loader integration</td>
<td>
<div className="worker-expected">Expected worker response: 1</div>
</td>
<td>
<WorkerLoaderDemo />
</td>
</tr>
</tbody>
</table>
</div>
);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useEffect, useState } from 'react';
import LoaderWorker from 'worker-loader!../worker/loader-worker.js';

export function WorkerLoaderDemo() {
const [result, setResult] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);

useEffect(() => {
try {
const worker = new LoaderWorker();

worker.onmessage = (event) => {
setResult(event.data ?? null);
};

worker.onerror = (event) => {
setError((event as unknown as ErrorEvent).message ?? 'Worker error');
};

worker.postMessage({ value: 'foo' });

return () => {
worker.terminate();
};
} catch (err) {
setError((err as Error).message);
}

return undefined;
}, []);

return (
<div>
<div className="worker-expected">Expected worker response: 1</div>
<pre className="worker-loader-result">
{result ? JSON.stringify(result, null, 2) : 'n/a'}
</pre>
{error ? <div className="worker-error">Worker error: {error}</div> : null}
</div>
);
}

export default WorkerLoaderDemo;
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useEffect, useState } from 'react';

export function WorkerNativeDemo() {
const [result, setResult] = useState<any>(null);
const [error, setError] = useState<string | null>(null);

useEffect(() => {
try {
const worker = new Worker(
new URL('../worker/native-worker.js', import.meta.url),
{
name: 'mf-native-worker',
type: 'module',
},
);

worker.onmessage = (event) => {
setResult(event.data ?? null);
};

worker.onerror = (event) => {
setError((event as unknown as ErrorEvent).message ?? 'Worker error');
};

worker.postMessage({ value: 'foo' });

return () => {
worker.terminate();
};
} catch (err) {
setError((err as Error).message);
}

return undefined;
}, []);

return (
<div>
<pre className="worker-native-result">
{result ? JSON.stringify(result, null, 2) : 'n/a'}
</pre>
{error ? <div className="worker-error">Worker error: {error}</div> : null}
</div>
);
}

export default WorkerNativeDemo;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
declare module 'worker-loader!*' {
class WebpackWorker extends Worker {
constructor();
}

export default WebpackWorker;
}
15 changes: 15 additions & 0 deletions apps/runtime-demo/3005-runtime-host/src/worker/loader-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* eslint-env worker */
import { workerMap } from './map.js';

self.onmessage = (event) => {
const value = event.data && event.data.value;
const federation =
typeof __webpack_require__ !== 'undefined'
? __webpack_require__.federation || {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is some debug code? Because in my project i got this problem, when federation is undefined (i was unable to reproduce it, but found another error, that i was reported in issue)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will revert

: {};
const federationKeys = Object.keys(federation);
self.postMessage({
answer: workerMap[value] ?? null,
federationKeys,
});
};
4 changes: 4 additions & 0 deletions apps/runtime-demo/3005-runtime-host/src/worker/map.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const workerMap = {
foo: '1',
bar: '2',
};
15 changes: 15 additions & 0 deletions apps/runtime-demo/3005-runtime-host/src/worker/native-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* eslint-env worker */
import { workerMap } from './map.js';

self.onmessage = (event) => {
const value = event.data && event.data.value;
const federation =
typeof __webpack_require__ !== 'undefined'
? __webpack_require__.federation || {}
: {};
const federationKeys = Object.keys(federation);
self.postMessage({
answer: workerMap[value] ?? null,
federationKeys,
});
};
9 changes: 9 additions & 0 deletions apps/runtime-demo/3005-runtime-host/src/worker/worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/* eslint-env worker */
import { workerMap } from './map';

self.onmessage = (event) => {
const value = event.data && event.data.value;
self.postMessage({
answer: workerMap[value] ?? null,
});
};
6 changes: 5 additions & 1 deletion apps/runtime-demo/3005-runtime-host/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ module.exports = composePlugins(withNx(), withReact(), (config, context) => {
},
}),
);

if (!config.devServer) {
config.devServer = {};
}
Expand All @@ -99,7 +100,10 @@ module.exports = composePlugins(withNx(), withReact(), (config, context) => {
scriptType: 'text/javascript',
};
config.optimization = {
runtimeChunk: false,
...(config.optimization ?? {}),
runtimeChunk: {
name: 'runtime',
},
minimize: false,
moduleIds: 'named',
};
Expand Down
3 changes: 3 additions & 0 deletions nx.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@
],
"sharedGlobals": ["{workspaceRoot}/babel.config.json"]
},
"tui": {
"enabled": false
},
"workspaceLayout": {
"appsDir": "apps",
"libsDir": "packages"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"f": "nx format:write",
"enhanced:jest": "pnpm build && cd packages/enhanced && NODE_OPTIONS=--experimental-vm-modules npx jest test/ConfigTestCases.basictest.js test/unit",
"lint": "nx run-many --target=lint",
"test": "nx run-many --target=test",
"test": "NX_TUI=false nx run-many --target=test --projects=tag:type:pkg --skip-nx-cache",
"build": "NX_TUI=false nx run-many --target=build --parallel=5 --projects=tag:type:pkg",
"build:pkg": "NX_TUI=false nx run-many --targets=build --projects=tag:type:pkg --skip-nx-cache",
"test:pkg": "NX_TUI=false nx run-many --targets=test --projects=tag:type:pkg --skip-nx-cache",
Expand Down
20 changes: 10 additions & 10 deletions packages/enhanced/jest.config.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
/* eslint-disable */
import { readFileSync, rmdirSync, existsSync } from 'fs';
import { readFileSync, rmSync, existsSync } from 'fs';
import path from 'path';
import os from 'os';
const rimraf = require('rimraf');

// Reading the SWC compilation config and remove the "exclude"
// for the test files to be compiled by SWC
const { exclude: _, ...swcJestConfig } = JSON.parse(
readFileSync(`${__dirname}/.swcrc`, 'utf-8'),
);

rimraf.sync(__dirname + '/test/js');
const transpiledDir = path.join(__dirname, 'test/js');
if (existsSync(transpiledDir)) {
rmSync(transpiledDir, { recursive: true, force: true });
}

// disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves.
// If we do not disable this, SWC Core will read .swcrc and won't transform our test files due to "exclude"
Expand All @@ -23,13 +25,11 @@ if (swcJestConfig.swcrc === undefined) {
// jest needs EsModule Interop to find the default exported setup/teardown functions
// swcJestConfig.module.noInterop = false;

const testMatch = [];

if (process.env['TEST_TYPE'] === 'unit') {
testMatch.push('<rootDir>/test/unit/**/*.test.ts');
} else {
testMatch.push('<rootDir>/test/*.basictest.js');
}
const testMatch = [
'<rootDir>/test/*.basictest.js',
'<rootDir>/test/unit/**/*.test.ts',
'<rootDir>/test/compiler-unit/**/*.test.ts',
];

export default {
displayName: 'enhanced',
Expand Down
8 changes: 5 additions & 3 deletions packages/enhanced/jest.embed.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
/* eslint-disable */
import { readFileSync, rmdirSync, existsSync } from 'fs';
import { readFileSync, rmSync, existsSync } from 'fs';
import path from 'path';
import os from 'os';
const rimraf = require('rimraf');

// Reading the SWC compilation config and remove the "exclude"
// for the test files to be compiled by SWC
const { exclude: _, ...swcJestConfig } = JSON.parse(
readFileSync(`${__dirname}/.swcrc`, 'utf-8'),
);

rimraf.sync(__dirname + '/test/js');
const transpiledDir = path.join(__dirname, 'test/js');
if (existsSync(transpiledDir)) {
rmSync(transpiledDir, { recursive: true, force: true });
}

// disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves.
// If we do not disable this, SWC Core will read .swcrc and won't transform our test files due to "exclude"
Expand Down
6 changes: 1 addition & 5 deletions packages/enhanced/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,7 @@
"parallel": false,
"commands": [
{
"command": "TEST_TYPE=basic node --expose-gc --max-old-space-size=24576 --experimental-vm-modules --trace-deprecation ./node_modules/jest-cli/bin/jest --logHeapUsage --config packages/enhanced/jest.config.ts --silent",
"forwardAllArgs": false
},
{
"command": "TEST_TYPE=unit node --expose-gc --max-old-space-size=24576 --experimental-vm-modules --trace-deprecation ./node_modules/jest-cli/bin/jest --logHeapUsage --config packages/enhanced/jest.config.ts --silent",
"command": "node --expose-gc --max-old-space-size=24576 --experimental-vm-modules --trace-deprecation ./node_modules/jest-cli/bin/jest --logHeapUsage --config packages/enhanced/jest.config.ts --silent",
"forwardAllArgs": false
}
]
Expand Down
Loading
Loading