Skip to content

Commit d288f00

Browse files
authored
Example projects (JS / TS) for node backend use of MF (#4316)
* updated js and ts project examples for node dynamic remote loading * remove old dynamic-remote-nodes project and replace with the new one * fix for project dynamic-remotes-node-javascript rename to dynamic-remotes-node
1 parent 908450d commit d288f00

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+29507
-172
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.idea
2+
node_modules
3+
host/dist
4+
remote/dist
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v20.12.0
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Dynamic Remotes on Node w/ TypeScript
2+
3+
Similar to browser side dynamic remotes.
4+
5+
This allows you to dynamically load remote containers on the server.
6+
7+
`pnpm run start` will initiate a build and http server, then the node will load the remotes. The host will poll for changes on the remote and reload when they are detected.
8+
9+
NOTE: HMR is not supported on the server side yet, so we can't get notified of changes in the remote. We have to poll for changes.

dynamic-remotes-node-typescript/host/package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { loadRemote, init } from '@module-federation/runtime';
2+
import {performReload, revalidate } from '@module-federation/node/utils';
3+
4+
console.log('hello from host host');
5+
6+
let instance;
7+
let loadedString;
8+
let loadedClass: (new() => any) | null;
9+
let loadedClassInstance;
10+
11+
async function initAndLoad() {
12+
await performReload(true)
13+
14+
// here we assign the return value of the init() function, which can be used to do some more complex
15+
// things with the module federation runtime
16+
instance = init({
17+
name: 'host',
18+
remotes: [
19+
{
20+
name: 'remote',
21+
entry: 'http://localhost:3002/remoteEntry.js',
22+
},
23+
],
24+
});
25+
26+
// NOTE - below we use .default as the "export default" style in the remote. This changes
27+
// based on the remote's export style.
28+
//
29+
// Other examples:
30+
// - using module.exports would not require the .default
31+
// - using named exports would require the specific export name on the remotely loaded object
32+
// (e.g. export class TestClass {} would require loadedObject.TestClass)
33+
34+
loadedString = (await loadRemote('remote/string') as any).default;
35+
console.log('loaded string', loadedString);
36+
37+
loadedClass = (await loadRemote('remote/class') as any).default;
38+
loadedClassInstance = loadedClass ? new loadedClass() : null;
39+
40+
console.log('current loaded class', loadedClass);
41+
console.log('current loaded class test value', loadedClassInstance?.getTestValue());
42+
console.log('current loaded class run test', await loadedClassInstance?.runTest());
43+
}
44+
45+
initAndLoad();
46+
47+
setInterval(async () => {
48+
console.log('host(): checking remote for updates');
49+
50+
// NOTE: this is called the first time an update is detected on the remote and never again
51+
// NOTE: had to patch hot-reload.js to get this to not throw an error
52+
// we automatically reset require cache, so the reload callback is only if you need to do something else
53+
const shouldReload = await revalidate();
54+
55+
// do something extra after revalidation
56+
if (shouldReload) {
57+
// reload the server
58+
console.log('host(): should reload');
59+
initAndLoad();
60+
} else {
61+
console.log('host(): should not reload');
62+
}
63+
}, 5000);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"compilerOptions": {
3+
"rootDir": "src",
4+
"outDir": "dist"
5+
},
6+
"include": ["src/**/**.ts"],
7+
"exclude": ["node_modules", "!node_modules/@types"]
8+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
const { UniversalFederationPlugin } = require('@module-federation/node');
2+
const webpack = require('webpack');
3+
4+
module.exports = {
5+
entry: './src/index.ts',
6+
mode: 'development',
7+
target: 'async-node',
8+
externals: [],
9+
output: {
10+
publicPath: 'http://localhost:3001/',
11+
library: { type: 'commonjs-module' },
12+
},
13+
resolve: {
14+
extensions: ['.ts', '.tsx', '.js'],
15+
},
16+
module: {
17+
noParse: /yargs/,
18+
rules: [
19+
{
20+
test: /\.tsx?$/,
21+
use: [
22+
{
23+
loader: 'ts-loader',
24+
options: {
25+
transpileOnly: false, // Enables type-checking and .d.ts file emission
26+
},
27+
},
28+
],
29+
exclude: /node_modules/,
30+
},
31+
],
32+
},
33+
plugins: [
34+
new webpack.HotModuleReplacementPlugin(),
35+
new UniversalFederationPlugin({
36+
remoteType: 'script',
37+
isServer: true,
38+
name: 'host',
39+
useRuntimePlugin: true,
40+
exposes: {
41+
'./noop': './src/noop.ts',
42+
},
43+
}),
44+
],
45+
};

0 commit comments

Comments
 (0)