├── test
├── app2
│ ├── .env
│ ├── src
│ │ ├── index.ts
│ │ ├── react-app-env.d.ts
│ │ ├── App.tsx
│ │ ├── Button
│ │ │ ├── JeanSunHo.ttf
│ │ │ ├── style.scss
│ │ │ └── index.tsx
│ │ ├── bootstrap.tsx
│ │ └── setupTests.ts
│ ├── public
│ │ ├── robots.txt
│ │ ├── favicon.ico
│ │ ├── logo192.png
│ │ ├── logo512.png
│ │ ├── manifest.json
│ │ └── index.html
│ ├── craco.config.js
│ ├── .gitignore
│ ├── modulefederation.config.js
│ ├── tsconfig.json
│ ├── package.json
│ └── README.md
└── app1
│ ├── src
│ ├── index.ts
│ ├── react-app-env.d.ts
│ ├── Button.tsx
│ ├── setupTests.ts
│ ├── bootstrap.tsx
│ ├── App.test.tsx
│ ├── index.css
│ ├── App.tsx
│ ├── App.css
│ └── logo.svg
│ ├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── index.html
│ ├── craco.config.js
│ ├── .gitignore
│ ├── modulefederation.config.js
│ ├── tsconfig.json
│ ├── package.json
│ └── README.md
├── .gitignore
├── .github
└── workflows
│ └── npm-publish.yml
├── package.json
├── LICENSE.md
├── index.js
└── README.md
/test/app2/.env:
--------------------------------------------------------------------------------
1 | PORT=3002
--------------------------------------------------------------------------------
/test/app1/src/index.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | import("./bootstrap");
3 |
--------------------------------------------------------------------------------
/test/app2/src/index.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | import("./bootstrap");
3 |
--------------------------------------------------------------------------------
/test/app1/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/test/app2/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/test/app1/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/test/app2/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/test/app1/src/Button.tsx:
--------------------------------------------------------------------------------
1 | const Button = () => ;
2 | export default Button;
3 |
--------------------------------------------------------------------------------
/test/app1/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hasanayan/craco-module-federation/HEAD/test/app1/public/favicon.ico
--------------------------------------------------------------------------------
/test/app1/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hasanayan/craco-module-federation/HEAD/test/app1/public/logo192.png
--------------------------------------------------------------------------------
/test/app1/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hasanayan/craco-module-federation/HEAD/test/app1/public/logo512.png
--------------------------------------------------------------------------------
/test/app2/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hasanayan/craco-module-federation/HEAD/test/app2/public/favicon.ico
--------------------------------------------------------------------------------
/test/app2/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hasanayan/craco-module-federation/HEAD/test/app2/public/logo192.png
--------------------------------------------------------------------------------
/test/app2/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hasanayan/craco-module-federation/HEAD/test/app2/public/logo512.png
--------------------------------------------------------------------------------
/test/app2/src/App.tsx:
--------------------------------------------------------------------------------
1 | import Button from "./Button";
2 | function App() {
3 | return ;
4 | }
5 |
6 | export default App;
7 |
--------------------------------------------------------------------------------
/test/app2/src/Button/JeanSunHo.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hasanayan/craco-module-federation/HEAD/test/app2/src/Button/JeanSunHo.ttf
--------------------------------------------------------------------------------
/test/app2/src/Button/style.scss:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: "JeanSunHo";
3 | src: url("./JeanSunHo.ttf");
4 | font-weight: 700;
5 | }
6 |
--------------------------------------------------------------------------------
/test/app1/craco.config.js:
--------------------------------------------------------------------------------
1 | const cracoModuleFederationPlugin = require("./plugin.js");
2 |
3 | module.exports = {
4 | plugins: [
5 | {
6 | plugin: cracoModuleFederationPlugin,
7 | },
8 | ],
9 | };
10 |
--------------------------------------------------------------------------------
/test/app2/src/bootstrap.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import App from "./App";
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById("root")
10 | );
11 |
--------------------------------------------------------------------------------
/test/app1/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/test/app2/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/test/app1/src/bootstrap.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./index.css";
4 | import App from "./App";
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById("root")
11 | );
12 |
--------------------------------------------------------------------------------
/test/app2/craco.config.js:
--------------------------------------------------------------------------------
1 | const cracoModuleFederationPlugin = require("./plugin.js");
2 |
3 | module.exports = {
4 | plugins: [
5 | {
6 | plugin: cracoModuleFederationPlugin,
7 | },
8 | ],
9 | webpack: {
10 | plugins: {
11 | remove: ["ModuleScopePlugin"],
12 | },
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/test/app1/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, screen } from '@testing-library/react';
3 | import App from './App';
4 |
5 | test('renders learn react link', () => {
6 | render();
7 | const linkElement = screen.getByText(/learn react/i);
8 | expect(linkElement).toBeInTheDocument();
9 | });
10 |
--------------------------------------------------------------------------------
/test/app1/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/.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 |
11 | # production
12 | /build
13 |
14 |
15 | # misc
16 | .DS_Store
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
26 | /test/**/plugin.js
27 |
--------------------------------------------------------------------------------
/test/app1/.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 |
11 | # production
12 | /build
13 | /build-with-subdir
14 |
15 |
16 | # misc
17 | .DS_Store
18 | .env.local
19 | .env.development.local
20 | .env.test.local
21 | .env.production.local
22 |
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | plugin.js
28 |
--------------------------------------------------------------------------------
/test/app2/.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 |
11 | # production
12 | /build
13 | /build-with-subdir
14 |
15 |
16 | # misc
17 | .DS_Store
18 | .env.local
19 | .env.development.local
20 | .env.test.local
21 | .env.production.local
22 |
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | plugin.js
28 |
--------------------------------------------------------------------------------
/test/app2/modulefederation.config.js:
--------------------------------------------------------------------------------
1 | const deps = require("./package.json").dependencies;
2 |
3 | module.exports = {
4 | name: "app2",
5 | exposes: {
6 | "./Button": "./src/Button",
7 | },
8 | filename: "remoteEntry.js",
9 | shared: {
10 | ...deps,
11 | react: {
12 | singleton: true,
13 | requiredVersion: deps["react"],
14 | },
15 | "react-dom": {
16 | singleton: true,
17 | requiredVersion: deps["react-dom"],
18 | },
19 | },
20 | };
21 |
--------------------------------------------------------------------------------
/test/app2/src/Button/index.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from "react";
2 | import styled from "styled-components";
3 | import "./style.scss";
4 |
5 | const Button: FC<{ className?: string }> = ({ className = "" }) => (
6 |
7 | Hello from app2
8 |
9 | );
10 | export default Button;
11 |
12 | const StyledButton = styled.button`
13 | font-family: JeanSunHo;
14 | color: red;
15 | font-size: 50px;
16 | height: 80px;
17 | `;
18 |
--------------------------------------------------------------------------------
/test/app1/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./App.css";
3 | //@ts-expect-error
4 | import Button from "app2/Button";
5 | import styled from "styled-components";
6 |
7 | function App() {
8 | return (
9 |
10 | This is app1
11 |
12 |
13 |
14 |
15 |
16 | );
17 | }
18 |
19 | const StyledButton = styled(Button)`
20 | color: blue;
21 | `;
22 |
23 | export default App;
24 |
--------------------------------------------------------------------------------
/test/app1/modulefederation.config.js:
--------------------------------------------------------------------------------
1 | const deps = require("./package.json").dependencies;
2 |
3 | console.log(process.env.ROOT_PATH_MODE);
4 | module.exports = {
5 | name: "app1",
6 | exposes: {
7 | "./Button": "./src/Button",
8 | },
9 | remotes: {
10 | app2: "app2@http://localhost:3002/remoteEntry.js",
11 | },
12 | filename: "remoteEntry.js",
13 | shared: {
14 | ...deps,
15 | react: {
16 | singleton: true,
17 | requiredVersion: deps["react"],
18 | },
19 | "react-dom": {
20 | singleton: true,
21 | requiredVersion: deps["react-dom"],
22 | },
23 | },
24 | };
25 |
--------------------------------------------------------------------------------
/test/app1/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/test/app2/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/test/app1/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/test/app2/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/.github/workflows/npm-publish.yml:
--------------------------------------------------------------------------------
1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
3 |
4 | name: Node.js Package
5 |
6 | on:
7 | release:
8 | types: [created]
9 |
10 | jobs:
11 | publish-npm:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v2
15 | - uses: actions/setup-node@v1
16 | with:
17 | node-version: 12
18 | registry-url: https://registry.npmjs.org/
19 | - run: npm ci
20 | - run: npm publish
21 | env:
22 | NODE_AUTH_TOKEN: ${{secrets.npm_token}}
23 |
--------------------------------------------------------------------------------
/test/app1/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | animation: App-logo-spin infinite 20s linear;
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 100vh;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: calc(10px + 2vmin);
24 | color: white;
25 | }
26 |
27 | .App-link {
28 | color: #61dafb;
29 | }
30 |
31 | @keyframes App-logo-spin {
32 | from {
33 | transform: rotate(0deg);
34 | }
35 | to {
36 | transform: rotate(360deg);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "craco-module-federation",
3 | "version": "1.1.0",
4 | "description": "Add module federation support to your CRA5 application without ejecting",
5 | "repository": {
6 | "type": "git",
7 | "url": "git+https://github.com/hasanayan/craco-module-federation.git"
8 | },
9 | "keywords": [
10 | "module-federation",
11 | "craco",
12 | "cra5",
13 | "cra",
14 | "create-react-app"
15 | ],
16 | "author": "hasanayan",
17 | "license": "MIT",
18 | "bugs": {
19 | "url": "https://github.com/hasanayan/craco-module-federation/issues"
20 | },
21 | "homepage": "https://github.com/hasanayan/craco-module-federation#readme",
22 | "main": "index.js",
23 | "directories": {
24 | "test": "test"
25 | },
26 | "scripts": {
27 | "test": "echo \"Error: no test specified\" && exit 1"
28 | },
29 | "dependencies": {
30 | "@craco/craco": "^6.3.0"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 WonbaeLee
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/test/app1/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app1",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.14.1",
7 | "@testing-library/react": "^12.1.2",
8 | "@testing-library/user-event": "^13.2.1",
9 | "@types/jest": "^27.0.2",
10 | "@types/node": "^16.10.3",
11 | "@types/react": "^17.0.27",
12 | "@types/react-dom": "^17.0.9",
13 | "@types/styled-components": "^5.1.14",
14 | "cross-env": "^7.0.3",
15 | "react": "^17.0.2",
16 | "react-dom": "^17.0.2",
17 | "react-scripts": "^5.0.0-next.47",
18 | "styled-components": "^5.3.1",
19 | "typescript": "^4.4.3"
20 | },
21 | "scripts": {
22 | "start": "cp ../../index.js ./plugin.js && craco start",
23 | "build": "cp ../../index.js ./plugin.js && craco build",
24 | "serve": "npm run build && serve -s build -p 3000",
25 | "test": "react-scripts test",
26 | "eject": "react-scripts eject"
27 | },
28 | "eslintConfig": {
29 | "extends": [
30 | "react-app",
31 | "react-app/jest"
32 | ]
33 | },
34 | "browserslist": {
35 | "production": [
36 | ">0.2%",
37 | "not dead",
38 | "not op_mini all"
39 | ],
40 | "development": [
41 | "last 1 chrome version",
42 | "last 1 firefox version",
43 | "last 1 safari version"
44 | ]
45 | },
46 | "devDependencies": {
47 | "@craco/craco": "^6.3.0"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/test/app2/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app2",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.14.1",
7 | "@testing-library/react": "^12.1.2",
8 | "@testing-library/user-event": "^13.2.1",
9 | "@types/jest": "^27.0.2",
10 | "@types/node": "^16.10.3",
11 | "@types/react": "^17.0.27",
12 | "@types/react-dom": "^17.0.9",
13 | "cross-env": "^7.0.3",
14 | "react": "^17.0.2",
15 | "react-dom": "^17.0.2",
16 | "react-scripts": "^5.0.0-next.47",
17 | "styled-components": "^5.3.1",
18 | "typescript": "^4.4.3"
19 | },
20 | "scripts": {
21 | "start": "cp ../../index.js ./plugin.js && craco start",
22 | "build": "cp ../../index.js ./plugin.js && craco build",
23 | "serve": "npm run build && serve -s build -p 3002",
24 | "test": "react-scripts test",
25 | "eject": "react-scripts eject"
26 | },
27 | "eslintConfig": {
28 | "extends": [
29 | "react-app",
30 | "react-app/jest"
31 | ]
32 | },
33 | "browserslist": {
34 | "production": [
35 | ">0.2%",
36 | "not dead",
37 | "not op_mini all"
38 | ],
39 | "development": [
40 | "last 1 chrome version",
41 | "last 1 firefox version",
42 | "last 1 safari version"
43 | ]
44 | },
45 | "devDependencies": {
46 | "@craco/craco": "^6.3.0",
47 | "@types/styled-components": "^5.1.14",
48 | "sass": "^1.42.1"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/test/app1/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/test/app2/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const webpack = require("webpack");
2 | const paths = require("react-scripts/config/paths");
3 |
4 | const getModuleFederationConfigPath = (additionalPaths = []) => {
5 | const path = require("path");
6 | const fs = require("fs");
7 | const appDirectory = fs.realpathSync(process.cwd());
8 | const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath);
9 |
10 | const moduleFederationConfigFiles = [
11 | "modulefederation.config.js",
12 | ...additionalPaths,
13 | ];
14 | return moduleFederationConfigFiles
15 | .map(resolveApp)
16 | .filter(fs.existsSync)
17 | .shift();
18 | };
19 |
20 | module.exports = {
21 | overrideWebpackConfig: ({ webpackConfig, pluginOptions }) => {
22 | const moduleFederationConfigPath = getModuleFederationConfigPath();
23 |
24 | if (moduleFederationConfigPath) {
25 | webpackConfig.output.publicPath = "auto";
26 |
27 | if (pluginOptions?.useNamedChunkIds) {
28 | webpackConfig.optimization.chunkIds = "named";
29 | }
30 |
31 | const htmlWebpackPlugin = webpackConfig.plugins.find(
32 | (plugin) => plugin.constructor.name === "HtmlWebpackPlugin"
33 | );
34 |
35 | htmlWebpackPlugin.userOptions = {
36 | ...htmlWebpackPlugin.userOptions,
37 | publicPath: paths.publicUrlOrPath,
38 | excludeChunks: [require(moduleFederationConfigPath).name],
39 | };
40 |
41 | webpackConfig.plugins = [
42 | ...webpackConfig.plugins,
43 | new webpack.container.ModuleFederationPlugin(
44 | require(moduleFederationConfigPath)
45 | ),
46 | ];
47 |
48 | // webpackConfig.module = {
49 | // ...webpackConfig.module,
50 | // generator: {
51 | // "asset/resource": {
52 | // publicPath: paths.publicUrlOrPath,
53 | // },
54 | // },
55 | // };
56 | }
57 | return webpackConfig;
58 | },
59 | overrideDevServerConfig: ({ devServerConfig }) => {
60 | devServerConfig.headers = {
61 | "Access-Control-Allow-Origin": "*",
62 | "Access-Control-Allow-Methods": "*",
63 | "Access-Control-Allow-Headers": "*",
64 | };
65 |
66 | return devServerConfig;
67 | },
68 | };
69 |
--------------------------------------------------------------------------------
/test/app1/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `yarn start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
13 |
14 | The page will reload if you make edits.\
15 | You will also see any lint errors in the console.
16 |
17 | ### `yarn test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `yarn build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `yarn eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
35 |
36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
39 |
40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
--------------------------------------------------------------------------------
/test/app2/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `yarn start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
13 |
14 | The page will reload if you make edits.\
15 | You will also see any lint errors in the console.
16 |
17 | ### `yarn test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `yarn build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `yarn eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
35 |
36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
39 |
40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # craco-module-federation
2 |
3 | ## Note
4 |
5 | THIS IS NOT PRODUCTION READY
6 | Webpack 5 support of CRA is still in its alpha phase and there might be breaking changes in either CRA5 or this plugin in the future.
7 |
8 | Add module-federation support to your CRA5 project without ejecting and losing update support of react-scripts
9 |
10 | 
11 | 
12 |
13 | ## Install
14 |
15 | ```
16 | npm install craco-module-federation --save-dev
17 | ```
18 |
19 | ## Usage
20 |
21 | 1. Add the plugin into your craco.config.js;
22 |
23 | ```
24 | const cracoModuleFederation = require('craco-module-federation');
25 |
26 | module.exports = {
27 | plugins: [{
28 | plugin: cracoModuleFederation,
29 | options: { useNamedChunkIds:true } //THIS LINE IS OPTIONAL
30 | },
31 | ]
32 | }
33 | ```
34 |
35 | 2. create a file named `modulefederation.config.js` in the project root. You should export ModuleFederationPlugin constructor options as json from this module. For example;
36 |
37 | ```
38 | const deps = require("./package.json").dependencies;
39 |
40 | module.exports = {
41 | name: "app1",
42 | exposes: {
43 | "./Button": "./src/Button",
44 | },
45 | remotes: {
46 | app2: "app2@http://localhost:3002/remoteEntry.js",
47 | },
48 | filename: "remoteEntry.js",
49 | shared: {
50 | ...deps,
51 | react: {
52 | singleton: true,
53 | requiredVersion: deps["react"],
54 | },
55 | "react-dom": {
56 | singleton: true,
57 | requiredVersion: deps["react-dom"],
58 | },
59 | },
60 | };
61 |
62 | ```
63 |
64 | 3. Update the scripts section of your package.json as follows:
65 |
66 | ```
67 | ...
68 | "scripts": {
69 | "start": "react-scripts start",
70 | "build": "react-scripts build",
71 | "craco:build": "craco build",
72 | "craco:start": "craco start",
73 | ...
74 | ```
75 |
76 | ## Testing the plugin locally
77 |
78 | There are two test apps in this repository inside test folder (app1 and app2). Install their dependencies on them using yarn (`yarn install`) and hit `yarn start` on both of them. When you navigate to app1 it should render the exported button from app2 that says `hello from app2`
79 |
80 | ## License
81 |
82 | Licensed under the MIT License, Copyright ©️ 2021 Hasan Ayan. See [LICENSE.md](LICENSE) for more information.
83 |
--------------------------------------------------------------------------------
/test/app1/src/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------