├── app1 ├── index.css ├── README.md ├── webpack.config.js ├── package.json └── index.js ├── app2 ├── index.css ├── README.md ├── webpack.config.js ├── app.js ├── package.json └── index.js ├── app3 ├── index.css ├── README.md ├── webpack.config.js ├── app.js ├── package.json └── index.js ├── base-app ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── index.html ├── README.md ├── src │ ├── App.test.js │ ├── store │ │ └── index.js │ ├── App.js │ ├── App.css │ ├── reducers │ │ └── appReducer.js │ ├── setupProxy.js │ ├── components │ │ ├── Container.jsx │ │ ├── HelloReact.jsx │ │ ├── NavBar.jsx │ │ └── App23Container.jsx │ ├── index.css │ ├── index.js │ ├── serviceWorker.js │ ├── logo.svg │ └── helper │ │ └── system.js ├── .gitignore └── package.json ├── README.md └── .gitignore /app1/index.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app2/index.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app3/index.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /base-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /base-app/README.md: -------------------------------------------------------------------------------- 1 | # base-app 2 | 3 | - `npm i` and `npm start` 4 | - open `localhost:3000` 5 | -------------------------------------------------------------------------------- /base-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmad-fajar/demo-micro-fe/HEAD/base-app/public/favicon.ico -------------------------------------------------------------------------------- /base-app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmad-fajar/demo-micro-fe/HEAD/base-app/public/logo192.png -------------------------------------------------------------------------------- /base-app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmad-fajar/demo-micro-fe/HEAD/base-app/public/logo512.png -------------------------------------------------------------------------------- /app1/README.md: -------------------------------------------------------------------------------- 1 | # app1 2 | 3 | - `npm i` and `npm start` 4 | - `app1`'s `js` are now served at `http://0.0.0.0:9001/app1.js` 5 | -------------------------------------------------------------------------------- /app2/README.md: -------------------------------------------------------------------------------- 1 | # app1 2 | 3 | - `npm i` and `npm start` 4 | - `app2`'s `js` are now served at `http://0.0.0.0:9002/app2.js` 5 | -------------------------------------------------------------------------------- /app3/README.md: -------------------------------------------------------------------------------- 1 | # app1 2 | 3 | - `npm i` and `npm start` 4 | - `app3`'s `js` are now served at `http://0.0.0.0:9003/app3.js` 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # demo-micro-fe 2 | 3 | Simple demo for implementing micro-fe with following stacks: 4 | - react 5 | - redux 6 | - webpack 7 | - single-spa 8 | 9 | To run this example, go to each sub folder and `npm start`.
10 | Base app runs at `localhost:3000`. 11 | -------------------------------------------------------------------------------- /base-app/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /base-app/src/store/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers, createStore } from 'redux'; 2 | 3 | import appReducer from '../reducers/appReducer'; 4 | 5 | const rootReducer = combineReducers({ 6 | appManager: appReducer 7 | }); 8 | 9 | const store = createStore(rootReducer); 10 | 11 | export default store; 12 | -------------------------------------------------------------------------------- /base-app/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import './App.css'; 4 | 5 | import NavBar from './components/NavBar'; 6 | import Container from './components/Container'; 7 | 8 | function App(props) { 9 | return ( 10 |
11 | 12 | 13 |
14 | ); 15 | } 16 | 17 | export default App; 18 | -------------------------------------------------------------------------------- /.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 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /base-app/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | } 8 | 9 | .App-header { 10 | background-color: #282c34; 11 | min-height: 100vh; 12 | display: flex; 13 | flex-direction: column; 14 | align-items: center; 15 | justify-content: center; 16 | font-size: calc(10px + 2vmin); 17 | color: white; 18 | } 19 | 20 | .App-link { 21 | color: #09d3ac; 22 | } 23 | -------------------------------------------------------------------------------- /base-app/.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 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /base-app/src/reducers/appReducer.js: -------------------------------------------------------------------------------- 1 | const defaultState = { 2 | app2Txt: '', 3 | app3Txt: '', 4 | }; 5 | 6 | const appReducer = (s = defaultState, action) => { 7 | const { type, payload: p } = action; 8 | switch(type) { 9 | case 'SAVE_APP2_TXT': 10 | return { ...s, app2Txt: p } 11 | 12 | case 'SAVE_APP3_TXT': 13 | return { ...s, app3Txt: p } 14 | 15 | default: 16 | return s 17 | } 18 | }; 19 | 20 | export default appReducer; 21 | -------------------------------------------------------------------------------- /base-app/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 | -------------------------------------------------------------------------------- /base-app/src/setupProxy.js: -------------------------------------------------------------------------------- 1 | const proxy = require('http-proxy-middleware'); 2 | 3 | module.exports = app => { 4 | app.use( 5 | '/app1', 6 | proxy({ 7 | target: 'http://localhost:9001', 8 | pathRewrite: { 9 | '^/app1': '', 10 | }, 11 | }), 12 | ); 13 | 14 | app.use( 15 | '/app2', 16 | proxy({ 17 | target: 'http://localhost:9002', 18 | pathRewrite: { 19 | '^/app2': '', 20 | }, 21 | }), 22 | ); 23 | 24 | app.use( 25 | '/app3', 26 | proxy({ 27 | target: 'http://localhost:9003', 28 | pathRewrite: { 29 | '^/app3': '', 30 | }, 31 | }), 32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /app1/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | // const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 3 | 4 | const appName = 'app1'; 5 | 6 | module.exports = { 7 | entry : { 8 | [appName]: './index.js', 9 | }, 10 | output : { 11 | filename: `${appName}.js`, 12 | path : path.resolve(__dirname , 'build'), 13 | library: appName, 14 | libraryTarget: 'amd', 15 | }, 16 | module : { 17 | rules : [ 18 | {test : /\.(js)$/, use:'babel-loader'}, 19 | {test : /\.css$/, use:['style-loader', 'css-loader']}, 20 | ] 21 | }, 22 | mode:'development', 23 | plugins : [ 24 | // new CleanWebpackPlugin(), 25 | ], 26 | } 27 | -------------------------------------------------------------------------------- /app2/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | // const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 3 | 4 | const appName = 'app2'; 5 | 6 | module.exports = { 7 | entry : { 8 | [appName]: './index.js', 9 | }, 10 | output : { 11 | filename: `${appName}.js`, 12 | path : path.resolve(__dirname , 'build'), 13 | library: appName, 14 | libraryTarget: 'amd', 15 | }, 16 | module : { 17 | rules : [ 18 | {test : /\.(js)$/, use:'babel-loader'}, 19 | {test : /\.css$/, use:['style-loader', 'css-loader']}, 20 | ] 21 | }, 22 | mode:'development', 23 | plugins : [ 24 | // new CleanWebpackPlugin(), 25 | ], 26 | } 27 | -------------------------------------------------------------------------------- /app3/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | // const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 3 | 4 | const appName = 'app3'; 5 | 6 | module.exports = { 7 | entry : { 8 | [appName]: './index.js', 9 | }, 10 | output : { 11 | filename: `${appName}.js`, 12 | path : path.resolve(__dirname , 'build'), 13 | library: appName, 14 | libraryTarget: 'amd', 15 | }, 16 | module : { 17 | rules : [ 18 | {test : /\.(js)$/, use:'babel-loader'}, 19 | {test : /\.css$/, use:['style-loader', 'css-loader']}, 20 | ] 21 | }, 22 | mode:'development', 23 | plugins : [ 24 | // new CleanWebpackPlugin(), 25 | ], 26 | } 27 | -------------------------------------------------------------------------------- /base-app/src/components/Container.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route, Switch } from 'react-router-dom'; 3 | 4 | import App23Container from './App23Container'; 5 | import HelloReact from './HelloReact'; 6 | 7 | function Container(props) { 8 | return ( 9 |
10 | 11 | 12 | } 16 | /> 17 | 18 | } 22 | /> 23 | 24 | 25 |
26 | ); 27 | }; 28 | 29 | export default Container; 30 | -------------------------------------------------------------------------------- /base-app/src/components/HelloReact.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import logo from '../logo.svg'; 4 | 5 | const HelloReact = () => { 6 | return ( 7 |
8 |
9 | logo 10 |

11 | Edit src/App.js and save to reload. 12 |

13 | 19 | Learn React 20 | 21 |
22 |
23 | ); 24 | }; 25 | 26 | export default HelloReact; 27 | -------------------------------------------------------------------------------- /app2/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | 4 | const App = p => { 5 | return ( 6 |
7 | Hello App2 World 8 | 9 |
10 |

11 | Message from app3: {p.txt3} 12 |

13 |
14 | p.saveTxt2(v.currentTarget.value)} 17 | /> 18 |
19 | ); 20 | }; 21 | 22 | const mapState = s => { 23 | return { 24 | txt3: s.appManager.app3Txt, 25 | }; 26 | }; 27 | 28 | const mapDispatch = d => { 29 | return { 30 | 31 | saveTxt2: s => d(function () { 32 | return { type: 'SAVE_APP2_TXT', payload: s }; 33 | }()), 34 | 35 | }; 36 | }; 37 | 38 | export default connect(mapState, mapDispatch)(App); 39 | -------------------------------------------------------------------------------- /app3/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | 4 | const App = p => { 5 | return ( 6 |
7 | Hello App3 World 8 | 9 |
10 |

11 | Message from app2: {p.txt2} 12 |

13 |
14 | p.saveTxt3(v.currentTarget.value)} 17 | /> 18 |
19 | ); 20 | }; 21 | 22 | const mapStateToProps = s => { 23 | return { 24 | txt2: s.appManager.app2Txt, 25 | }; 26 | }; 27 | 28 | const mapDispatchToProps = d => { 29 | return { 30 | 31 | saveTxt3: s => d(function () { 32 | return { type: 'SAVE_APP3_TXT', payload: s }; 33 | }()), 34 | 35 | }; 36 | }; 37 | 38 | export default connect(mapStateToProps, mapDispatchToProps)(App); 39 | -------------------------------------------------------------------------------- /base-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "base-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "http-proxy-middleware": "^0.20.0", 7 | "react": "^16.9.0", 8 | "react-dom": "^16.9.0", 9 | "react-redux": "^7.1.1", 10 | "react-router-dom": "^5.0.1", 11 | "react-scripts": "3.1.2", 12 | "redux": "^4.0.4", 13 | "single-spa": "^4.4.1", 14 | "single-spa-react": "^2.10.2" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test", 20 | "eject": "react-scripts eject" 21 | }, 22 | "eslintConfig": { 23 | "extends": "react-app" 24 | }, 25 | "browserslist": { 26 | "production": [ 27 | ">0.2%", 28 | "not dead", 29 | "not op_mini all" 30 | ], 31 | "development": [ 32 | "last 1 chrome version", 33 | "last 1 firefox version", 34 | "last 1 safari version" 35 | ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /base-app/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 | 15 | #ex2-container { 16 | font-size: 32px; 17 | font-weight: normal; 18 | } 19 | 20 | #ex2-container p span { 21 | font-weight: bold; 22 | } 23 | 24 | #example2-header p { 25 | margin-bottom: 0; 26 | margin-top: 0; 27 | } 28 | 29 | #container23 { 30 | display: flex; 31 | height: 10em; 32 | } 33 | 34 | #container23 > div { 35 | border: 1px solid grey; 36 | box-sizing: border-box; 37 | width: 50%; 38 | } 39 | 40 | #container23 input { 41 | border-radius: 4px; 42 | height: 1.5em; 43 | } 44 | 45 | #container23 p { 46 | color: red; 47 | } 48 | -------------------------------------------------------------------------------- /app1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app1", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "NODE_ENV=development webpack-dev-server --port 9001 --host 0.0.0.0 --content-base build/" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "react": "^16.9.0", 14 | "react-dom": "^16.9.0", 15 | "single-spa-react": "^2.10.2" 16 | }, 17 | "devDependencies": { 18 | "@babel/core": "^7.6.0", 19 | "@babel/preset-env": "^7.6.0", 20 | "@babel/preset-react": "^7.0.0", 21 | "babel-loader": "^8.0.6", 22 | "css-loader": "^3.2.0", 23 | "html-webpack-plugin": "^3.2.0", 24 | "style-loader": "^1.0.0", 25 | "webpack": "^4.40.2", 26 | "webpack-cli": "^3.3.9", 27 | "webpack-dev-server": "^3.8.1" 28 | }, 29 | "babel": { 30 | "presets": [ 31 | "@babel/preset-env", 32 | "@babel/preset-react" 33 | ] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app1", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "NODE_ENV=development webpack-dev-server --port 9002 --host 0.0.0.0 --content-base build/" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "react": "^16.9.0", 14 | "react-dom": "^16.9.0", 15 | "react-redux": "^7.1.1", 16 | "redux": "^4.0.4", 17 | "single-spa-react": "^2.10.2" 18 | }, 19 | "devDependencies": { 20 | "@babel/core": "^7.6.0", 21 | "@babel/preset-env": "^7.6.0", 22 | "@babel/preset-react": "^7.0.0", 23 | "babel-loader": "^8.0.6", 24 | "css-loader": "^3.2.0", 25 | "html-webpack-plugin": "^3.2.0", 26 | "style-loader": "^1.0.0", 27 | "webpack": "^4.40.2", 28 | "webpack-cli": "^3.3.9", 29 | "webpack-dev-server": "^3.8.1" 30 | }, 31 | "babel": { 32 | "presets": [ 33 | "@babel/preset-env", 34 | "@babel/preset-react" 35 | ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app1", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "NODE_ENV=development webpack-dev-server --port 9003 --host 0.0.0.0 --content-base build/" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "react": "^16.9.0", 14 | "react-dom": "^16.9.0", 15 | "react-redux": "^7.1.1", 16 | "redux": "^4.0.4", 17 | "single-spa-react": "^2.10.2" 18 | }, 19 | "devDependencies": { 20 | "@babel/core": "^7.6.0", 21 | "@babel/preset-env": "^7.6.0", 22 | "@babel/preset-react": "^7.0.0", 23 | "babel-loader": "^8.0.6", 24 | "css-loader": "^3.2.0", 25 | "html-webpack-plugin": "^3.2.0", 26 | "style-loader": "^1.0.0", 27 | "webpack": "^4.40.2", 28 | "webpack-cli": "^3.3.9", 29 | "webpack-dev-server": "^3.8.1" 30 | }, 31 | "babel": { 32 | "presets": [ 33 | "@babel/preset-env", 34 | "@babel/preset-react" 35 | ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /base-app/src/components/NavBar.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | 3 | const SideBar = p => { 4 | const [activeItem, setActive] = useState('home'); 5 | 6 | const clickHandler = n => { 7 | setActive(n); 8 | p.history.push(`/${n}`); 9 | }; 10 | 11 | const navItem = (label, name) => { 12 | let cName = 'nav-item'; 13 | if (activeItem === name) { cName += ' active'; } 14 | 15 | return ( 16 |
  • clickHandler(name)} > 17 | {label} 18 |
  • 19 | ); 20 | }; 21 | 22 | return ( 23 |
    24 | 34 |
    35 | ); 36 | }; 37 | 38 | export default SideBar; 39 | -------------------------------------------------------------------------------- /base-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | import { registerApplication, start } from 'single-spa'; 8 | 9 | import { Provider } from 'react-redux'; 10 | import store from './store'; 11 | 12 | // import SystemJS 13 | import './helper/system'; 14 | 15 | import { BrowserRouter, Route } from 'react-router-dom'; 16 | 17 | 18 | function startApp() { 19 | registerApplication( 20 | 'app1', 21 | 22 | async () => await window.SystemJS.import('/app1/app1.js'), 23 | 24 | (location) => location.pathname.startsWith('/ex1'), 25 | 26 | { sampleProp: 'just some random string' }, 27 | ); 28 | 29 | start(); 30 | } 31 | startApp(); 32 | 33 | ReactDOM.render( 34 | ( 35 | 36 | 37 | 38 | 39 | 40 | ), 41 | document.getElementById('root') 42 | ); 43 | 44 | // If you want your app to work offline and load faster, you can change 45 | // unregister() to register() below. Note this comes with some pitfalls. 46 | // Learn more about service workers: https://bit.ly/CRA-PWA 47 | serviceWorker.unregister(); 48 | -------------------------------------------------------------------------------- /app1/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import singleSpaReact from 'single-spa-react'; 4 | import './index.css'; 5 | 6 | const AppRoot = p => { 7 | console.log('props: ', p) 8 | const s = { 9 | color: 'blue', 10 | fontSize: '48px', 11 | fontWeight: 'bold', 12 | }; 13 | return( 14 |
    15 | Hello App1 World 16 |
    17 | ) 18 | }; 19 | 20 | // ReactDOM.render(, document.getElementById('app')); 21 | 22 | const reactLifeCycles = singleSpaReact({ 23 | React, 24 | ReactDOM, 25 | domElementGetter, 26 | rootComponent: AppRoot, 27 | suppressComponentDidCatchWarning: true, 28 | }); 29 | 30 | export const bootstrap = [reactLifeCycles.bootstrap]; 31 | export const mount = [reactLifeCycles.mount]; 32 | export const unmount = [reactLifeCycles.unmount]; 33 | 34 | function domElementGetter() { 35 | const APP_NAME = 'app1'; 36 | const MOUNT_DIV = 'app'; 37 | 38 | let el = document.getElementById(APP_NAME); 39 | 40 | if (!el) { 41 | el = document.createElement('div'); 42 | el.id = APP_NAME; 43 | } 44 | 45 | let app = document.getElementById(MOUNT_DIV); 46 | if (!app) { 47 | app = app.createElement('div'); 48 | app.id = MOUNT_DIV; 49 | document.body.appendChild(app); 50 | } 51 | 52 | app.appendChild(el); 53 | 54 | return el; 55 | } 56 | -------------------------------------------------------------------------------- /base-app/src/components/App23Container.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { registerApplication, start } from 'single-spa'; 3 | import { connect } from 'react-redux'; 4 | 5 | import store from '../store'; 6 | 7 | let hasRegister = false; 8 | 9 | const App23Container = p => { 10 | const apps = [ 11 | { 12 | name: 'app2', 13 | js: '/app2/app2.js', 14 | props: { 15 | store, 16 | }, 17 | }, 18 | { 19 | name: 'app3', 20 | js: '/app3/app3.js', 21 | props: { 22 | store, 23 | }, 24 | }, 25 | ]; 26 | 27 | async function startApp() { 28 | await apps.forEach(a => { 29 | registerApplication( 30 | a.name, 31 | 32 | async () => await window.SystemJS.import(a.js), 33 | 34 | location => location.pathname.startsWith('/ex2'), 35 | 36 | { ...a.props }, 37 | ); 38 | }); 39 | 40 | start(); 41 | return; 42 | } 43 | 44 | if(!hasRegister) { 45 | startApp(); 46 | hasRegister = true; 47 | } 48 | 49 | return ( 50 |
    51 |
    52 |

    2 Apps, one page

    53 |

    Message from app2: {p.app2Txt}

    54 |

    Message from app3: {p.app3Txt}

    55 |
    56 | 57 |
    58 |
     
    59 |
     
    60 |
    61 |
    62 | ); 63 | }; 64 | 65 | const mapState = s => { 66 | const { appManager: a } = s; 67 | return { 68 | app2Txt: a.app2Txt, 69 | app3Txt: a.app3Txt, 70 | }; 71 | } 72 | 73 | export default connect(mapState)(App23Container); 74 | -------------------------------------------------------------------------------- /app2/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | import singleSpaReact from 'single-spa-react'; 5 | 6 | import App from './app'; 7 | 8 | import './index.css'; 9 | 10 | const AppRoot = p => { 11 | return ( 12 | 13 | 14 | 15 | ); 16 | }; 17 | 18 | // ReactDOM.render(, document.getElementById('container2')); 19 | 20 | const reactLifeCycles = singleSpaReact({ 21 | React, 22 | ReactDOM, 23 | domElementGetter, 24 | rootComponent: AppRoot, 25 | suppressComponentDidCatchWarning: true, 26 | }); 27 | 28 | export const bootstrap = [reactLifeCycles.bootstrap]; 29 | export const mount = [reactLifeCycles.mount]; 30 | export const unmount = [reactLifeCycles.unmount]; 31 | 32 | function domElementGetter() { 33 | const APP_NAME = 'app2'; 34 | const MOUNT_DIV = 'container2'; 35 | const APPS_CONTAINER_DIV = 'container23'; 36 | const EX2_CONTAINER_DIV = 'ex2-container'; 37 | 38 | let app = document.getElementById('app'); 39 | if (!app) { 40 | app = document.createElement('div'); 41 | app.id = 'app'; 42 | document.body.appendChild(app); 43 | } 44 | 45 | let ex2ContainerDiv = document.getElementById(EX2_CONTAINER_DIV); 46 | if (!ex2ContainerDiv) { 47 | ex2ContainerDiv = document.createElement('div'); 48 | ex2ContainerDiv.id = EX2_CONTAINER_DIV; 49 | app.appendChild(ex2ContainerDiv); 50 | } 51 | 52 | let appsContainerDiv = document.getElementById(APPS_CONTAINER_DIV); 53 | if (!appsContainerDiv) { 54 | appsContainerDiv = document.createElement('div'); 55 | appsContainerDiv.id = APPS_CONTAINER_DIV; 56 | ex2ContainerDiv.appendChild(appsContainerDiv); 57 | } 58 | 59 | let mountDiv = document.getElementById(MOUNT_DIV); 60 | if (!mountDiv) { 61 | mountDiv = document.createElement('div'); 62 | mountDiv.id = MOUNT_DIV; 63 | appsContainerDiv.appendChild(mountDiv); 64 | } 65 | 66 | let el = document.getElementById(APP_NAME); 67 | if (!el) { 68 | el = document.createElement('div'); 69 | el.id = APP_NAME; 70 | } 71 | 72 | mountDiv.appendChild(el); 73 | 74 | return el; 75 | } 76 | -------------------------------------------------------------------------------- /app3/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | import singleSpaReact from 'single-spa-react'; 5 | 6 | import App from './app'; 7 | 8 | import './index.css'; 9 | 10 | const AppRoot = p => { 11 | return ( 12 | 13 | 14 | 15 | ); 16 | }; 17 | 18 | // ReactDOM.render(, document.getElementById('container3')); 19 | 20 | const reactLifeCycles = singleSpaReact({ 21 | React, 22 | ReactDOM, 23 | domElementGetter, 24 | rootComponent: AppRoot, 25 | suppressComponentDidCatchWarning: true, 26 | }); 27 | 28 | export const bootstrap = [reactLifeCycles.bootstrap]; 29 | export const mount = [reactLifeCycles.mount]; 30 | export const unmount = [reactLifeCycles.unmount]; 31 | 32 | function domElementGetter() { 33 | const APP_NAME = 'app3'; 34 | const MOUNT_DIV = 'container3'; 35 | const APPS_CONTAINER_DIV = 'container23'; 36 | const EX2_CONTAINER_DIV = 'ex2-container'; 37 | 38 | let app = document.getElementById('app'); 39 | if (!app) { 40 | app = document.createElement('div'); 41 | app.id = 'app'; 42 | document.body.appendChild(app); 43 | } 44 | 45 | let ex2ContainerDiv = document.getElementById(EX2_CONTAINER_DIV); 46 | if (!ex2ContainerDiv) { 47 | ex2ContainerDiv = document.createElement('div'); 48 | ex2ContainerDiv.id = EX2_CONTAINER_DIV; 49 | app.appendChild(ex2ContainerDiv); 50 | } 51 | 52 | let appsContainerDiv = document.getElementById(APPS_CONTAINER_DIV); 53 | if (!appsContainerDiv) { 54 | appsContainerDiv = document.createElement('div'); 55 | appsContainerDiv.id = APPS_CONTAINER_DIV; 56 | ex2ContainerDiv.appendChild(appsContainerDiv); 57 | } 58 | 59 | let mountDiv = document.getElementById(MOUNT_DIV); 60 | if (!mountDiv) { 61 | mountDiv = document.createElement('div'); 62 | mountDiv.id = MOUNT_DIV; 63 | appsContainerDiv.appendChild(mountDiv); 64 | } 65 | 66 | let el = document.getElementById(APP_NAME); 67 | if (!el) { 68 | el = document.createElement('div'); 69 | el.id = APP_NAME; 70 | } 71 | 72 | mountDiv.appendChild(el); 73 | 74 | return el; 75 | } 76 | -------------------------------------------------------------------------------- /base-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
    37 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /base-app/src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.1/8 is considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl) 104 | .then(response => { 105 | // Ensure service worker exists, and that we really are getting a JS file. 106 | const contentType = response.headers.get('content-type'); 107 | if ( 108 | response.status === 404 || 109 | (contentType != null && contentType.indexOf('javascript') === -1) 110 | ) { 111 | // No service worker found. Probably a different app. Reload the page. 112 | navigator.serviceWorker.ready.then(registration => { 113 | registration.unregister().then(() => { 114 | window.location.reload(); 115 | }); 116 | }); 117 | } else { 118 | // Service worker found. Proceed as normal. 119 | registerValidSW(swUrl, config); 120 | } 121 | }) 122 | .catch(() => { 123 | console.log( 124 | 'No internet connection found. App is running in offline mode.' 125 | ); 126 | }); 127 | } 128 | 129 | export function unregister() { 130 | if ('serviceWorker' in navigator) { 131 | navigator.serviceWorker.ready.then(registration => { 132 | registration.unregister(); 133 | }); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /base-app/src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /base-app/src/helper/system.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SystemJS v0.20.19 Dev 3 | */ 4 | // eslint-disable-next-line 5 | !function(){"use strict";function e(e){return ut?Symbol():"@@"+e}function t(e,t){ot||(t=t.replace(at?/file:\/\/\//g:/file:\/\//g,""));var r,n=(e.message||e)+"\n "+t;r=ft&&e.fileName?new Error(n,e.fileName,e.lineNumber):new Error(n);var o=e.originalErr?e.originalErr.stack:e.stack;return r.stack=it?n+"\n "+o:o,r.originalErr=e.originalErr||e,r}function r(e,t){throw new RangeError('Unable to resolve "'+e+'" to '+t)}function n(e,t){e=e.trim();var n=t&&t.substr(0,t.indexOf(":")+1),o=e[0],i=e[1];if("/"===o&&"/"===i)return n||r(e,t),n+e;if("."===o&&("/"===i||"."===i&&("/"===e[2]||2===e.length&&(e+="/"))||1===e.length&&(e+="/"))||"/"===o){var a,s=!n||"/"!==t[n.length];if(s?(void 0===t&&r(e,t),a=t):a="/"===t[n.length+1]?"file:"!==n?(a=t.substr(n.length+2)).substr(a.indexOf("/")+1):t.substr(8):t.substr(n.length+1),"/"===o){if(!s)return t.substr(0,t.length-a.length-1)+e;r(e,t)}for(var u=a.substr(0,a.lastIndexOf("/")+1)+e,l=[],c=-1,f=0;f1&&g(e,r,i))})).then(function(t){if(void 0!==t){if(!(t instanceof l))throw new TypeError("Instantiate did not return a valid Module object.");return delete i.records[r.key],e.trace&&v(e,r,n),o[r.key]=t}var a=r.registration;if(r.registration=void 0,!a)throw new TypeError("Module instantiation did not call an anonymous or correctly named System.register.");return n.dependencies=a[0],r.importerSetters=[],n.moduleObj={},a[2]?(n.moduleObj.default=n.moduleObj.__useDefault={},n.executingRequire=a[1],n.execute=a[2]):y(e,r,n,a[1]),r}).catch(function(e){throw r.linkRecord=void 0,r.loadError=r.loadError||t(e,"Instantiating "+r.key)}))}function m(e,t,r,n,o,i){return e.resolve(t,r).then(function(r){i&&(i[t]=r);var a=o.records[r],s=n[r];if(s&&(!a||a.module&&s!==a.module))return s;if(a&&a.loadError)throw a.loadError;(!a||!s&&a.module)&&(a=d(o,r,a&&a.registration));var u=a.linkRecord;return u?h(e,a,u,n,o):a})}function v(e,t,r){e.loads=e.loads||{},e.loads[t.key]={key:t.key,deps:r.dependencies,dynamicDeps:[],depMap:r.depMap||{}}}function y(e,t,r,n){var o=r.moduleObj,i=t.importerSetters,a=!1,s=n.call(st,function(e,t){if("object"==typeof e){var r=!1;for(var n in e)t=e[n],"__useDefault"===n||n in o&&o[n]===t||(r=!0,o[n]=t);if(!1===r)return t}else{if((a||e in o)&&o[e]===t)return t;o[e]=t}for(var s=0;sthis.len&&(this.match=e,this.len=r)}}function N(e,t){if(Object.hasOwnProperty.call(e,t))return t;var r={name:t,match:void 0,len:0};return Object.keys(e).forEach(z,r),r.match}function J(e,t,r,n){if("file:///"===e.substr(0,8)){if(Ft)return $(e,t,r,n);throw new Error("Unable to fetch file URLs in this environment.")}e=e.replace(/#/g,"%23");var o={headers:{Accept:"application/x-es-module, */*"}};return r&&(o.integrity=r),t&&("string"==typeof t&&(o.headers.Authorization=t),o.credentials="include"),fetch(e,o).then(function(e){if(e.ok)return n?e.arrayBuffer():e.text();throw new Error("Fetch error: "+e.status+" "+e.statusText)})}function $(e,t,r,n){return new Promise(function(r,o){function i(){r(n?s.response:s.responseText)}function a(){o(new Error("XHR error: "+(s.status?" ("+s.status+(s.statusText?" "+s.statusText:"")+")":"")+" loading "+e))}e=e.replace(/#/g,"%23");var s=new XMLHttpRequest;n&&(s.responseType="arraybuffer"),s.onreadystatechange=function(){4===s.readyState&&(0==s.status?s.response?i():(s.addEventListener("error",a),s.addEventListener("load",i)):200===s.status?i():a())},s.open("GET",e,!0),s.setRequestHeader&&(s.setRequestHeader("Accept","application/x-es-module, */*"),t&&("string"==typeof t&&s.setRequestHeader("Authorization",t),s.withCredentials=!0)),s.send(null)})}function B(e,t,r,n){return"file:///"!=e.substr(0,8)?Promise.reject(new Error('Unable to fetch "'+e+'". Only file URLs of the form file:/// supported running in Node.')):(Lt=Lt||require("fs"),e=at?e.replace(/\//g,"\\").substr(8):e.substr(7),new Promise(function(t,r){Lt.readFile(e,function(e,o){if(e)return r(e);if(n)t(o);else{var i=o+"";"\ufeff"===i[0]&&(i=i.substr(1)),t(i)}})}))}function W(){throw new Error("No fetch method is defined for this environment.")}function G(){return{pluginKey:void 0,pluginArgument:void 0,pluginModule:void 0,packageKey:void 0,packageConfig:void 0,load:void 0}}function H(e,t,r){var n=G();if(r){var o;t.pluginFirst?-1!==(o=r.lastIndexOf("!"))&&(n.pluginArgument=n.pluginKey=r.substr(0,o)):-1!==(o=r.indexOf("!"))&&(n.pluginArgument=n.pluginKey=r.substr(o+1)),n.packageKey=N(t.packages,r),n.packageKey&&(n.packageConfig=t.packages[n.packageKey])}return n}function Z(e,t){var r=this[St],n=G(),o=H(this,r,t),i=this;return Promise.resolve().then(function(){var r=e.lastIndexOf("#?");if(-1===r)return Promise.resolve(e);var n=he.call(i,e.substr(r+2));return me.call(i,n,t,!0).then(function(t){return t?e.substr(0,r):"@empty"})}).then(function(e){var a=ne(r.pluginFirst,e);return a?(n.pluginKey=a.plugin,Promise.all([ee.call(i,r,a.argument,o&&o.pluginArgument||t,n,o,!0),i.resolve(a.plugin,t)]).then(function(e){if(n.pluginArgument=e[0],n.pluginKey=e[1],n.pluginArgument===n.pluginKey)throw new Error("Plugin "+n.pluginArgument+" cannot load itself, make sure it is excluded from any wildcard meta configuration via a custom loader: false rule.");return oe(r.pluginFirst,e[0],e[1])})):ee.call(i,r,e,o&&o.pluginArgument||t,n,o,!1)}).then(function(e){return ve.call(i,e,t,o)}).then(function(e){return re.call(i,r,e,n),n.pluginKey||!n.load.loader?e:i.resolve(n.load.loader,e).then(function(t){return n.pluginKey=t,n.pluginArgument=e,e})}).then(function(e){return i[jt][e]=n,e})}function X(e,t){var r=ne(e.pluginFirst,t);if(r){var n=X.call(this,e,r.plugin);return oe(e.pluginFirst,Q.call(this,e,r.argument,void 0,!1,!1),n)}return Q.call(this,e,t,void 0,!1,!1)}function Y(e,t){var r=this[St],n=G(),o=o||H(this,r,t),i=ne(r.pluginFirst,e);return i?(n.pluginKey=Y.call(this,i.plugin,t),oe(r.pluginFirst,V.call(this,r,i.argument,o.pluginArgument||t,n,o,!!n.pluginKey),n.pluginKey)):V.call(this,r,e,o.pluginArgument||t,n,o,!!n.pluginKey)}function Q(e,t,r,o,i){var a=n(t,r||nt);if(a)return T(e.baseURL,e.paths,a);if(o){var s=N(e.map,t);if(s&&(t=e.map[s]+t.substr(s.length),a=n(t,nt)))return T(e.baseURL,e.paths,a)}if(this.registry.has(t))return t;if("@node/"===t.substr(0,6))return t;var u=i&&"/"!==t[t.length-1],l=T(e.baseURL,e.paths,u?t+"/":t);return u?l.substr(0,l.length-1):l}function V(e,t,r,n,o,i){if(o&&o.packageConfig&&"."!==t[0]){var a=o.packageConfig.map,s=a&&N(a,t);if(s&&"string"==typeof a[s]){var u=ue(this,e,o.packageConfig,o.packageKey,s,t,n,i);if(u)return u}}var l=Q.call(this,e,t,r,!0,!0),c=de(e,l);if(n.packageKey=c&&c.packageKey||N(e.packages,l),!n.packageKey)return l;if(-1!==e.packageConfigKeys.indexOf(l))return n.packageKey=void 0,l;n.packageConfig=e.packages[n.packageKey]||(e.packages[n.packageKey]=Ee());var f=l.substr(n.packageKey.length+1);return ae(this,e,n.packageConfig,n.packageKey,f,n,i)}function ee(e,t,r,n,o,i){var a=this;return Et.then(function(){if(o&&o.packageConfig&&"./"!==t.substr(0,2)){var r=o.packageConfig.map,s=r&&N(r,t);if(s)return ce(a,e,o.packageConfig,o.packageKey,s,t,n,i)}return Et}).then(function(o){if(o)return o;var s=Q.call(a,e,t,r,!0,!0),u=de(e,s);return n.packageKey=u&&u.packageKey||N(e.packages,s),n.packageKey?-1!==e.packageConfigKeys.indexOf(s)?(n.packageKey=void 0,n.load=te(),n.load.format="json",n.load.loader="",Promise.resolve(s)):(n.packageConfig=e.packages[n.packageKey]||(e.packages[n.packageKey]=Ee()),(u&&!n.packageConfig.configured?pe(a,e,u.configPath,n):Et).then(function(){var t=s.substr(n.packageKey.length+1);return le(a,e,n.packageConfig,n.packageKey,t,n,i)})):Promise.resolve(s)})}function te(){return{extension:"",deps:void 0,format:void 0,loader:void 0,scriptLoad:void 0,globals:void 0,nonce:void 0,integrity:void 0,sourceMap:void 0,exports:void 0,encapsulateGlobal:!1,crossOrigin:void 0,cjsRequireDetection:!0,cjsDeferDepsExecute:!1,esModule:!1}}function re(e,t,r){r.load=r.load||te();var n,o=0;for(var i in e.meta)if(-1!==(n=i.indexOf("*"))&&i.substr(0,n)===t.substr(0,n)&&i.substr(n+1)===t.substr(t.length-i.length+n+1)){var a=i.split("/").length;a>o&&(o=a),F(r.load,e.meta[i],o!==a)}if(e.meta[t]&&F(r.load,e.meta[t],!1),r.packageKey){var s=t.substr(r.packageKey.length+1),u={};if(r.packageConfig.meta){o=0;ge(r.packageConfig.meta,s,function(e,t,r){r>o&&(o=r),F(u,t,r&&o>r)}),F(r.load,u,!1)}!r.packageConfig.format||r.pluginKey||r.load.loader||(r.load.format=r.load.format||r.packageConfig.format)}}function ne(e,t){var r,n,o=e?t.indexOf("!"):t.lastIndexOf("!");if(-1!==o)return e?(r=t.substr(o+1),n=t.substr(0,o)):(r=t.substr(0,o),n=t.substr(o+1)||r.substr(r.lastIndexOf(".")+1)),{argument:r,plugin:n}}function oe(e,t,r){return e?r+"!"+t:t+"!"+r}function ie(e,t,r,n,o){if(!n||!t.defaultExtension||"/"===n[n.length-1]||o)return n;var i=!1;if(t.meta&&ge(t.meta,n,function(e,t,r){if(0===r||e.lastIndexOf("*")!==e.length-1)return i=!0}),!i&&e.meta&&ge(e.meta,r+"/"+n,function(e,t,r){if(0===r||e.lastIndexOf("*")!==e.length-1)return i=!0}),i)return n;var a="."+t.defaultExtension;return n.substr(n.length-a.length)!==a?n+a:n}function ae(e,t,r,n,o,i,a){if(!o){if(!r.main)return n;o="./"===r.main.substr(0,2)?r.main.substr(2):r.main}if(r.map){var s="./"+o,u=N(r.map,s);if(u||(s="./"+ie(t,r,n,o,a))!=="./"+o&&(u=N(r.map,s)),u){var l=ue(e,t,r,n,u,s,i,a);if(l)return l}}return n+"/"+ie(t,r,n,o,a)}function se(e,t,r){return!(t.substr(0,e.length)===e&&r.length>e.length)}function ue(e,t,r,n,o,i,a,s){"/"===i[i.length-1]&&(i=i.substr(0,i.length-1));var u=r.map[o];if("object"==typeof u)throw new Error("Synchronous conditional normalization not supported sync normalizing "+o+" in "+n);if(se(o,u,i)&&"string"==typeof u)return V.call(e,t,u+i.substr(o.length),n+"/",a,a,s)}function le(e,t,r,n,o,i,a){if(!o){if(!r.main)return Promise.resolve(n);o="./"===r.main.substr(0,2)?r.main.substr(2):r.main}var s,u;return r.map&&(s="./"+o,(u=N(r.map,s))||(s="./"+ie(t,r,n,o,a))!=="./"+o&&(u=N(r.map,s))),(u?ce(e,t,r,n,u,s,i,a):Et).then(function(e){return e?Promise.resolve(e):Promise.resolve(n+"/"+ie(t,r,n,o,a))})}function ce(e,t,r,n,o,i,a,s){"/"===i[i.length-1]&&(i=i.substr(0,i.length-1));var u=r.map[o];if("string"==typeof u)return se(o,u,i)?ee.call(e,t,u+i.substr(o.length),n+"/",a,a,s).then(function(t){return ve.call(e,t,n+"/",a)}):Et;var l=[],c=[];for(var d in u){var p=he(d);c.push({condition:p,map:u[d]}),l.push(f.prototype.import.call(e,p.module,n))}return Promise.all(l).then(function(e){for(var t=0;t1?o instanceof Array?r[n]=[].concat(o):"object"==typeof o?r[n]=be(o,t-1):"packageConfig"!==n&&(r[n]=o):r[n]=o}return r}function we(e,t){var r=e[t];return r instanceof Array?e[t].concat([]):"object"==typeof r?be(r,3):e[t]}function xe(e){if(e){if(-1!==Or.indexOf(e))return we(this[St],e);throw new Error('"'+e+'" is not a valid configuration name. Must be one of '+Or.join(", ")+".")}for(var t={},r=0;r "+o.paths[a]+" is no longer supported as wildcards are deprecated."),delete o.paths[a])}if(e.defaultJSExtensions&&R.call(o,"The defaultJSExtensions configuration option is deprecated.\n Use packages defaultExtension instead.",!0),"boolean"==typeof e.pluginFirst&&(o.pluginFirst=e.pluginFirst),e.map)for(var a in e.map){var s=e.map[a];if("string"==typeof s){var u=Q.call(r,o,s,void 0,!1,!1);"/"===u[u.length-1]&&":"!==a[a.length-1]&&"/"!==a[a.length-1]&&(u=u.substr(0,u.length-1)),o.map[a]=u}else{m=(m=Q.call(r,o,"/"!==a[a.length-1]?a+"/":a,void 0,!0,!0)).substr(0,m.length-1);var l=o.packages[m];l||((l=o.packages[m]=Ee()).defaultExtension=""),Oe(l,{map:s},m,!1,o)}}if(e.packageConfigPaths){for(var c=[],f=0;ft.index)return!0;return!1}It.lastIndex=tr.lastIndex=rr.lastIndex=0;var r,n=[],o=[],i=[];if(e.length/e.split("\n").length<200){for(;r=rr.exec(e);)o.push([r.index,r.index+r[0].length]);for(;r=tr.exec(e);)t(o,r)||i.push([r.index+r[1].length,r.index+r[0].length-1])}for(;r=It.exec(e);)if(!t(o,r)&&!t(i,r)){var a=r[1].substr(1,r[1].length-2);if(a.match(/"|'/))continue;n.push(a)}return n}function Fe(e){if(-1===nr.indexOf(e)){try{var t=st[e]}catch(t){nr.push(e)}this(e,t)}}function Ke(e){if("string"==typeof e)return q(e,st);if(!(e instanceof Array))throw new Error("Global exports must be a string or array.");for(var t={},r=0;r1;)e=e[n=o.shift()]=e[n]||{};void 0===e[n=o.shift()]&&(e[n]=r)}function Ve(e,t){var r=e.match(br);if(r)for(var n=r[0].match(wr),o=0;o