Skip to content

Commit

Permalink
Example projects (JS / TS) for node backend use of MF (#4316)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
arimus authored Dec 4, 2024
1 parent 908450d commit d288f00
Show file tree
Hide file tree
Showing 47 changed files with 29,507 additions and 172 deletions.
4 changes: 4 additions & 0 deletions dynamic-remotes-node-typescript/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.idea
node_modules
host/dist
remote/dist
1 change: 1 addition & 0 deletions dynamic-remotes-node-typescript/.nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v20.12.0
9 changes: 9 additions & 0 deletions dynamic-remotes-node-typescript/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Dynamic Remotes on Node w/ TypeScript

Similar to browser side dynamic remotes.

This allows you to dynamically load remote containers on the server.

`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.

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.
6 changes: 6 additions & 0 deletions dynamic-remotes-node-typescript/host/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dynamic-remotes-node-typescript/host/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
63 changes: 63 additions & 0 deletions dynamic-remotes-node-typescript/host/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { loadRemote, init } from '@module-federation/runtime';
import {performReload, revalidate } from '@module-federation/node/utils';

console.log('hello from host host');

let instance;
let loadedString;
let loadedClass: (new() => any) | null;
let loadedClassInstance;

async function initAndLoad() {
await performReload(true)

// here we assign the return value of the init() function, which can be used to do some more complex
// things with the module federation runtime
instance = init({
name: 'host',
remotes: [
{
name: 'remote',
entry: 'http://localhost:3002/remoteEntry.js',
},
],
});

// NOTE - below we use .default as the "export default" style in the remote. This changes
// based on the remote's export style.
//
// Other examples:
// - using module.exports would not require the .default
// - using named exports would require the specific export name on the remotely loaded object
// (e.g. export class TestClass {} would require loadedObject.TestClass)

loadedString = (await loadRemote('remote/string') as any).default;
console.log('loaded string', loadedString);

loadedClass = (await loadRemote('remote/class') as any).default;
loadedClassInstance = loadedClass ? new loadedClass() : null;

console.log('current loaded class', loadedClass);
console.log('current loaded class test value', loadedClassInstance?.getTestValue());
console.log('current loaded class run test', await loadedClassInstance?.runTest());
}

initAndLoad();

setInterval(async () => {
console.log('host(): checking remote for updates');

// NOTE: this is called the first time an update is detected on the remote and never again
// NOTE: had to patch hot-reload.js to get this to not throw an error
// we automatically reset require cache, so the reload callback is only if you need to do something else
const shouldReload = await revalidate();

// do something extra after revalidation
if (shouldReload) {
// reload the server
console.log('host(): should reload');
initAndLoad();
} else {
console.log('host(): should not reload');
}
}, 5000);
File renamed without changes.
8 changes: 8 additions & 0 deletions dynamic-remotes-node-typescript/host/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"rootDir": "src",
"outDir": "dist"
},
"include": ["src/**/**.ts"],
"exclude": ["node_modules", "!node_modules/@types"]
}
45 changes: 45 additions & 0 deletions dynamic-remotes-node-typescript/host/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const { UniversalFederationPlugin } = require('@module-federation/node');
const webpack = require('webpack');

module.exports = {
entry: './src/index.ts',
mode: 'development',
target: 'async-node',
externals: [],
output: {
publicPath: 'http://localhost:3001/',
library: { type: 'commonjs-module' },
},
resolve: {
extensions: ['.ts', '.tsx', '.js'],
},
module: {
noParse: /yargs/,
rules: [
{
test: /\.tsx?$/,
use: [
{
loader: 'ts-loader',
options: {
transpileOnly: false, // Enables type-checking and .d.ts file emission
},
},
],
exclude: /node_modules/,
},
],
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new UniversalFederationPlugin({
remoteType: 'script',
isServer: true,
name: 'host',
useRuntimePlugin: true,
exposes: {
'./noop': './src/noop.ts',
},
}),
],
};
Loading

0 comments on commit d288f00

Please sign in to comment.