add module-federation example

This commit is contained in:
Tobias Koppers 2020-06-04 02:48:12 +02:00
parent 3df380e2d7
commit bc723ebeb3
15 changed files with 2597 additions and 4 deletions

View File

@ -170,6 +170,9 @@
"prewalking",
"overridables",
"overridable",
"darkblue",
"darkgreen",
"darkred",
"webassemblyjs",
"fsevents",
@ -209,5 +212,5 @@
"dependabot"
],
"ignoreRegExpList": ["/Author.+/", "/data:.*/", "/\"mappings\":\".+\"/"],
"ignorePaths": ["**/dist/**"]
"ignorePaths": ["**/dist/**", "examples/**/README.md"]
}

View File

@ -12,9 +12,11 @@ const async = require("neo-async");
const extraArgs = "";
const targetArgs = global.NO_TARGET_ARGS ? "" : " ./example.js -o dist/output.js ";
const displayReasons = global.NO_REASONS ? "" : " --display-reasons --display-used-exports --display-provided-exports";
const commonArgs = `--display-chunks --no-color --display-max-modules 99999 --display-origins --output-public-path "dist/" ${extraArgs} ${targetArgs}`;
const targetArgs = global.NO_TARGET_ARGS ? "" : "./example.js -o dist/output.js ";
const displayReasons = global.NO_REASONS ? "" : "--display-reasons --display-used-exports --display-provided-exports";
const statsArgs = global.NO_STATS_OPTIONS ? "" : "--display-chunks --display-max-modules 99999 --display-origins";
const publicPathArgs = global.NO_PUBLIC_PATH ? "" : '--output-public-path "dist/"';
const commonArgs = `--no-color ${statsArgs} ${publicPathArgs} ${extraArgs} ${targetArgs}`;
let readme = fs.readFileSync(require("path").join(process.cwd(), "template.md"), "utf-8");

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
global.NO_TARGET_ARGS = true;
global.NO_REASONS = true;
global.NO_STATS_OPTIONS = true;
global.NO_PUBLIC_PATH = true;
require("../build-common");

View File

@ -0,0 +1,83 @@
<html>
<head>
<style>
.spinner {
font-size: 10px;
margin: 50px auto;
text-indent: -9999em;
width: 11em;
height: 11em;
border-radius: 50%;
background: #595959;
background: linear-gradient(
to right,
#595959 10%,
rgba(89, 89, 89, 0) 42%
);
position: relative;
animation: spin 1.4s infinite linear;
transform: translateZ(0);
}
.spinner:before {
width: 50%;
height: 50%;
background: #595959;
border-radius: 100% 0 0 0;
position: absolute;
top: 0;
left: 0;
content: "";
}
.spinner:after {
background: white;
width: 75%;
height: 75%;
border-radius: 50%;
content: "";
margin: auto;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
</style>
</head>
<body>
<!-- A spinner -->
<div class="spinner"></div>
<!-- This script only contains boostrapping logic -->
<!-- It will load all other scripts if neccessary -->
<script src="/dist/aaa/app.js" async></script>
<!-- These script tags are optional -->
<!-- They improve loading performance -->
<!-- Omitting them will add an additional round trip -->
<script src="/dist/bbb/mfeBBB.js" async></script>
<script src="/dist/ccc/mfeCCC.js" async></script>
<!-- All these scripts are pretty small ~5kb -->
<!-- For optimal performance they can be inlined -->
</body>
</html>

View File

@ -0,0 +1,18 @@
import React from "react";
import { formatRelative, subDays } from "date-fns";
// date-fns is a shared module, but used as usual
// exposing modules act as async boundary,
// so no additional async boundary need to be added here
// As data-fns is an shared module, it will be placed in a separate file
// It will be loaded in parallel to the code of this module
const Component = ({ locale }) => (
<div style={{ border: "5px solid darkblue" }}>
<p>I'm a Component exposed from container B!</p>
<p>
Using date-fn in Remote:{" "}
{formatRelative(subDays(new Date(), 2), new Date(), { locale })}
</p>
</div>
);
export default Component;

View File

@ -0,0 +1,13 @@
import React from "react";
import { formatRelative, subDays } from "date-fns";
const Component = ({ locale }) => (
<div style={{ border: "5px solid darkred" }}>
<p>I'm a Component exposed from container C!</p>
<p>
Using date-fn in Remote:{" "}
{formatRelative(subDays(new Date(), 3), new Date(), { locale })}
</p>
</div>
);
export default Component;

View File

@ -0,0 +1,11 @@
import React from "react";
import { random } from "lodash";
const Component = () => (
<div style={{ border: "5px solid darkgreen" }}>
<p>I'm a lazy Component exposed from container C!</p>
<p>I'm lazy loaded by the app and lazy load another component myself.</p>
<p>Using lodash in Remote: {random(0, 6)}</p>
</div>
);
export default Component;

View File

@ -0,0 +1,26 @@
import React from "react";
import ComponentB from "mfe-b/Component"; // <- these are remote modules,
import ComponentC from "mfe-c/Component"; // <- but they are used as usual packages
import { de } from "date-fns/locale";
// remote modules can also be used with import() which lazy loads them as usual
const ComponentD = React.lazy(() => import("mfe-c/Component2"));
const App = () => (
<article>
<header>
<h1>Hello World</h1>
</header>
<p>This component is from a remote container:</p>
<ComponentB locale={de} />
<p>And this component is from another remote container:</p>
<ComponentC locale={de} />
<React.Suspense fallback={<p>Lazy loading component...</p>}>
<p>
And this component is from this remote container too, but lazy loaded:
</p>
<ComponentD />
</React.Suspense>
</article>
);
export default App;

View File

@ -0,0 +1,11 @@
import ReactDom from "react-dom";
import React from "react"; // <- this is a shared module, but used as usual
import App from "./App";
// load app
const el = document.createElement("main");
ReactDom.render(<App />, el);
document.body.appendChild(el);
// remove spinner
document.body.removeChild(document.getElementsByClassName("spinner")[0]);

View File

@ -0,0 +1,13 @@
// Sharing modules requires that all remotes are initialized
// and can provide shared modules to the common scope
// As this is an async operation we need an async boundary (import())
// Using modules from remotes is also an async operation
// as chunks need to be loaded for the code of the remote module
// This also requires an async boundary (import())
// At this point shared modules initialized and remote modules are loaded
import("./bootstrap");
// It's possible to place more code here to do stuff on page init
// but it can't use any of the shared modules or remote modules.

View File

@ -0,0 +1,61 @@
# webpack.config.js
```javascript
_{{webpack.config.js}}_
```
# src/index.js
```javascript
_{{src/index.js}}_
```
# src/bootstrap.js
```jsx
_{{src/bootstrap.js}}_
```
# src/App.js
```jsx
_{{src/App.js}}_
```
# src-b/Component.js
```jsx
_{{src-b/Component.js}}_
```
# dist/aaa/app.js
```javascript
_{{dist/aaa/app.js}}_
```
# dist/bbb/mfeBBB.js
```javascript
_{{dist/bbb/mfeBBB.js}}_
```
# dist/ccc/mfeCCC.js
```javascript
_{{dist/ccc/mfeCCC.js}}_
```
# Info
## Unoptimized
```
_{{stdout}}_
```
## Production mode
```
_{{production:stdout}}_
```

View File

@ -0,0 +1,133 @@
const path = require("path");
const { ModuleFederationPlugin } = require("../../").container;
const devDeps = require("../../package.json").devDependencies;
const rules = [
{
test: /\.js$/,
include: path.resolve(__dirname, "src"),
use: {
loader: "babel-loader",
options: {
presets: ["@babel/react"]
}
}
}
];
const optimization = {
chunkIds: "named", // for this example only: readable filenames in production too
nodeEnv: "production" // for this example only: always production version of react
};
const stats = {
chunks: true,
modules: false,
chunkModules: false,
chunkRootModules: true,
chunkOrigins: true
};
module.exports = (env = "development") => [
{
name: "app",
mode: env,
entry: {
app: "./src/index.js"
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist/aaa"),
publicPath: "dist/aaa/",
// Each build needs a unique name
// to avoid runtime collisions
// The default uses "name" from package.json
uniqueName: "module-federation-aaa"
},
module: { rules },
optimization,
plugins: [
new ModuleFederationPlugin({
// List of remotes with URLs
remotes: {
"mfe-b": "mfeBBB@/dist/bbb/mfeBBB.js",
"mfe-c": "mfeCCC@/dist/ccc/mfeCCC.js"
},
// list of shared modules with version requirement and other options
shared: {
react: {
singleton: true, // make sure only a single react module is used
requiredVersion: devDeps.react // e. g. "^16.8.0"
}
}
})
],
stats
},
{
name: "mfe-b",
mode: env,
entry: {},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist/bbb"),
publicPath: "dist/bbb/",
uniqueName: "module-federation-bbb"
},
module: { rules },
optimization,
plugins: [
new ModuleFederationPlugin({
// A unique name
name: "mfeBBB",
// List of exposed modules
exposes: {
"./Component": "./src-b/Component"
},
// list of shared modules with version requirement and other options
// Here date-fns is shared with the other remote, host doesn't know about that
shared: {
"date-fns": devDeps["date-fns"], // e. g. "^2.12.0"
react: {
singleton: true, // must be specified in each config
requiredVersion: devDeps.react
}
}
})
],
stats
},
{
name: "mfe-c",
mode: env,
entry: {},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist/ccc"),
publicPath: "dist/ccc/",
uniqueName: "module-federation-ccc"
},
module: { rules },
optimization,
plugins: [
new ModuleFederationPlugin({
name: "mfeCCC",
exposes: {
"./Component": "./src-c/Component",
"./Component2": "./src-c/LazyComponent"
},
shared: {
"date-fns": devDeps["date-fns"],
lodash: devDeps["lodash"],
react: {
singleton: true,
requiredVersion: devDeps.react
}
}
})
],
stats
}
];

View File

@ -30,6 +30,7 @@
},
"devDependencies": {
"@babel/core": "^7.7.2",
"@babel/preset-react": "^7.10.1",
"@types/jest": "^25.1.5",
"@types/node": "^12.6.9",
"babel-loader": "^8.0.6",

View File

@ -41,6 +41,30 @@
lodash "^4.17.13"
source-map "^0.5.0"
"@babel/helper-annotate-as-pure@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.1.tgz#f6d08acc6f70bbd59b436262553fb2e259a1a268"
integrity sha512-ewp3rvJEwLaHgyWGe4wQssC2vjks3E80WiUe2BpMb0KhreTjMROCbxXcEovTrbeGVdQct5VjQfrv9EgC+xMzCw==
dependencies:
"@babel/types" "^7.10.1"
"@babel/helper-builder-react-jsx-experimental@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.10.1.tgz#9a7d58ad184d3ac3bafb1a452cec2bad7e4a0bc8"
integrity sha512-irQJ8kpQUV3JasXPSFQ+LCCtJSc5ceZrPFVj6TElR6XCHssi3jV8ch3odIrNtjJFRZZVbrOEfJMI79TPU/h1pQ==
dependencies:
"@babel/helper-annotate-as-pure" "^7.10.1"
"@babel/helper-module-imports" "^7.10.1"
"@babel/types" "^7.10.1"
"@babel/helper-builder-react-jsx@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.1.tgz#a327f0cf983af5554701b1215de54a019f09b532"
integrity sha512-KXzzpyWhXgzjXIlJU1ZjIXzUPdej1suE6vzqgImZ/cpAsR/CC8gUcX4EWRmDfWz/cs6HOCPMBIJ3nKoXt3BFuw==
dependencies:
"@babel/helper-annotate-as-pure" "^7.10.1"
"@babel/types" "^7.10.1"
"@babel/helper-function-name@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.1.tgz#92bd63829bfc9215aca9d9defa85f56b539454f4"
@ -96,6 +120,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250"
integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==
"@babel/helper-plugin-utils@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.1.tgz#ec5a5cf0eec925b66c60580328b122c01230a127"
integrity sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA==
"@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670"
@ -182,6 +211,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.0"
"@babel/plugin-syntax-jsx@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.10.1.tgz#0ae371134a42b91d5418feb3c8c8d43e1565d2da"
integrity sha512-+OxyOArpVFXQeXKLO9o+r2I4dIoVoy6+Uu0vKELrlweDM3QJADZj+Z+5ERansZqIZBcLj42vHnDI8Rz9BnRIuQ==
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
"@babel/plugin-syntax-logical-assignment-operators@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.8.3.tgz#3995d7d7ffff432f6ddc742b47e730c054599897"
@ -224,6 +260,69 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.0"
"@babel/plugin-transform-react-display-name@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.10.1.tgz#e6a33f6d48dfb213dda5e007d0c7ff82b6a3d8ef"
integrity sha512-rBjKcVwjk26H3VX8pavMxGf33LNlbocMHdSeldIEswtQ/hrjyTG8fKKILW1cSkODyRovckN/uZlGb2+sAV9JUQ==
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
"@babel/plugin-transform-react-jsx-development@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.10.1.tgz#1ac6300d8b28ef381ee48e6fec430cc38047b7f3"
integrity sha512-XwDy/FFoCfw9wGFtdn5Z+dHh6HXKHkC6DwKNWpN74VWinUagZfDcEJc3Y8Dn5B3WMVnAllX8Kviaw7MtC5Epwg==
dependencies:
"@babel/helper-builder-react-jsx-experimental" "^7.10.1"
"@babel/helper-plugin-utils" "^7.10.1"
"@babel/plugin-syntax-jsx" "^7.10.1"
"@babel/plugin-transform-react-jsx-self@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.10.1.tgz#22143e14388d72eb88649606bb9e46f421bc3821"
integrity sha512-4p+RBw9d1qV4S749J42ZooeQaBomFPrSxa9JONLHJ1TxCBo3TzJ79vtmG2S2erUT8PDDrPdw4ZbXGr2/1+dILA==
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
"@babel/plugin-syntax-jsx" "^7.10.1"
"@babel/plugin-transform-react-jsx-source@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.10.1.tgz#30db3d4ee3cdebbb26a82a9703673714777a4273"
integrity sha512-neAbaKkoiL+LXYbGDvh6PjPG+YeA67OsZlE78u50xbWh2L1/C81uHiNP5d1fw+uqUIoiNdCC8ZB+G4Zh3hShJA==
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
"@babel/plugin-syntax-jsx" "^7.10.1"
"@babel/plugin-transform-react-jsx@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.10.1.tgz#91f544248ba131486decb5d9806da6a6e19a2896"
integrity sha512-MBVworWiSRBap3Vs39eHt+6pJuLUAaK4oxGc8g+wY+vuSJvLiEQjW1LSTqKb8OUPtDvHCkdPhk7d6sjC19xyFw==
dependencies:
"@babel/helper-builder-react-jsx" "^7.10.1"
"@babel/helper-builder-react-jsx-experimental" "^7.10.1"
"@babel/helper-plugin-utils" "^7.10.1"
"@babel/plugin-syntax-jsx" "^7.10.1"
"@babel/plugin-transform-react-pure-annotations@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.10.1.tgz#f5e7c755d3e7614d4c926e144f501648a5277b70"
integrity sha512-mfhoiai083AkeewsBHUpaS/FM1dmUENHBMpS/tugSJ7VXqXO5dCN1Gkint2YvM1Cdv1uhmAKt1ZOuAjceKmlLA==
dependencies:
"@babel/helper-annotate-as-pure" "^7.10.1"
"@babel/helper-plugin-utils" "^7.10.1"
"@babel/preset-react@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.10.1.tgz#e2ab8ae9a363ec307b936589f07ed753192de041"
integrity sha512-Rw0SxQ7VKhObmFjD/cUcKhPTtzpeviEFX1E6PgP+cYOhQ98icNqtINNFANlsdbQHrmeWnqdxA4Tmnl1jy5tp3Q==
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
"@babel/plugin-transform-react-display-name" "^7.10.1"
"@babel/plugin-transform-react-jsx" "^7.10.1"
"@babel/plugin-transform-react-jsx-development" "^7.10.1"
"@babel/plugin-transform-react-jsx-self" "^7.10.1"
"@babel/plugin-transform-react-jsx-source" "^7.10.1"
"@babel/plugin-transform-react-pure-annotations" "^7.10.1"
"@babel/runtime@^7.6.3":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.4.tgz#d79f5a2040f7caa24d53e563aad49cbc05581308"