├── .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 |
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 | 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 | --------------------------------------------------------------------------------