├── .gitignore
├── README.md
├── architecture.dio
├── child1
├── .babelrc
├── package-lock.json
├── package.json
├── postcss.config.js
├── src
│ ├── App.jsx
│ ├── Child1Content.jsx
│ ├── Feature1_M1.jsx
│ ├── Feature2_M1.jsx
│ ├── Feature3_M1.jsx
│ ├── Feature4_M1.jsx
│ ├── IndexPage.jsx
│ ├── index.html
│ ├── index.js
│ ├── index.scss
│ └── store.js
├── tailwind.config.js
├── webpack.config.js
└── yarn.lock
├── child2
├── .babelrc
├── package-lock.json
├── package.json
├── postcss.config.js
├── src
│ ├── App.jsx
│ ├── Child2Content.jsx
│ ├── Feature1_M2.jsx
│ ├── Feature2_M2.jsx
│ ├── Feature3_M2.jsx
│ ├── Feature4_M2.jsx
│ ├── IndexPage.jsx
│ ├── index.html
│ ├── index.js
│ ├── index.scss
│ └── store.js
├── tailwind.config.js
├── webpack.config.js
└── yarn.lock
└── container
├── .babelrc
├── cypress.json
├── cypress
├── fixtures
│ └── example.json
├── integration
│ └── e2e_spec.js
├── plugins
│ └── index.js
└── support
│ ├── commands.js
│ └── index.js
├── jest.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── src
├── App.jsx
├── MainLayout.jsx
├── NotDeployed.jsx
├── Test3.jsx
├── component
│ └── Layout.jsx
├── index.html
├── index.js
├── index.scss
├── reducers
│ ├── userReducer.js
│ └── utils.js
└── store.js
├── tailwind.config.js
├── webpack.config.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | build
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | 
4 |
5 | 
6 |
7 | 
8 |
9 | 
10 |
--------------------------------------------------------------------------------
/architecture.dio:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
--------------------------------------------------------------------------------
/child1/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-react", "@babel/preset-env"],
3 | "plugins": [
4 | ["@babel/transform-runtime"]
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/child1/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "child1",
3 | "version": "1.0.0",
4 | "scripts": {
5 | "build": "webpack --mode production",
6 | "build:dev": "webpack --mode development",
7 | "build:start": "cd dist && PORT=3002 npx serve",
8 | "start": "webpack serve --open --mode development",
9 | "start:live": "webpack serve --open --mode development --live-reload --hot"
10 | },
11 | "license": "MIT",
12 | "author": {
13 | "name": "Syed Ashar",
14 | "email": "saghirashar@gmail.com"
15 | },
16 | "devDependencies": {
17 | "@babel/core": "^7.15.8",
18 | "@babel/plugin-transform-runtime": "^7.15.8",
19 | "@babel/preset-env": "^7.15.8",
20 | "@babel/preset-react": "^7.14.5",
21 | "autoprefixer": "^10.1.0",
22 | "babel-loader": "^8.2.2",
23 | "css-loader": "^6.3.0",
24 | "html-webpack-plugin": "^5.3.2",
25 | "postcss": "^8.2.1",
26 | "postcss-loader": "^4.1.0",
27 | "style-loader": "^3.3.0",
28 | "tailwindcss": "^2.0.2",
29 | "webpack": "^5.57.1",
30 | "webpack-cli": "^4.9.0",
31 | "webpack-dev-server": "^4.3.1"
32 | },
33 | "dependencies": {
34 | "@babel/runtime": "^7.13.10",
35 | "react": "^17.0.2",
36 | "react-dom": "^17.0.2",
37 | "react-redux": "^7.2.8",
38 | "react-router-dom": "^5.3.0",
39 | "redux": "^4.1.2",
40 | "redux-thunk": "^2.4.1",
41 | "remixicon": "^2.5.0",
42 | "rxjs": "^7.4.0",
43 | "windowed-observable": "^1.0.0"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/child1/postcss.config.js:
--------------------------------------------------------------------------------
1 | const autoprefixer = require("autoprefixer");
2 | const tailwindcss = require("tailwindcss");
3 |
4 | module.exports = {
5 | plugins: [tailwindcss, autoprefixer],
6 | };
7 |
--------------------------------------------------------------------------------
/child1/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 |
4 | import "remixicon/fonts/remixicon.css";
5 | import "./index.scss";
6 | import Child1Content from 'child1/Child1Content'
7 | import { Provider } from 'react-redux'
8 | import store from './store';
9 |
10 | ReactDOM.render(
11 |
12 | , document.getElementById("app"));
13 |
--------------------------------------------------------------------------------
/child1/src/Child1Content.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import { BrowserRouter as Router, Route, Switch , Link } from "react-router-dom";
3 | import Feature1_M1 from "./Feature1_M1";
4 | import Feature2_M1 from "./Feature2_M1";
5 | import Feature3_M1 from "./Feature3_M1";
6 | import Feature4_M1 from "./Feature4_M1";
7 | import IndexPage from "./IndexPage";
8 | import { Observable } from 'windowed-observable';
9 | import { useDispatch } from 'react-redux'
10 |
11 | const observable = new Observable('child1');
12 |
13 | export default function Child1Content({}) {
14 |
15 | const dispatch = useDispatch()
16 |
17 | const handleNewMessage = (newMessage) => {
18 | console.log(newMessage);
19 | dispatch({ type: 'CHANGE_PERMS', payload: newMessage.modulePerms});
20 | };
21 |
22 | useEffect(() => {
23 | observable.subscribe(handleNewMessage);
24 | return () => {
25 | observable.unsubscribe(handleNewMessage)
26 | }
27 | }, [handleNewMessage]);
28 |
29 |
30 | return (
31 |
32 |
33 |
34 |
35 | } />
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | );
45 | }
46 |
--------------------------------------------------------------------------------
/child1/src/Feature1_M1.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router-dom'
3 | import { useSelector } from 'react-redux'
4 |
5 | export default function Feature1_M1() {
6 |
7 | const modulePerms = useSelector((state) => state.userLogin.modulePerms);
8 |
9 | return (
10 | Module 1 / Feature 1
11 | )
12 | }
--------------------------------------------------------------------------------
/child1/src/Feature2_M1.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router-dom'
3 | import { useSelector } from 'react-redux'
4 |
5 | export default function Feature2_M1() {
6 |
7 | const modulePerms = useSelector((state) => state.userLogin.modulePerms);
8 |
9 | return (
10 | Module 1 / Feature 2
11 | )
12 | }
--------------------------------------------------------------------------------
/child1/src/Feature3_M1.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router-dom'
3 | import { useSelector } from 'react-redux'
4 |
5 | export default function Feature3_M1() {
6 |
7 | const modulePerms = useSelector((state) => state.userLogin.modulePerms);
8 |
9 | return (
10 | Module 1 / Feature 3
11 | )
12 | }
--------------------------------------------------------------------------------
/child1/src/Feature4_M1.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router-dom'
3 | import { useSelector } from 'react-redux'
4 |
5 | export default function Feature4_M1() {
6 |
7 | const modulePerms = useSelector((state) => state.userLogin.modulePerms);
8 |
9 | return (
10 | Module 1 / Feature 4
11 | )
12 | }
--------------------------------------------------------------------------------
/child1/src/IndexPage.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import { BrowserRouter as Switch , Link } from "react-router-dom";
3 | import { useSelector } from 'react-redux'
4 |
5 | export default function IndexPage({}) {
6 |
7 |
8 | const [pre, setPre] = useState('/')
9 | const modulePerms = useSelector((state) => state.userLogin.modulePerms);
10 |
11 | useEffect(() => {
12 | setPre(modulePerms ? '/module1' : '' )
13 | }, [ ])
14 |
15 | return (
16 |
17 |
Module 1
18 | {(!modulePerms || modulePerms.m1_feature1 ) &&
Feature : 1
}
19 | {(!modulePerms || modulePerms.m1_feature2 ) &&
Feature : 2
}
20 | {(!modulePerms || modulePerms.m1_feature3 ) &&
Feature : 3
}
21 | {(!modulePerms || modulePerms.m1_feature4 ) &&
Feature : 4
}
22 |
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/child1/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | child 1
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/child1/src/index.js:
--------------------------------------------------------------------------------
1 | import("./App");
2 |
--------------------------------------------------------------------------------
/child1/src/index.scss:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | body {
6 | font-family: Arial, Helvetica, sans-serif;
7 |
8 | }
--------------------------------------------------------------------------------
/child1/src/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, compose, applyMiddleware, combineReducers } from 'redux';
2 | import thunk from 'redux-thunk';
3 |
4 | const initialState = {};
5 | const userSigninReducer = ( state = {} , action ) =>
6 | {
7 | switch (action.type) {
8 | case 'CHANGE_PERMS':
9 | return { modulePerms : action.payload };
10 | default:
11 | return state;
12 | }
13 | };
14 |
15 | const reducer = combineReducers({
16 | userLogin : userSigninReducer ,
17 | });
18 | const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
19 | const store = createStore(
20 | reducer,
21 | initialState,
22 | composeEnhancer(applyMiddleware(thunk))
23 | );
24 |
25 | export default store;
--------------------------------------------------------------------------------
/child1/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | purge: [],
3 | darkMode: false, // or 'media' or 'class'
4 | theme: {
5 | extend: {},
6 | },
7 | variants: {
8 | extend: {},
9 | },
10 | plugins: [],
11 | }
12 |
--------------------------------------------------------------------------------
/child1/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebPackPlugin = require("html-webpack-plugin");
2 | const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
3 |
4 | const deps = require("./package.json").dependencies;
5 | module.exports = {
6 | output: {
7 | publicPath: "http://localhost:3002/",
8 | },
9 |
10 | resolve: {
11 | extensions: [".tsx", ".ts", ".jsx", ".js", ".json"],
12 | },
13 |
14 | devServer: {
15 | port: 3002,
16 | historyApiFallback: true,
17 | },
18 |
19 | module: {
20 | rules: [
21 | {
22 | test: /\.m?js/,
23 | type: "javascript/auto",
24 | resolve: {
25 | fullySpecified: false,
26 | },
27 | },
28 | {
29 | test: /\.(css|s[ac]ss)$/i,
30 | use: ["style-loader", "css-loader", "postcss-loader"],
31 | },
32 | {
33 | test: /\.(ts|tsx|js|jsx)$/,
34 | exclude: /node_modules/,
35 | use: {
36 | loader: "babel-loader",
37 | },
38 | },
39 | ],
40 | },
41 |
42 | plugins: [
43 | new ModuleFederationPlugin({
44 | name: "child1",
45 | filename: "remoteEntry.js",
46 | remotes: {
47 | child1: "child1@http://localhost:3002/remoteEntry.js",
48 | },
49 | exposes: {
50 | "./Child1Content": "./src/Child1Content.jsx",
51 | "./Feature1_M1":"./src/Feature1_M1.jsx",
52 | "./Feature2_M1":"./src/Feature2_M1.jsx",
53 | "./Feature3_M1":"./src/Feature3_M1.jsx",
54 | "./Feature4_M1":"./src/Feature4_M1.jsx"
55 | },
56 | shared: {
57 | ...deps,
58 | react: {
59 | singleton: true,
60 | requiredVersion: deps.react,
61 | },
62 | "react-dom": {
63 | singleton: true,
64 | requiredVersion: deps["react-dom"],
65 | },
66 | },
67 | }),
68 | new HtmlWebPackPlugin({
69 | template: "./src/index.html",
70 | }),
71 | ],
72 | };
73 |
--------------------------------------------------------------------------------
/child2/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-react", "@babel/preset-env"],
3 | "plugins": [
4 | ["@babel/transform-runtime"]
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/child2/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "child2",
3 | "version": "1.0.0",
4 | "scripts": {
5 | "build": "webpack --mode production",
6 | "build:dev": "webpack --mode development",
7 | "build:start": "cd dist && PORT=3001 npx serve",
8 | "start": "webpack serve --open --mode development",
9 | "start:live": "webpack serve --open --mode development --live-reload --hot"
10 | },
11 | "license": "MIT",
12 | "author": {
13 | "name": "Syed Ashar",
14 | "email": "saghirashar@gmail.com"
15 | },
16 | "devDependencies": {
17 | "@babel/core": "^7.15.8",
18 | "@babel/plugin-transform-runtime": "^7.15.8",
19 | "@babel/preset-env": "^7.15.8",
20 | "@babel/preset-react": "^7.14.5",
21 | "autoprefixer": "^10.1.0",
22 | "babel-loader": "^8.2.2",
23 | "css-loader": "^6.3.0",
24 | "html-webpack-plugin": "^5.3.2",
25 | "postcss": "^8.2.1",
26 | "postcss-loader": "^4.1.0",
27 | "style-loader": "^3.3.0",
28 | "tailwindcss": "^2.0.2",
29 | "webpack": "^5.57.1",
30 | "webpack-cli": "^4.9.0",
31 | "webpack-dev-server": "^4.3.1"
32 | },
33 | "dependencies": {
34 | "@babel/runtime": "^7.13.10",
35 | "react": "^17.0.2",
36 | "react-dom": "^17.0.2",
37 | "react-redux": "^7.2.8",
38 | "react-router-dom": "^5.3.0",
39 | "redux": "^4.1.2",
40 | "redux-thunk": "^2.4.1",
41 | "remixicon": "^2.5.0",
42 | "windowed-observable": "^1.0.0"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/child2/postcss.config.js:
--------------------------------------------------------------------------------
1 | const autoprefixer = require("autoprefixer");
2 | const tailwindcss = require("tailwindcss");
3 |
4 | module.exports = {
5 | plugins: [tailwindcss, autoprefixer],
6 | };
7 |
--------------------------------------------------------------------------------
/child2/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 |
4 | import "remixicon/fonts/remixicon.css";
5 | import "./index.scss";
6 | import Child2Content from 'child2/Child2Content'
7 | import { Provider } from 'react-redux'
8 | import store from './store';
9 |
10 | ReactDOM.render(
11 |
12 | , document.getElementById("app"));
13 |
--------------------------------------------------------------------------------
/child2/src/Child2Content.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import { BrowserRouter as Router, Route, Switch , Link } from "react-router-dom";
3 | import Feature1_M2 from "./Feature1_M2";
4 | import Feature2_M2 from "./Feature2_M2";
5 | import Feature3_M2 from "./Feature3_M2";
6 | import Feature4_M2 from "./Feature4_M2";
7 | import IndexPage from "./IndexPage";
8 | import { Observable } from 'windowed-observable';
9 | import { useDispatch } from 'react-redux'
10 |
11 | const observable = new Observable('child2');
12 |
13 | export default function Child2Content({ }) {
14 |
15 | const dispatch = useDispatch()
16 |
17 | const handleNewMessage = (newMessage) => {
18 | console.log(newMessage);
19 | dispatch({ type: 'CHANGE_PERMS', payload: newMessage.modulePerms});
20 | };
21 | useEffect(() => {
22 | observable.subscribe(handleNewMessage);
23 | return () => {
24 | observable.unsubscribe(handleNewMessage)
25 | }
26 | }, [handleNewMessage]);
27 |
28 | return (
29 |
30 |
31 |
32 |
33 | } />
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | );
43 | }
44 |
--------------------------------------------------------------------------------
/child2/src/Feature1_M2.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router-dom'
3 | import { useSelector } from 'react-redux'
4 |
5 | export default function Feature1_M2() {
6 |
7 | const modulePerms = useSelector((state) => state.userLogin.modulePerms);
8 |
9 | return (
10 | Module 2 / Feature 1
11 | )
12 | }
--------------------------------------------------------------------------------
/child2/src/Feature2_M2.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router-dom'
3 | import { useSelector } from 'react-redux'
4 |
5 | export default function Feature2_M2() {
6 |
7 | const modulePerms = useSelector((state) => state.userLogin.modulePerms);
8 |
9 | return (
10 | Module 2 / Feature 2
11 | )
12 | }
--------------------------------------------------------------------------------
/child2/src/Feature3_M2.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router-dom'
3 | import { useSelector } from 'react-redux'
4 |
5 | export default function Feature3_M2() {
6 |
7 | const modulePerms = useSelector((state) => state.userLogin.modulePerms);
8 |
9 | return (
10 | Module 2 / Feature 3
11 | )
12 | }
--------------------------------------------------------------------------------
/child2/src/Feature4_M2.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router-dom'
3 | import { useSelector } from 'react-redux'
4 |
5 | export default function Feature4_M2() {
6 |
7 | const modulePerms = useSelector((state) => state.userLogin.modulePerms);
8 |
9 | return (
10 | Module 2 / Feature 4
11 | )
12 | }
--------------------------------------------------------------------------------
/child2/src/IndexPage.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import { BrowserRouter as Router, Route, Switch , Link } from "react-router-dom";
3 | import { useSelector } from 'react-redux'
4 |
5 | export default function IndexPage({}) {
6 |
7 |
8 | const [pre, setPre] = useState('/')
9 | const modulePerms = useSelector((state) => state.userLogin.modulePerms);
10 |
11 | useEffect(() => {
12 | setPre(modulePerms ? '/module2' : '' )
13 | }, [ ])
14 |
15 | return (
16 |
17 |
Module 2
18 | {(!modulePerms || modulePerms.m2_feature1 ) &&
Feature : 1
}
19 | {(!modulePerms || modulePerms.m2_feature2 ) &&
Feature : 2
}
20 | {(!modulePerms || modulePerms.m2_feature3 ) &&
Feature : 3
}
21 | {(!modulePerms || modulePerms.m2_feature4 ) &&
Feature : 4
}
22 |
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/child2/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Child 2
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/child2/src/index.js:
--------------------------------------------------------------------------------
1 | import("./App");
2 |
--------------------------------------------------------------------------------
/child2/src/index.scss:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | body {
6 | font-family: Arial, Helvetica, sans-serif;
7 | }
--------------------------------------------------------------------------------
/child2/src/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, compose, applyMiddleware, combineReducers } from 'redux';
2 | import thunk from 'redux-thunk';
3 |
4 | const initialState = {};
5 | const userSigninReducer = ( state = {} , action ) =>
6 | {
7 | switch (action.type) {
8 | case 'CHANGE_PERMS':
9 | return { modulePerms : action.payload };
10 | default:
11 | return state;
12 | }
13 | };
14 |
15 | const reducer = combineReducers({
16 | userLogin : userSigninReducer ,
17 | });
18 | const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
19 | const store = createStore(
20 | reducer,
21 | initialState,
22 | composeEnhancer(applyMiddleware(thunk))
23 | );
24 |
25 | export default store;
--------------------------------------------------------------------------------
/child2/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | purge: [],
3 | darkMode: false, // or 'media' or 'class'
4 | theme: {
5 | extend: {},
6 | },
7 | variants: {
8 | extend: {},
9 | },
10 | plugins: [],
11 | }
12 |
--------------------------------------------------------------------------------
/child2/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebPackPlugin = require("html-webpack-plugin");
2 | const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
3 |
4 | const deps = require("./package.json").dependencies;
5 | module.exports = {
6 | output: {
7 | publicPath: "http://localhost:3001/",
8 | },
9 |
10 | resolve: {
11 | extensions: [".tsx", ".ts", ".jsx", ".js", ".json"],
12 | },
13 |
14 | devServer: {
15 | port: 3001,
16 | historyApiFallback: true,
17 | },
18 |
19 | module: {
20 | rules: [
21 | {
22 | test: /\.m?js/,
23 | type: "javascript/auto",
24 | resolve: {
25 | fullySpecified: false,
26 | },
27 | },
28 | {
29 | test: /\.(css|s[ac]ss)$/i,
30 | use: ["style-loader", "css-loader", "postcss-loader"],
31 | },
32 | {
33 | test: /\.(ts|tsx|js|jsx)$/,
34 | exclude: /node_modules/,
35 | use: {
36 | loader: "babel-loader",
37 | },
38 | },
39 | ],
40 | },
41 |
42 | plugins: [
43 | new ModuleFederationPlugin({
44 | name: "child2",
45 | filename: "remoteEntry.js",
46 | remotes: {
47 | child2: "child2@http://localhost:3001/remoteEntry.js",
48 | },
49 | exposes: {
50 | "./Child2Content": "./src/Child2Content.jsx",
51 | "./Feature1_M2":"./src/Feature1_M2.jsx",
52 | "./Feature2_M2":"./src/Feature2_M2.jsx",
53 | "./Feature3_M2":"./src/Feature3_M2.jsx",
54 | "./Feature4_M2":"./src/Feature4_M2.jsx"
55 | },
56 | shared: {
57 | ...deps,
58 | react: {
59 | singleton: true,
60 | requiredVersion: deps.react,
61 | },
62 | "react-dom": {
63 | singleton: true,
64 | requiredVersion: deps["react-dom"],
65 | },
66 | },
67 | }),
68 | new HtmlWebPackPlugin({
69 | template: "./src/index.html",
70 | }),
71 | ],
72 | };
73 |
--------------------------------------------------------------------------------
/container/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-react", "@babel/preset-env"],
3 | "plugins": [
4 | ["@babel/transform-runtime"]
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/container/cypress.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/container/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
6 |
--------------------------------------------------------------------------------
/container/cypress/integration/e2e_spec.js:
--------------------------------------------------------------------------------
1 | describe("My First E2E Test", () => {
2 | it("Should add an item to the cart", () => {
3 | cy.visit("http://localhost:3000/");
4 | cy.get("#showlogin").click();
5 | cy.get("#loginbtn").click();
6 | cy.get("#showcart").click();
7 | cy.get("#clearcart").click();
8 | cy.get("#addtocart_1").click();
9 | cy.get("#cart").click();
10 | cy.get("#grand_total").should("contain", "$5.99");
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/container/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | ///
2 | // ***********************************************************
3 | // This example plugins/index.js can be used to load plugins
4 | //
5 | // You can change the location of this file or turn off loading
6 | // the plugins file with the 'pluginsFile' configuration option.
7 | //
8 | // You can read more here:
9 | // https://on.cypress.io/plugins-guide
10 | // ***********************************************************
11 |
12 | // This function is called when a project is opened or re-opened (e.g. due to
13 | // the project's config changing)
14 |
15 | /**
16 | * @type {Cypress.PluginConfig}
17 | */
18 | // eslint-disable-next-line no-unused-vars
19 | module.exports = (on, config) => {
20 | // `on` is used to hook into various events Cypress emits
21 | // `config` is the resolved Cypress config
22 | }
23 |
--------------------------------------------------------------------------------
/container/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 | //
11 | //
12 | // -- This is a parent command --
13 | // Cypress.Commands.add('login', (email, password) => { ... })
14 | //
15 | //
16 | // -- This is a child command --
17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
18 | //
19 | //
20 | // -- This is a dual command --
21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
22 | //
23 | //
24 | // -- This will overwrite an existing command --
25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
26 |
--------------------------------------------------------------------------------
/container/cypress/support/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands'
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
21 |
--------------------------------------------------------------------------------
/container/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | testEnvironment: "jsdom",
3 | };
4 |
--------------------------------------------------------------------------------
/container/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "container",
3 | "version": "1.0.0",
4 | "scripts": {
5 | "build": "webpack --mode production",
6 | "build:dev": "webpack --mode development",
7 | "build:start": "cd dist && PORT=3000 npx serve",
8 | "start": "webpack serve --open --mode development",
9 | "start:live": "webpack serve --open --mode development --live-reload --hot"
10 | },
11 | "license": "MIT",
12 | "author": {
13 | "name": "Syed Ashar",
14 | "email": "saghirashar@gmail.com"
15 | },
16 | "devDependencies": {
17 | "@babel/core": "^7.15.8",
18 | "@babel/plugin-transform-runtime": "^7.15.8",
19 | "@babel/preset-env": "^7.15.8",
20 | "@babel/preset-react": "^7.14.5",
21 | "@testing-library/react-hooks": "^7.0.2",
22 | "autoprefixer": "^10.1.0",
23 | "babel-loader": "^8.2.2",
24 | "css-loader": "^6.3.0",
25 | "cypress": "^8.7.0",
26 | "html-webpack-plugin": "^5.3.2",
27 | "jest": "^27.3.1",
28 | "postcss": "^8.2.1",
29 | "postcss-loader": "^4.1.0",
30 | "style-loader": "^3.3.0",
31 | "tailwindcss": "^2.0.2",
32 | "webpack": "^5.57.1",
33 | "webpack-cli": "^4.9.0",
34 | "webpack-dev-server": "^4.3.1"
35 | },
36 | "dependencies": {
37 | "@babel/runtime": "^7.13.10",
38 | "react": "^17.0.2",
39 | "react-dom": "^17.0.2",
40 | "react-redux": "^7.2.7",
41 | "react-router-dom": "^5.3.0",
42 | "redux": "^4.1.2",
43 | "redux-thunk": "^2.4.1",
44 | "remixicon": "^2.5.0",
45 | "windowed-observable": "^1.0.0"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/container/postcss.config.js:
--------------------------------------------------------------------------------
1 | const autoprefixer = require("autoprefixer");
2 | const tailwindcss = require("tailwindcss");
3 |
4 | module.exports = {
5 | plugins: [tailwindcss, autoprefixer],
6 | };
7 |
--------------------------------------------------------------------------------
/container/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 |
4 | import "remixicon/fonts/remixicon.css";
5 | import "./index.scss";
6 |
7 | import MainLayout from "container/MainLayout";
8 | import { Provider } from 'react-redux'
9 | import store from './store';
10 |
11 | ReactDOM.render(
12 |
13 |
14 | ,
15 | document.getElementById("app"));
16 |
--------------------------------------------------------------------------------
/container/src/MainLayout.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState , useEffect } from "react";
2 | import { BrowserRouter as Router, Route, Switch , Link } from "react-router-dom";
3 | import { useDispatch, useSelector } from 'react-redux'
4 |
5 | import "remixicon/fonts/remixicon.css";
6 | import "./index.scss";
7 | import NotDeployed from './NotDeployed';
8 | import { Observable } from 'windowed-observable';
9 | const observable1 = new Observable('child1');
10 | const observable2 = new Observable('child2');
11 | const observable3 = new Observable('child3');
12 |
13 | let Child1Content , Child2Content , Child3Content , Feature1_M1 , Feature2_M1 , Feature3_M1 , Feature4_M1 ,Feature1_M2 , Feature2_M2 , Feature3_M2 , Feature4_M2 ;
14 | try { Child1Content = require('child1/Child1Content').default } catch (e) {Child1Content = NotDeployed}
15 | try { Child2Content = require('child2/Child2Content').default } catch (e) {Child2Content = NotDeployed}
16 | try { Child3Content = require('child3/Child3Content').default } catch (e) {Child3Content = NotDeployed}
17 | try { Feature1_M1 = require('child1/Feature1_M1').default } catch (e) {Feature1_M1 = NotDeployed}
18 | try { Feature1_M2 = require('child1/Feature2_M1').default } catch (e) {Feature2_M1 = NotDeployed}
19 | try { Feature3_M1 = require('child1/Feature3_M1').default } catch (e) {Feature3_M1 = NotDeployed}
20 | try { Feature4_M1 = require('child1/Feature4_M1').default } catch (e) {Feature4_M1 = NotDeployed}
21 | try { Feature1_M2 = require('child2/Feature1_M2').default } catch (e) {Feature1_M2 = NotDeployed}
22 | try { Feature2_M2 = require('child2/Feature2_M2').default } catch (e) {Feature2_M2 = NotDeployed}
23 | try { Feature3_M2 = require('child2/Feature3_M2').default } catch (e) {Feature3_M2 = NotDeployed}
24 | try { Feature4_M2 = require('child2/Feature4_M2').default } catch (e) {Feature4_M2 = NotDeployed}
25 |
26 |
27 |
28 | export default function MainLayout() {
29 |
30 | const dispatch = useDispatch()
31 |
32 | const modules = useSelector((state) => state.userLogin.modules);
33 | const modulePerms = useSelector((state) => state.userLogin.modulePerms);
34 |
35 | const [path, setPath] = useState(null)
36 |
37 | const login = () => {
38 | dispatch({ type: 'USER_SIGNIN_REQUEST'});
39 | //wait
40 | dispatch({ type: 'USER_SIGNIN_SUCCESS'})
41 |
42 | }
43 |
44 | const logout = () => {
45 | dispatch({ type: 'USER_SIGNOUT'});
46 | window.sessionStorage.clear();
47 | }
48 |
49 | useEffect(() => {
50 | console.log(modules);
51 | console.log(modulePerms);
52 | if (!modules || !modulePerms ) return ;
53 | window.sessionStorage.setItem('modules',JSON.stringify(modules))
54 | window.sessionStorage.setItem('modulePerms',JSON.stringify(modulePerms))
55 | }, [modules,modulePerms])
56 |
57 |
58 | const changePerm = () => {
59 |
60 | if(!modules) return;
61 | dispatch({ type: 'USER_CHANGE_PERMISSIONS'});
62 | setPath('/')
63 | }
64 |
65 | const handleRouteChange = (r) => {
66 |
67 | if(r=='/module1') observable1.publish({modulePerms : {m1_feature1 : modulePerms.m1_feature1 , m1_feature2 : modulePerms.m1_feature2 , m1_feature3 : modulePerms.m1_feature3 , m1_feature4 : modulePerms.m1_feature4} });
68 | if(r=='/module2') observable2.publish({modulePerms : {m2_feature1 : modulePerms.m2_feature1 , m2_feature2 : modulePerms.m2_feature2 , m2_feature3 : modulePerms.m2_feature3 , m2_feature4 : modulePerms.m2_feature4} });
69 | if(r=='/module3') observable3.publish({modulePerms : {m3_feature1 : modulePerms.m3_feature1 , m3_feature2 : modulePerms.m3_feature2 , m3_feature3 : modulePerms.m3_feature3 , m3_feature4 : modulePerms.m3_feature4} });
70 |
71 | setPath(r)
72 | }
73 |
74 |
75 | return (
76 |
77 |
78 |
79 |
80 |
81 |
Home
82 |
|
83 |
84 |
|
85 |
86 |
87 |
88 | Change Controls
89 |
90 |
91 |
92 |
93 |
94 |
95 | {(modules && modules.m1) &&
handleRouteChange('/module1')} >
Module 1
}
96 | {(modules && modules.m2) &&
handleRouteChange('/module2')} >
Module 2
}
97 | {(modules && modules.m3) &&
handleRouteChange('/module3')} >
Module 3
}
98 |
99 |
100 |
101 |
102 | {modules && modulePerms ?
103 | Welcome
} />
104 |
105 | {modules.m1 && } />}
106 | {modulePerms.m1_feature1 && } />}
107 | {modulePerms.m1_feature2 && } />}
108 | {modulePerms.m1_feature3 && } />}
109 | {modulePerms.m1_feature4 && } />}
110 |
111 | {modules.m2 && } />}
112 | {modulePerms.m2_feature1 && } />}
113 | {modulePerms.m2_feature2 && } />}
114 | {modulePerms.m2_feature3 && } />}
115 | {modulePerms.m2_feature4 && } />}
116 |
117 | {modules.m3 && } />}
118 |
119 | Page Not Found
} />
120 |
121 |
122 | :
123 |
Please Log In First
124 | }
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 | Footer
134 |
135 |
136 | );
137 | }
138 |
--------------------------------------------------------------------------------
/container/src/NotDeployed.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useHistory } from 'react-router-dom'
3 |
4 | export default function NotDeployed() {
5 |
6 | const history = useHistory()
7 | return (
8 |
9 |
{history && history.location.pathname.split('/')[1]}
10 |
is Not Deployed. Please Contact Support.
11 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/container/src/Test3.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | // import TrySome from "child3/TrySome";
3 |
4 | export default function Test3() {
5 | return (
6 |
7 |
8 | )
9 | }
10 |
--------------------------------------------------------------------------------
/container/src/component/Layout.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router-dom'
3 |
4 | export default function Layout({children}) {
5 | return (
6 |
7 |
8 |
9 |
10 |
Home
11 |
|
12 |
13 |
14 |
15 |
16 | Change Controls
17 |
18 |
19 |
20 |
21 |
22 |
23 | {children}
24 |
25 |
26 |
27 | Footer
28 |
29 |
30 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/container/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | home
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/container/src/index.js:
--------------------------------------------------------------------------------
1 | import("./App");
2 |
--------------------------------------------------------------------------------
/container/src/index.scss:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | body {
6 | font-family: Arial, Helvetica, sans-serif;
7 | }
--------------------------------------------------------------------------------
/container/src/reducers/userReducer.js:
--------------------------------------------------------------------------------
1 | import {m1 , m2 , m3 , m1_feature1 , m1_feature2 , m2_feature3 , m2_feature1 , m1_feature3 , m2_feature2 , m3_feature1 , m1_feature4 , m2_feature4 } from './utils'
2 |
3 | export const userSigninReducer = (
4 |
5 | state = {
6 | modules : window.sessionStorage.getItem("modules") ? JSON.parse(window.sessionStorage.getItem("modules")) : null ,
7 | modulePerms : window.sessionStorage.getItem("modulePerms") ? JSON.parse(window.sessionStorage.getItem("modulePerms")) : null ,
8 | }
9 | , action ) =>
10 | {
11 |
12 | console.log(state);
13 |
14 | switch (action.type) {
15 | case 'USER_SIGNIN_REQUEST':
16 | return { loading: true };
17 | case 'USER_SIGNIN_SUCCESS':
18 | return {
19 | loading: false,
20 | modules: {m1 , m2 , m3 },
21 | modulePerms:{
22 | m1_feature1,
23 | m1_feature2,
24 | m1_feature3,
25 | m1_feature4,
26 | m2_feature1,
27 | m2_feature2,
28 | m2_feature3,
29 | m2_feature4,
30 | m3_feature1}
31 | };
32 | case 'USER_CHANGE_PERMISSIONS':
33 | return {
34 | loading: false,
35 | modules: {m1} ,
36 | modulePerms:{
37 | m1_feature2,
38 | m1_feature3 }
39 | };
40 | case 'USER_SIGNIN_FAIL':
41 | return { loading: false, error: action.payload };
42 | case 'USER_SIGNOUT':
43 | return {};
44 | default:
45 | return state;
46 | }
47 | };
48 |
--------------------------------------------------------------------------------
/container/src/reducers/utils.js:
--------------------------------------------------------------------------------
1 | const m1 = true ;
2 | const m2 = true ;
3 | const m3 = true ;
4 | const m1_feature1 = {} ;
5 | const m1_feature2 = {} ;
6 | const m1_feature3 = {} ;
7 | const m1_feature4 = {} ;
8 | const m2_feature1 = {} ;
9 | const m2_feature2 = {} ;
10 | const m2_feature3 = {} ;
11 | const m2_feature4 = {} ;
12 | const m3_feature1 = {} ;
13 |
14 | export {m1 , m2 , m3 , m1_feature1 , m1_feature2 , m2_feature3 , m2_feature1 , m1_feature3 , m2_feature2 , m3_feature1 , m1_feature4 , m2_feature4 }
--------------------------------------------------------------------------------
/container/src/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, compose, applyMiddleware, combineReducers } from 'redux';
2 | import thunk from 'redux-thunk';
3 | import {userSigninReducer} from './reducers/userReducer';
4 |
5 |
6 | const initialState = {};
7 |
8 | const reducer = combineReducers({
9 | userLogin : userSigninReducer ,
10 | });
11 | const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
12 | const store = createStore(
13 | reducer,
14 | initialState,
15 | composeEnhancer(applyMiddleware(thunk))
16 | );
17 |
18 | export default store;
--------------------------------------------------------------------------------
/container/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | purge: [],
3 | darkMode: false, // or 'media' or 'class'
4 | theme: {
5 | extend: {},
6 | },
7 | variants: {
8 | extend: {},
9 | },
10 | plugins: [],
11 | }
12 |
--------------------------------------------------------------------------------
/container/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebPackPlugin = require("html-webpack-plugin");
2 | const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
3 |
4 | const deps = require("./package.json").dependencies;
5 | module.exports = {
6 | output: {
7 | publicPath: "http://localhost:3000/",
8 | },
9 |
10 | resolve: {
11 | extensions: [".tsx", ".ts", ".jsx", ".js", ".json"],
12 | },
13 |
14 | devServer: {
15 | port: 3000,
16 | historyApiFallback: true,
17 | },
18 |
19 | module: {
20 | rules: [
21 | {
22 | test: /\.m?js/,
23 | type: "javascript/auto",
24 | resolve: {
25 | fullySpecified: false,
26 | },
27 | },
28 | {
29 | test: /\.(css|s[ac]ss)$/i,
30 | use: ["style-loader", "css-loader", "postcss-loader"],
31 | },
32 | {
33 | test: /\.(ts|tsx|js|jsx)$/,
34 | exclude: /node_modules/,
35 | use: {
36 | loader: "babel-loader",
37 | },
38 | },
39 | ],
40 | },
41 |
42 | plugins: [
43 | new ModuleFederationPlugin({
44 | name: "container",
45 | filename: "remoteEntry.js",
46 | remotes: {
47 | container: "container@http://localhost:3000/remoteEntry.js",
48 | child2: "child2@http://localhost:3001/remoteEntry.js",
49 | child1: "child1@http://localhost:3002/remoteEntry.js",
50 | child3: "child3@http://localhost:3003/remoteEntry.js"
51 | },
52 | exposes: {
53 | "./MainLayout": "./src/MainLayout.jsx",
54 | },
55 | shared: {
56 | ...deps,
57 | react: {
58 | singleton: true,
59 | requiredVersion: deps.react,
60 | },
61 | "react-dom": {
62 | singleton: true,
63 | requiredVersion: deps["react-dom"],
64 | },
65 | },
66 | }),
67 | new HtmlWebPackPlugin({
68 | template: "./src/index.html",
69 | }),
70 | ],
71 | };
72 |
--------------------------------------------------------------------------------