├── .vscode
└── settings.json
├── host-5001
├── .eslintignore
├── src
│ ├── index.tsx
│ ├── reset.css
│ ├── bootstrap.tsx
│ ├── types.d.ts
│ ├── ErrorBoundary.tsx
│ └── App.tsx
├── public
│ ├── assets
│ │ └── app-code.png
│ └── index.html
├── .prettierrc
├── tsconfig.json
├── .gitignore
├── package.json
├── .eslintrc.js
└── webpack.config.js
└── remote-5002
├── .eslintignore
├── src
├── expose
│ ├── customCalc.ts
│ ├── Button.tsx
│ └── Button.module.scss
├── index.tsx
├── types.d.ts
├── bootstrap.tsx
└── DemoStand.tsx
├── public
├── assets
│ ├── holybg.png
│ ├── people.png
│ ├── button-code.png
│ └── fonts
│ │ └── open-sans
│ │ ├── Bold
│ │ ├── OpenSans-Bold.woff
│ │ └── OpenSans-Bold.woff2
│ │ ├── Italic
│ │ ├── OpenSans-Italic.woff
│ │ └── OpenSans-Italic.woff2
│ │ └── Regular
│ │ ├── OpenSans-Regular.woff
│ │ └── OpenSans-Regular.woff2
└── index.html
├── .prettierrc
├── tsconfig.json
├── .gitignore
├── package.json
├── .eslintrc.js
└── webpack.config.js
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.workingDirectories": [ "./host-5001", "./remote-5002" ]
3 | }
--------------------------------------------------------------------------------
/host-5001/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist/*
3 | __generated__
4 | webpack.config.js
5 | .eslintrc.js
--------------------------------------------------------------------------------
/remote-5002/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist/*
3 | __generated__
4 | webpack.config.js
5 | .eslintrc.js
--------------------------------------------------------------------------------
/host-5001/src/index.tsx:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | import bootstrap from './bootstrap';
3 |
4 | bootstrap(() => {});
5 |
--------------------------------------------------------------------------------
/host-5001/src/reset.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #80f3ff;
3 | color: #004f90;
4 | height: 100vh;
5 | }
--------------------------------------------------------------------------------
/remote-5002/src/expose/customCalc.ts:
--------------------------------------------------------------------------------
1 | export default function customCalc(n: number) {
2 | return n + 1000;
3 | }
4 |
--------------------------------------------------------------------------------
/remote-5002/src/index.tsx:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | import bootstrap from './bootstrap';
3 |
4 | bootstrap(() => {});
5 |
--------------------------------------------------------------------------------
/host-5001/public/assets/app-code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nodkz/module-federation-demo/HEAD/host-5001/public/assets/app-code.png
--------------------------------------------------------------------------------
/remote-5002/public/assets/holybg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nodkz/module-federation-demo/HEAD/remote-5002/public/assets/holybg.png
--------------------------------------------------------------------------------
/remote-5002/public/assets/people.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nodkz/module-federation-demo/HEAD/remote-5002/public/assets/people.png
--------------------------------------------------------------------------------
/remote-5002/public/assets/button-code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nodkz/module-federation-demo/HEAD/remote-5002/public/assets/button-code.png
--------------------------------------------------------------------------------
/host-5001/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "arrowParens": "always",
4 | "tabWidth": 2,
5 | "useTabs": false,
6 | "printWidth": 100,
7 | "trailingComma": "es5"
8 | }
--------------------------------------------------------------------------------
/remote-5002/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "arrowParens": "always",
4 | "tabWidth": 2,
5 | "useTabs": false,
6 | "printWidth": 100,
7 | "trailingComma": "es5"
8 | }
--------------------------------------------------------------------------------
/remote-5002/public/assets/fonts/open-sans/Bold/OpenSans-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nodkz/module-federation-demo/HEAD/remote-5002/public/assets/fonts/open-sans/Bold/OpenSans-Bold.woff
--------------------------------------------------------------------------------
/remote-5002/public/assets/fonts/open-sans/Bold/OpenSans-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nodkz/module-federation-demo/HEAD/remote-5002/public/assets/fonts/open-sans/Bold/OpenSans-Bold.woff2
--------------------------------------------------------------------------------
/remote-5002/src/types.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'public';
2 | declare module '*.less';
3 | declare module '*.scss';
4 | declare module '*.css';
5 | declare module '*.png';
6 | declare module '*.svg';
7 |
--------------------------------------------------------------------------------
/remote-5002/public/assets/fonts/open-sans/Italic/OpenSans-Italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nodkz/module-federation-demo/HEAD/remote-5002/public/assets/fonts/open-sans/Italic/OpenSans-Italic.woff
--------------------------------------------------------------------------------
/remote-5002/public/assets/fonts/open-sans/Italic/OpenSans-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nodkz/module-federation-demo/HEAD/remote-5002/public/assets/fonts/open-sans/Italic/OpenSans-Italic.woff2
--------------------------------------------------------------------------------
/remote-5002/public/assets/fonts/open-sans/Regular/OpenSans-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nodkz/module-federation-demo/HEAD/remote-5002/public/assets/fonts/open-sans/Regular/OpenSans-Regular.woff
--------------------------------------------------------------------------------
/remote-5002/public/assets/fonts/open-sans/Regular/OpenSans-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nodkz/module-federation-demo/HEAD/remote-5002/public/assets/fonts/open-sans/Regular/OpenSans-Regular.woff2
--------------------------------------------------------------------------------
/remote-5002/src/bootstrap.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { DemoStand } from './DemoStand';
4 |
5 | ReactDOM.render(, document.getElementById('root'));
6 |
--------------------------------------------------------------------------------
/host-5001/src/bootstrap.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { App } from './App';
4 | import './reset.css';
5 |
6 | ReactDOM.render(, document.getElementById('root'));
7 |
--------------------------------------------------------------------------------
/host-5001/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "sourceMap": true,
5 | "esModuleInterop": true,
6 | "jsx": "react"
7 | },
8 | "include": ["src/**/*"]
9 | }
10 |
--------------------------------------------------------------------------------
/remote-5002/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "sourceMap": true,
5 | "esModuleInterop": true,
6 | "jsx": "react"
7 | },
8 | "include": ["src/**/*"]
9 | }
10 |
--------------------------------------------------------------------------------
/remote-5002/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/host-5001/src/types.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'public';
2 | declare module '*.less';
3 | declare module '*.scss';
4 | declare module '*.css';
5 | declare module '*.png';
6 | declare module '*.svg';
7 |
8 | // See more about Module Federation & TypeScript in the following video
9 | // https://www.youtube.com/watch?v=UbEx1v26kCs
10 | declare module 'remote5002/*';
11 |
--------------------------------------------------------------------------------
/remote-5002/src/expose/Button.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import s from './Button.module.scss';
3 |
4 | interface P {
5 | text: string;
6 | onClick?: () => any;
7 | }
8 |
9 | export default function Button({ text, onClick }: P) {
10 | return (
11 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/host-5001/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/remote-5002/src/DemoStand.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Button from './expose/Button';
3 |
4 | export function DemoStand() {
5 | return (
6 |
7 |
8 | Remote5002 APP which exposes <Button /> component with styles, fonts & images to
9 | other apps.
10 |
11 |
12 |
13 |
14 |
15 |
16 |
.default})
17 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/host-5001/src/ErrorBoundary.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export class ErrorBoundary extends React.Component<
4 | { children: React.ReactNode },
5 | { hasError: boolean }
6 | > {
7 | constructor(props) {
8 | super(props);
9 | this.state = { hasError: false };
10 | }
11 |
12 | static getDerivedStateFromError() {
13 | return { hasError: true };
14 | }
15 |
16 | componentDidCatch(error, errorInfo) {
17 | // You can also log the error to an error reporting service
18 | // logErrorToMyService(error, errorInfo);
19 | }
20 |
21 | render() {
22 | if (this.state.hasError) {
23 | return Remote is not available.
;
24 | }
25 |
26 | return this.props.children;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/host-5001/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | coverage
10 | cypress/videos
11 | cypress/screenshots
12 | wdio/report/*
13 | wdio/node_modules/*
14 | wdio/allure-report/*
15 | wdio/junit/*
16 | wdioreport.*
17 | wdio.idea/*
18 |
19 | # production
20 | build/
21 | dist/
22 |
23 | # misc
24 | .DS_Store
25 | .env.local
26 | .env.development.local
27 | .env.test.local
28 | .env.production.local
29 |
30 | npm-debug.log*
31 | yarn-debug.log*
32 | yarn-error.log*
33 |
34 | # ignore backup files, which generated by bin/scripts/calc-yarn-lock-hash.sh on MacOS
35 | .gitlab-ci.yml-*
36 |
37 | .next
38 |
39 | __generated__
40 | .yarn-cache
41 |
42 | tsconfig.build.tsbuildinfo
43 | APP_IMAGE.txt
44 | APP_NAME.txt
45 | APP_REVISION.txt
46 | .react-intl
47 |
--------------------------------------------------------------------------------
/remote-5002/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | coverage
10 | cypress/videos
11 | cypress/screenshots
12 | wdio/report/*
13 | wdio/node_modules/*
14 | wdio/allure-report/*
15 | wdio/junit/*
16 | wdioreport.*
17 | wdio.idea/*
18 |
19 | # production
20 | build/
21 | dist/
22 |
23 | # misc
24 | .DS_Store
25 | .env.local
26 | .env.development.local
27 | .env.test.local
28 | .env.production.local
29 |
30 | npm-debug.log*
31 | yarn-debug.log*
32 | yarn-error.log*
33 |
34 | # ignore backup files, which generated by bin/scripts/calc-yarn-lock-hash.sh on MacOS
35 | .gitlab-ci.yml-*
36 |
37 | .next
38 |
39 | __generated__
40 | .yarn-cache
41 |
42 | tsconfig.build.tsbuildinfo
43 | APP_IMAGE.txt
44 | APP_NAME.txt
45 | APP_REVISION.txt
46 | .react-intl
47 |
--------------------------------------------------------------------------------
/remote-5002/src/expose/Button.module.scss:
--------------------------------------------------------------------------------
1 | .button {
2 | font-family: 'Open Sans';
3 | background: url(public/assets/holybg.png);
4 | background-size: cover;
5 | font-size: 100px;
6 | margin: 30px;
7 | padding: 5px;
8 | color: #fff;
9 | border: 5px solid #226fa2;
10 | outline: 0;
11 |
12 | &:hover {
13 | color: #ccc;
14 | border-color: #941e84;
15 | }
16 |
17 | &:active {
18 | border-color: #BBB;
19 | }
20 | }
21 |
22 | @font-face {
23 | font-family: 'Open Sans';
24 | font-style: normal;
25 | font-weight: 300;
26 | src: local('Open Sans'), local('OpenSans-Regular'),
27 | url('public/assets/fonts/open-sans/Regular/OpenSans-Regular.woff2') format('woff2'),
28 | url('public/assets/fonts/open-sans/Regular/OpenSans-Regular.woff') format('woff');
29 | }
30 | @font-face {
31 | font-family: 'Open Sans';
32 | font-style: normal;
33 | font-weight: 600;
34 | src: local('Open Sans Semibold'), local('OpenSans-Semibold'),
35 | url('public/assets/fonts/open-sans/Bold/OpenSans-Bold.woff2') format('woff2'),
36 | url('public/assets/fonts/open-sans/Bold/OpenSans-Bold.woff') format('woff');
37 | }
--------------------------------------------------------------------------------
/host-5001/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { ErrorBoundary } from './ErrorBoundary';
3 |
4 | const RemoteButton = React.lazy(() => import('remote5002/Button'));
5 |
6 | export function App() {
7 | const [cnt, setCnt] = useState(0);
8 |
9 | return (
10 |
11 |
Host Application
12 |
13 |
14 | App imports <Button /> from another domain at runtime,
15 | and controls its props & behavior.
16 |
17 |
18 |
19 |
20 | {
23 | setCnt(cnt + 1);
24 | }}
25 | // // Click with custom remote logic:
26 | // onClick={() => {
27 | // import('remote5002/customCalc')
28 | // .then((m) => m.default)
29 | // .then((f) => {
30 | // setCnt(f(cnt + 1));
31 | // });
32 | // }}
33 | />
34 |
35 |
36 |
37 |
38 |
Code example:
39 |
.default})
40 |
41 |
42 | );
43 | }
44 |
--------------------------------------------------------------------------------
/host-5001/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "host-3001",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "devDependencies": {
7 | "@module-federation/dashboard-plugin": "^1.1.0",
8 | "@types/react": "16.9.56",
9 | "@types/react-dom": "16.9.9",
10 | "@typescript-eslint/eslint-plugin": "4.7.0",
11 | "@typescript-eslint/parser": "4.7.0",
12 | "bundle-loader": "0.5.6",
13 | "css-loader": "5.0.1",
14 | "eslint": "7.13.0",
15 | "eslint-config-prettier": "6.15.0",
16 | "eslint-plugin-prettier": "3.1.4",
17 | "file-loader": "6.2.0",
18 | "html-webpack-plugin": "4.5.0",
19 | "mini-css-extract-plugin": "1.3.0",
20 | "nodemon": "^2.0.6",
21 | "prettier": "2.1.2",
22 | "sass": "^1.27.0",
23 | "sass-loader": "^10.0.4",
24 | "style-loader": "2.0.0",
25 | "ts-loader": "8.0.11",
26 | "typescript": "^4.0.3",
27 | "url-loader": "^4.1.1",
28 | "webpack": "5.4.0",
29 | "webpack-cli": "4.2.0",
30 | "webpack-dev-server": "3.11.0"
31 | },
32 | "scripts": {
33 | "dev": "yarn && nodemon --exec \"webpack-cli serve\" --delay 1 --watch webpack.config.js --watch tsconfig.json",
34 | "build": "yarn clean && NODE_ENV=production webpack --mode production",
35 | "start": "serve dist -p 5001 --cors --single",
36 | "serve": "yarn start",
37 | "clean": "rm -rf dist"
38 | },
39 | "dependencies": {
40 | "react": "^17.0.1",
41 | "react-dom": "^17.0.1",
42 | "serve": "^11.3.2"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/remote-5002/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "remote-3002",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "devDependencies": {
7 | "@module-federation/dashboard-plugin": "^1.1.0",
8 | "@types/react": "16.9.56",
9 | "@types/react-dom": "16.9.9",
10 | "@typescript-eslint/eslint-plugin": "4.7.0",
11 | "@typescript-eslint/parser": "4.7.0",
12 | "bundle-loader": "0.5.6",
13 | "css-loader": "5.0.1",
14 | "eslint": "7.13.0",
15 | "eslint-config-prettier": "6.15.0",
16 | "eslint-plugin-prettier": "3.1.4",
17 | "file-loader": "6.2.0",
18 | "html-webpack-plugin": "4.5.0",
19 | "mini-css-extract-plugin": "1.3.0",
20 | "nodemon": "^2.0.6",
21 | "prettier": "2.1.2",
22 | "sass": "^1.27.0",
23 | "sass-loader": "^10.0.4",
24 | "style-loader": "2.0.0",
25 | "ts-loader": "8.0.11",
26 | "typescript": "^4.0.3",
27 | "url-loader": "^4.1.1",
28 | "webpack": "5.4.0",
29 | "webpack-cli": "4.2.0",
30 | "webpack-dev-server": "3.11.0"
31 | },
32 | "scripts": {
33 | "dev": "yarn && nodemon --exec \"webpack-cli serve\" --delay 1 --watch webpack.config.js --watch tsconfig.json",
34 | "build": "yarn clean && NODE_ENV=production webpack --mode production",
35 | "start": "serve dist -p 5002 --cors --single",
36 | "serve": "yarn start",
37 | "clean": "rm -rf dist"
38 | },
39 | "dependencies": {
40 | "react": "^17.0.1",
41 | "react-dom": "^17.0.1",
42 | "serve": "^11.3.2"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/host-5001/.eslintrc.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | parser: '@typescript-eslint/parser',
5 | plugins: ['@typescript-eslint', 'prettier'],
6 | extends: [
7 | 'plugin:@typescript-eslint/recommended',
8 | 'prettier/@typescript-eslint',
9 | 'plugin:prettier/recommended',
10 | ],
11 | parserOptions: {
12 | sourceType: 'module',
13 | useJSXTextNode: true,
14 | project: [path.resolve(__dirname, 'tsconfig.json')],
15 | },
16 | rules: {
17 | 'no-underscore-dangle': 0,
18 | 'arrow-body-style': 0,
19 | 'no-unused-expressions': 1,
20 | 'no-plusplus': 0,
21 | 'no-console': 0,
22 | 'func-names': 0,
23 | 'comma-dangle': [
24 | 'error',
25 | {
26 | arrays: 'always-multiline',
27 | objects: 'always-multiline',
28 | imports: 'always-multiline',
29 | exports: 'always-multiline',
30 | functions: 'never',
31 | },
32 | ],
33 | 'no-prototype-builtins': 0,
34 | 'prefer-destructuring': 0,
35 | 'no-else-return': 1,
36 | 'lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true }],
37 | '@typescript-eslint/explicit-member-accessibility': 0,
38 | '@typescript-eslint/explicit-module-boundary-types': 0,
39 | '@typescript-eslint/no-explicit-any': 0,
40 | '@typescript-eslint/no-inferrable-types': 0,
41 | '@typescript-eslint/explicit-function-return-type': 0,
42 | '@typescript-eslint/no-use-before-define': 0,
43 | '@typescript-eslint/no-empty-function': 0,
44 | '@typescript-eslint/ban-ts-comment': 0,
45 | '@typescript-eslint/no-var-requires': 0,
46 | curly: ['error', 'all'],
47 | 'padding-line-between-statements': [
48 | 'warn',
49 | {
50 | blankLine: 'always',
51 | prev: '*',
52 | next: 'return', // add blank line *before* all returns (if there are statements before)
53 | },
54 | {
55 | blankLine: 'always',
56 | prev: '*',
57 | next: 'if', // add blank line *before* all ifs
58 | },
59 | {
60 | blankLine: 'always',
61 | prev: 'if',
62 | next: '*', // add blank line *after* all ifs
63 | },
64 | {
65 | blankLine: 'any',
66 | prev: 'if',
67 | next: 'if', // allow blank line between ifs, but not enforce either
68 | },
69 | {
70 | blankLine: 'always',
71 | prev: '*',
72 | next: ['function', 'class'], // add blank line *before* all functions and classes
73 | },
74 | {
75 | blankLine: 'always',
76 | prev: ['function', 'class'],
77 | next: '*', // add blank line *after* all functions and classes
78 | },
79 | {
80 | blankLine: 'always',
81 | prev: '*',
82 | next: 'import', // add blank line *before* all imports
83 | },
84 | {
85 | blankLine: 'always',
86 | prev: 'import',
87 | next: '*', // add blank line *after* all imports
88 | },
89 | {
90 | blankLine: 'never',
91 | prev: 'import',
92 | next: 'import', // dont allow blank line between imports
93 | },
94 | ],
95 | },
96 | env: {
97 | node: true,
98 | jest: true,
99 | },
100 | };
101 |
--------------------------------------------------------------------------------
/remote-5002/.eslintrc.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | parser: '@typescript-eslint/parser',
5 | plugins: ['@typescript-eslint', 'prettier'],
6 | extends: [
7 | 'plugin:@typescript-eslint/recommended',
8 | 'prettier/@typescript-eslint',
9 | 'plugin:prettier/recommended',
10 | ],
11 | parserOptions: {
12 | sourceType: 'module',
13 | useJSXTextNode: true,
14 | project: [path.resolve(__dirname, 'tsconfig.json')],
15 | },
16 | rules: {
17 | 'no-underscore-dangle': 0,
18 | 'arrow-body-style': 0,
19 | 'no-unused-expressions': 1,
20 | 'no-plusplus': 0,
21 | 'no-console': 0,
22 | 'func-names': 0,
23 | 'comma-dangle': [
24 | 'error',
25 | {
26 | arrays: 'always-multiline',
27 | objects: 'always-multiline',
28 | imports: 'always-multiline',
29 | exports: 'always-multiline',
30 | functions: 'never',
31 | },
32 | ],
33 | 'no-prototype-builtins': 0,
34 | 'prefer-destructuring': 0,
35 | 'no-else-return': 1,
36 | 'lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true }],
37 | '@typescript-eslint/explicit-member-accessibility': 0,
38 | '@typescript-eslint/explicit-module-boundary-types': 0,
39 | '@typescript-eslint/no-explicit-any': 0,
40 | '@typescript-eslint/no-inferrable-types': 0,
41 | '@typescript-eslint/explicit-function-return-type': 0,
42 | '@typescript-eslint/no-use-before-define': 0,
43 | '@typescript-eslint/no-empty-function': 0,
44 | '@typescript-eslint/ban-ts-comment': 0,
45 | '@typescript-eslint/no-var-requires': 0,
46 | curly: ['error', 'all'],
47 | 'padding-line-between-statements': [
48 | 'warn',
49 | {
50 | blankLine: 'always',
51 | prev: '*',
52 | next: 'return', // add blank line *before* all returns (if there are statements before)
53 | },
54 | {
55 | blankLine: 'always',
56 | prev: '*',
57 | next: 'if', // add blank line *before* all ifs
58 | },
59 | {
60 | blankLine: 'always',
61 | prev: 'if',
62 | next: '*', // add blank line *after* all ifs
63 | },
64 | {
65 | blankLine: 'any',
66 | prev: 'if',
67 | next: 'if', // allow blank line between ifs, but not enforce either
68 | },
69 | {
70 | blankLine: 'always',
71 | prev: '*',
72 | next: ['function', 'class'], // add blank line *before* all functions and classes
73 | },
74 | {
75 | blankLine: 'always',
76 | prev: ['function', 'class'],
77 | next: '*', // add blank line *after* all functions and classes
78 | },
79 | {
80 | blankLine: 'always',
81 | prev: '*',
82 | next: 'import', // add blank line *before* all imports
83 | },
84 | {
85 | blankLine: 'always',
86 | prev: 'import',
87 | next: '*', // add blank line *after* all imports
88 | },
89 | {
90 | blankLine: 'never',
91 | prev: 'import',
92 | next: 'import', // dont allow blank line between imports
93 | },
94 | ],
95 | },
96 | env: {
97 | node: true,
98 | jest: true,
99 | },
100 | };
101 |
--------------------------------------------------------------------------------
/host-5001/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const HtmlWebpackPlugin = require('html-webpack-plugin');
3 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
4 | const webpack = require('webpack');
5 | const ModuleFederationPlugin = webpack.container.ModuleFederationPlugin;
6 | const DashboardPlugin = require('@module-federation/dashboard-plugin');
7 |
8 | const isProduction = process.env.NODE_ENV === 'production';
9 |
10 | module.exports = {
11 | entry: './src/index',
12 | mode: isProduction ? 'production' : 'development',
13 | devServer: {
14 | contentBase: path.join(__dirname, 'dist'),
15 | allowedHosts: ['localhost'],
16 | port: 5001,
17 | headers: {
18 | 'Access-Control-Allow-Origin': '*',
19 | 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
20 | 'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization',
21 | },
22 | historyApiFallback: {
23 | index: 'index.html', // open index page for any missing route
24 | },
25 | },
26 | output: {
27 | filename: 'assets/js/[name].[chunkhash].js',
28 | publicPath: 'auto', // must be auto for Module Federation!
29 | crossOriginLoading: 'anonymous',
30 | },
31 | resolve: {
32 | extensions: ['.ts', '.tsx', '.js', '.less'],
33 | alias: {
34 | public: path.resolve(__dirname, 'public/'),
35 | },
36 | },
37 | module: {
38 | rules: [
39 | {
40 | // https://github.com/webpack/webpack/issues/11467#issuecomment-691702706
41 | // https://github.com/vercel/next.js/pull/17095
42 | test: /\.m?js/,
43 | resolve: {
44 | fullySpecified: false,
45 | },
46 | },
47 | {
48 | test: /bootstrap\.tsx$/,
49 | loader: 'bundle-loader',
50 | options: {
51 | lazy: true,
52 | },
53 | },
54 | {
55 | test: /\.tsx?$/,
56 | loader: 'ts-loader',
57 | exclude: /node_modules/,
58 | },
59 | {
60 | test: /\.(png|jpe?g|gif|svg)$/i,
61 | use: [
62 | {
63 | loader: 'file-loader?name=assets/image/[name]-[hash].[ext]',
64 | },
65 | ],
66 | },
67 | {
68 | test: /\.(eot|woff|woff2|ttf)$/,
69 | use: {
70 | loader: 'url-loader?limit=30000&name=assets/fonts/[name]-[hash].[ext]',
71 | },
72 | },
73 | {
74 | test: /\.scss|\.css$/,
75 | use: [
76 | isProduction
77 | ? {
78 | loader: MiniCssExtractPlugin.loader,
79 | options: {
80 | publicPath: '', // fix at build Error: Automatic publicPath is not supported in this browser
81 | },
82 | }
83 | : 'style-loader',
84 | {
85 | loader: 'css-loader',
86 | options: {
87 | modules: {
88 | auto: /\.module\.\w+$/i,
89 | localIdentName: isProduction ? '[hash:base64]' : '[local]---[path][name]',
90 | },
91 | sourceMap: !isProduction,
92 | },
93 | },
94 | {
95 | loader: 'sass-loader',
96 | options: {
97 | sourceMap: !isProduction,
98 | },
99 | },
100 | ],
101 | },
102 | ],
103 | },
104 | plugins: [
105 | new ModuleFederationPlugin({
106 | name: 'host5001',
107 | // library: { type: 'var', name: 'host5001' },
108 | // filename: 'remoteEntry.js',
109 | remotes: {
110 | /* ✨✨✨ `@` is an undocumented DamnMagic! 🤯 */
111 | remote5002: 'remote5002@',
112 | },
113 | shared: {
114 | react: {
115 | requiredVersion: '17.0.1',
116 | singleton: true,
117 | },
118 | },
119 | }),
120 | new HtmlWebpackPlugin({
121 | template: './public/index.html',
122 | publicPath: '/',
123 | }),
124 |
125 | // Run docker run -p 3000:3000 -it scriptedalchemy/mf-dashboard:latest
126 | new DashboardPlugin({
127 | dashboardURL: 'http://localhost:3000/api/update',
128 | }),
129 | isProduction
130 | ? new MiniCssExtractPlugin({
131 | filename: 'assets/css/[name].css',
132 | })
133 | : false,
134 | ].filter(Boolean),
135 | };
136 |
--------------------------------------------------------------------------------
/remote-5002/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const HtmlWebpackPlugin = require('html-webpack-plugin');
3 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
4 | const webpack = require('webpack');
5 | const ModuleFederationPlugin = webpack.container.ModuleFederationPlugin;
6 | const DashboardPlugin = require('@module-federation/dashboard-plugin');
7 |
8 | const isProduction = process.env.NODE_ENV === 'production';
9 |
10 | module.exports = {
11 | entry: './src/index',
12 | mode: isProduction ? 'production' : 'development',
13 | devServer: {
14 | contentBase: path.join(__dirname, 'dist'),
15 | // allowedHosts: ['localhost'],
16 | disableHostCheck: true,
17 | port: 5002,
18 | headers: {
19 | 'Access-Control-Allow-Origin': '*',
20 | 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
21 | 'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization',
22 | },
23 | historyApiFallback: {
24 | index: 'index.html', // open index page for any missing route
25 | },
26 | },
27 | output: {
28 | filename: 'assets/js/[name].[chunkhash].js',
29 | publicPath: 'auto', // must be auto for Module Federation!
30 | crossOriginLoading: 'anonymous',
31 | },
32 | resolve: {
33 | extensions: ['.ts', '.tsx', '.js', '.less'],
34 | alias: {
35 | public: path.resolve(__dirname, 'public/'),
36 | },
37 | },
38 | module: {
39 | rules: [
40 | {
41 | // https://github.com/webpack/webpack/issues/11467#issuecomment-691702706
42 | // https://github.com/vercel/next.js/pull/17095
43 | test: /\.m?js/,
44 | resolve: {
45 | fullySpecified: false,
46 | },
47 | },
48 | {
49 | // TODO: почему бандл-лоадер (чуть больше контекста)
50 | test: /bootstrap\.tsx$/,
51 | loader: 'bundle-loader',
52 | options: {
53 | lazy: true,
54 | },
55 | },
56 | {
57 | test: /\.tsx?$/,
58 | loader: 'ts-loader',
59 | exclude: /node_modules/,
60 | },
61 | {
62 | test: /\.(png|jpe?g|gif|svg)$/i,
63 | use: [
64 | {
65 | loader: 'file-loader?name=assets/image/[name]-[hash].[ext]',
66 | },
67 | ],
68 | },
69 | {
70 | test: /\.(eot|woff|woff2|ttf)$/,
71 | use: {
72 | loader: 'url-loader?limit=30000&name=assets/fonts/[name]-[hash].[ext]',
73 | },
74 | },
75 | {
76 | test: /\.scss|\.css$/,
77 | use: [
78 | isProduction
79 | ? {
80 | loader: MiniCssExtractPlugin.loader,
81 | options: {
82 | publicPath: '/', // fix at build Error: Automatic publicPath is not supported in this browser
83 | },
84 | }
85 | : 'style-loader',
86 | {
87 | loader: 'css-loader',
88 | options: {
89 | modules: {
90 | auto: /\.module\.\w+$/i,
91 | localIdentName: isProduction ? '[hash:base64]' : '[local]---[path][name]',
92 | },
93 | sourceMap: !isProduction,
94 | },
95 | },
96 | {
97 | loader: 'sass-loader',
98 | options: {
99 | sourceMap: !isProduction,
100 | },
101 | },
102 | ],
103 | },
104 | ],
105 | },
106 | plugins: [
107 | new ModuleFederationPlugin({
108 | name: 'remote5002',
109 | // вспомнить почему
110 | library: { type: 'var', name: 'remote5002' },
111 | filename: 'remoteEntry.js',
112 | exposes: {
113 | // Will work like import('remote5002/Button');
114 | './Button': './src/expose/Button.tsx',
115 | './customCalc': './src/expose/customCalc.ts',
116 | },
117 | shared: {
118 | react: {
119 | requiredVersion: '17.0.1',
120 | singleton: true,
121 | },
122 | },
123 | }),
124 |
125 | new HtmlWebpackPlugin({
126 | template: './public/index.html',
127 | publicPath: '/',
128 | }),
129 |
130 | // Run docker run -p 3000:3000 -it scriptedalchemy/mf-dashboard:latest
131 | new DashboardPlugin({
132 | dashboardURL: 'http://localhost:3000/api/update',
133 | }),
134 |
135 | isProduction
136 | ? new MiniCssExtractPlugin({
137 | filename: 'assets/css/[name].css',
138 | })
139 | : false,
140 | ].filter(Boolean),
141 | };
142 |
--------------------------------------------------------------------------------