Skip to content

Commit

Permalink
feat: add MF React and Preact example
Browse files Browse the repository at this point in the history
  • Loading branch information
nexckycort committed Dec 16, 2024
1 parent 8ec14d7 commit f6d2449
Show file tree
Hide file tree
Showing 18 changed files with 6,417 additions and 5,073 deletions.
11,224 changes: 6,151 additions & 5,073 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions react-preact-runtime-typescript/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# React and Preact Integration at Runtime

This example demonstrates how to run a React-based application (shell) while consuming a remote Preact-based application (remote) dynamically at runtime.

- `shell`: is the host application using React and ReactDOM.
- `remote`: The guest application built with Preact. It provides an injector function that allows the host application (shell) to import and mount it into a specified `<div>` element.

# How to Run the Demo

Run `pnpm run start`. This will build and serve both `shell` and `remote` on ports 3001 and 3002 respectively.

- [localhost:3001](http://localhost:3001/) (HOST)
- [localhost:3002](http://localhost:3002/) (STANDALONE REMOTE)
9 changes: 9 additions & 0 deletions react-preact-runtime-typescript/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "react-preact-runtime-typescript",
"description": "This example demos host and remote applications running in isolation with React and Preact",
"version": "0.0.0",
"scripts": {
"start": "pnpm --filter react-preact-runtime-typescript-* --parallel start",
"build": "pnpm --filter react-preact-runtime-typescript-* --parallel build"
}
}
19 changes: 19 additions & 0 deletions react-preact-runtime-typescript/remote/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "react-preact-runtime-typescript-remote",
"private": true,
"version": "1.0.0",
"scripts": {
"start": "rsbuild dev",
"build": "rsbuild build",
"preview": "rsbuild preview"
},
"dependencies": {
"preact": "^10.25.1"
},
"devDependencies": {
"@module-federation/rsbuild-plugin": "^0.8.4",
"@rsbuild/core": "^1.1.8",
"@rsbuild/plugin-preact": "^1.2.0",
"typescript": "^5.7.2"
}
}
26 changes: 26 additions & 0 deletions react-preact-runtime-typescript/remote/rsbuild.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { pluginModuleFederation } from '@module-federation/rsbuild-plugin';
import { defineConfig } from '@rsbuild/core';
import { pluginPreact } from '@rsbuild/plugin-preact';

export default defineConfig({
plugins: [
pluginPreact(),
pluginModuleFederation({
name: 'remote',
exposes: {
'./appInjector': './src/appInjector',
},
}),
],
server: {
port: 3002,
},
tools: {
rspack: {
output: {
uniqueName: 'remote',
publicPath: 'auto',
},
},
},
});
10 changes: 10 additions & 0 deletions react-preact-runtime-typescript/remote/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import LocalButton from './Button';

const App = () => (
<div style={{ border: '1px red solid' }}>
<h1>Remote Application - Preact</h1>
<LocalButton />
</div>
);

export default App;
3 changes: 3 additions & 0 deletions react-preact-runtime-typescript/remote/src/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const Button = () => <button>Remote Button</button>;

export default Button;
26 changes: 26 additions & 0 deletions react-preact-runtime-typescript/remote/src/appInjector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import App from './App';
import { render } from 'preact';

let root: HTMLElement | null = null;

export const inject = (parentElementId: string) => {
const parentElement = document.getElementById(parentElementId);
if (!parentElement) {
console.error(`Element with id '${parentElementId}' not found.`);
return;
}

root = parentElement;
render(<App />, root);
};

export const unmount = () => {
if (root) {
render(null, root);
root = null;
} else {
console.warn(
'Root is not defined. Ensure inject() is called before unmount().',
);
}
};
7 changes: 7 additions & 0 deletions react-preact-runtime-typescript/remote/src/bootstrap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { render } from 'preact';
import App from './App';

const root = document.getElementById('root');
if (root) {
render(<App />, root);
}
1 change: 1 addition & 0 deletions react-preact-runtime-typescript/remote/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import('./bootstrap');
28 changes: 28 additions & 0 deletions react-preact-runtime-typescript/remote/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"compilerOptions": {
"lib": ["DOM", "ES2020"],
"jsx": "react-jsx",
"target": "ES2020",
"noEmit": true,
"skipLibCheck": true,
"jsxImportSource": "preact",
"useDefineForClassFields": true,

/* modules */
"module": "ESNext",
"isolatedModules": true,
"resolveJsonModule": true,
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"paths": {
"react": ["./node_modules/preact/compat/"],
"react-dom": ["./node_modules/preact/compat/"]
},

/* type checking */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true
},
"include": ["src"]
}
22 changes: 22 additions & 0 deletions react-preact-runtime-typescript/shell/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "react-preact-runtime-typescript-shell",
"private": true,
"version": "1.0.0",
"scripts": {
"start": "rsbuild dev",
"build": "rsbuild build",
"preview": "rsbuild preview"
},
"dependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@module-federation/enhanced": "^0.8.4",
"@rsbuild/core": "^1.1.8",
"@rsbuild/plugin-react": "^1.0.7",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"typescript": "^5.7.2"
}
}
9 changes: 9 additions & 0 deletions react-preact-runtime-typescript/shell/rsbuild.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from '@rsbuild/core';
import { pluginReact } from '@rsbuild/plugin-react';

export default defineConfig({
plugins: [pluginReact()],
server: {
port: 3001,
},
});
55 changes: 55 additions & 0 deletions react-preact-runtime-typescript/shell/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useEffect } from 'react';
import { init, loadRemote } from '@module-federation/enhanced/runtime';

interface RemoteModule {
inject: (parentElementId: string) => void;
unmount: () => void;
}

init({
name: 'shell',
remotes: [
{
name: 'remote',
entry: 'http://localhost:3002/mf-manifest.json',
},
],
});

const parentElementId = 'parent';

const App = () => {
useEffect(() => {
let unmountRemote: () => void;

const loadRemoteApp = async () => {
try {
const module = await loadRemote<RemoteModule>('remote/appInjector');
if (!module) return;
const { inject, unmount } = module;
unmountRemote = unmount;

inject(parentElementId);
} catch (error) {
console.error('Error loading the Remote:', error);
}
};

loadRemoteApp();

return () => {
if (unmountRemote) unmountRemote();
};
}, []);

// Remote will be injected in the div with parentElementId
return (
<div>
<h1>Host Application - React</h1>
<h2>Remote</h2>
<div id={parentElementId} />
</div>
);
};

export default App;
13 changes: 13 additions & 0 deletions react-preact-runtime-typescript/shell/src/bootstrap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const rootEl = document.getElementById('root');
if (rootEl) {
const root = ReactDOM.createRoot(rootEl);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
}
1 change: 1 addition & 0 deletions react-preact-runtime-typescript/shell/src/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="@rsbuild/core/types" />
1 change: 1 addition & 0 deletions react-preact-runtime-typescript/shell/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import('./bootstrap');
23 changes: 23 additions & 0 deletions react-preact-runtime-typescript/shell/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"compilerOptions": {
"lib": ["DOM", "ES2020"],
"jsx": "react-jsx",
"target": "ES2020",
"noEmit": true,
"skipLibCheck": true,
"useDefineForClassFields": true,

/* modules */
"module": "ESNext",
"isolatedModules": true,
"resolveJsonModule": true,
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,

/* type checking */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true
},
"include": ["src"]
}

0 comments on commit f6d2449

Please sign in to comment.