├── .adr.json ├── .codeclimate.yml ├── .coveralls.yml ├── .editorconfig ├── .github └── FUNDING.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── ISSUE_TEMPLATE.md ├── LICENSE ├── README.md ├── _config.yml ├── deploy ├── package.json ├── serverless.yml └── yarn.lock ├── docs ├── README.md ├── adr │ ├── 0001-基本的-angular-应用替换.md │ ├── 0002-重构项目目录.md │ ├── 0003-使用自定义创建-element-方法.md │ ├── 0004-支持子-app-路由.md │ ├── 0005-支持单个应用多个路由入口.md │ ├── 0006-创建调试用命令行.md │ ├── 0007-base-path-支持问题.md │ ├── 0008-生成打包脚本.md │ ├── 0009-考虑-loading-动画.md │ ├── 0010-清空-parentelement-dom-下的所有-children.md │ ├── 0011-支持组件之间跳转.md │ ├── 0012-assets-目录合并问题.md │ ├── 0013-修复-app-修改-host-url-带来的问题.md │ ├── 0014-cli-支持创建项目.md │ ├── 0015-支持在-cli-中使用中文.md │ ├── 0016-使用-iframe-支持传统应用.md │ ├── 0017-应用间切换保存最后的状态.md │ ├── 0018-支持-iframe-与-angular-应用切换使用.md │ ├── 0019-支持在配置文件中使用-link-和配置根据链接来区别.md │ ├── 0020-使用-cli-来将应用设置成-mooa-应用.md │ ├── 0021-编写-mooa-client-来支持其它非-angular-应用.md │ ├── 0022-提供一个预加载的-api来加载对应的-dom.md │ ├── 0023-支持-zone.min.js-可配置来兼容应用.md │ ├── 0024-通过-index.html-中的标签来匹配根节点元素.md │ ├── 0025-支持可选参数来过滤-js-和-css.md │ ├── 0026-支持-lazyload-子应用.md │ └── README.md ├── mooa-app.jpg ├── mooa-design.jpg ├── mooa-graph.jpg ├── mooa-workflow.sketch ├── mooa.png ├── mooa.sketch └── workflow.png ├── examples ├── .angular-cli.json ├── apps.txt ├── karma.conf.js ├── package.json ├── protractor.conf.js ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── games │ │ │ ├── games.component.css │ │ │ ├── games.component.html │ │ │ ├── games.component.spec.ts │ │ │ └── games.component.ts │ │ ├── graph │ │ │ ├── graph.component.css │ │ │ ├── graph.component.html │ │ │ ├── graph.component.spec.ts │ │ │ ├── graph.component.ts │ │ │ └── graph.module.ts │ │ ├── home │ │ │ ├── home.component.css │ │ │ ├── home.component.html │ │ │ ├── home.component.spec.ts │ │ │ ├── home.component.ts │ │ │ └── home.module.ts │ │ ├── navbar │ │ │ ├── navbar.component.css │ │ │ ├── navbar.component.html │ │ │ ├── navbar.component.spec.ts │ │ │ └── navbar.component.ts │ │ ├── pipes │ │ │ └── SafePipe.ts │ │ ├── reader │ │ │ ├── reader.component.css │ │ │ ├── reader.component.html │ │ │ ├── reader.component.spec.ts │ │ │ └── reader.component.ts │ │ └── shopping │ │ │ ├── shopping.component.css │ │ │ ├── shopping.component.html │ │ │ ├── shopping.component.spec.ts │ │ │ └── shopping.component.ts │ ├── assets │ │ ├── .gitkeep │ │ ├── app1 │ │ │ ├── 0.chunk.js │ │ │ ├── 1.chunk.js │ │ │ ├── 3rdpartylicenses.txt │ │ │ ├── assets │ │ │ │ ├── avatar.jpg │ │ │ │ └── zone.js │ │ │ ├── cors.py │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ ├── inline.bundle.js │ │ │ ├── main.bundle.js │ │ │ ├── polyfills.bundle.js │ │ │ └── styles.bundle.css │ │ ├── apps.json │ │ ├── avatar.jpg │ │ ├── data │ │ │ └── company.json │ │ ├── help │ │ │ ├── avatar.jpg │ │ │ ├── index.html │ │ │ ├── inline.bundle.js │ │ │ ├── main.bundle.js │ │ │ ├── polyfills.bundle.js │ │ │ └── styles.bundle.css │ │ ├── iframe.html │ │ ├── mifa.css │ │ ├── styles.css │ │ └── zone.min.js │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.css │ ├── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ └── typings.d.ts ├── tsconfig.json ├── tslint.json └── yarn.lock ├── jest.setup.js ├── package.json ├── rollup.config.ts ├── src ├── cli.ts ├── cli │ ├── cli.help.ts │ ├── create.ts │ ├── generate.ts │ └── update.ts ├── helper │ ├── app.helper.ts │ ├── assets.helper.ts │ ├── dom.utils.ts │ ├── loader.helper.ts │ ├── status.helper.ts │ └── timeouts.ts ├── lifecycles │ ├── bootstrap.ts │ ├── load.ts │ ├── mount.ts │ ├── unload.ts │ └── unmount.ts ├── loader │ └── mooa.loader.ts ├── model │ ├── IAppOption.ts │ ├── MooaOption.ts │ └── constants.ts ├── mooa.ts ├── platform │ └── platform.ts └── router.ts ├── test ├── helper │ ├── app.helper.spec.ts │ ├── assets-loader.helper.spec.ts │ ├── dom.utils.spec.ts │ └── status.helper.spec.ts ├── lifecycyle │ ├── bootstrap.spec.ts │ ├── load.spec.ts │ ├── mount.spec.ts │ ├── unload.spec.ts │ └── unmount.spec.ts ├── loader │ ├── loader.bootstrap.spec.ts │ ├── loader.unload.spec.ts │ ├── loader.unmount.spec.ts │ └── mooa.loader.spec.ts ├── mooa.spec.ts └── platform │ └── platform.spec.ts ├── tools ├── gh-pages-publish.ts └── semantic-release-prepare.ts ├── tsconfig.json ├── tslint.json └── yarn.lock /.adr.json: -------------------------------------------------------------------------------- 1 | {"language":"zh-cn","path":"docs/adr/","prefix":"","digits":4} -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | tslint: 4 | channel: beta 5 | config: tslint.json 6 | enabled: true 7 | exclude_patterns: 8 | - "config" 9 | - "**/*.d.ts" 10 | - "**/*.spec.ts" 11 | - "examples" 12 | - "test" 13 | ratings: 14 | paths: 15 | - "src/**/*.ts" 16 | -------------------------------------------------------------------------------- /.coveralls.yml: -------------------------------------------------------------------------------- 1 | repo_token: 3iZQAlQwhiwRlA180KWNYlIsBTUQV8Ofj 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: phodal 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | custom: # Replace with a single custom sponsorship URL 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /dist-server 6 | /tmp 7 | /out-tsc 8 | 9 | # dependencies 10 | /node_modules 11 | 12 | # IDEs and editors 13 | /.idea 14 | .project 15 | .classpath 16 | .c9/ 17 | *.launch 18 | .settings/ 19 | *.sublime-workspace 20 | 21 | # IDE - VSCode 22 | .vscode/* 23 | !.vscode/settings.json 24 | !.vscode/tasks.json 25 | !.vscode/launch.json 26 | !.vscode/extensions.json 27 | 28 | # misc 29 | /.sass-cache 30 | /connect.lock 31 | /coverage 32 | /libpeerconnection.log 33 | npm-debug.log 34 | testem.log 35 | /typings 36 | 37 | # e2e 38 | /e2e/*.js 39 | /e2e/*.map 40 | 41 | # System Files 42 | .DS_Store 43 | Thumbs.db 44 | apps/ 45 | examples/node_modules/ 46 | deploy/node_modules/ 47 | .rpt2_cache/ 48 | docs/mooa 49 | examples/dist 50 | apps.json 51 | .serverless/ 52 | yarn-error.log 53 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | examples/ 2 | apps/ 3 | docs/ 4 | e2e/ 5 | test/ 6 | coverage/ 7 | deploy/ 8 | tools/ 9 | rollup.config.ts 10 | tsconfig.json 11 | tslint.json 12 | yarn.lock 13 | .travis.yml 14 | .editorconfig 15 | .adr.json 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | branches: 3 | only: 4 | - master 5 | - /^greenkeeper/.*$/ 6 | cache: 7 | yarn: true 8 | directories: 9 | - node_modules 10 | notifications: 11 | email: false 12 | node_js: 13 | - node 14 | script: 15 | - npm run test:prod && npm run build 16 | after_success: 17 | - npm run report-coverage 18 | - npm run deploy-docs 19 | - npm run semantic-release 20 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | ## I'm submitting a... 8 | 9 |

10 | [ ] Regression (a behavior that used to work and stopped working in a new release)
11 | [ ] Bug report  
12 | [ ] Feature request
13 | [ ] Documentation issue or request
14 | [ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
15 | 
16 | 17 | ## Current behavior 18 | 19 | 20 | 21 | ## Expected behavior 22 | 23 | 24 | 25 | ## Minimal reproduction of the problem with instructions 26 | 30 | 31 | ## What is the motivation / use case for changing the behavior? 32 | 33 | 34 | 35 | ## Environment 36 | 37 |

38 | Angular version: X.Y.Z
39 | 
40 | 
41 | Browser:
42 | - [ ] Chrome (desktop) version XX
43 | - [ ] Chrome (Android) version XX
44 | - [ ] Chrome (iOS) version XX
45 | - [ ] Firefox version XX
46 | - [ ] Safari (desktop) version XX
47 | - [ ] Safari (iOS) version XX
48 | - [ ] IE version XX
49 | - [ ] Edge version XX
50 |  
51 | For Tooling issues:
52 | - Node version: XX  
53 | - Platform:  
54 | 
55 | Others:
56 | 
57 | 
58 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | License (MIT) 2 | 3 | Copyright (c) 2013-2014 Christopher Simpkins [single-spa](https://github.com/CanopyTax/single-spa) 4 | Copyright (c) 2018 Phodal HUANG 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | 24 | LICENSE(APACHE 2.0) 25 | Copyright (c) 2017-2018 Robin Coma Delperier [single-spa-angular-cli](https://github.com/PlaceMe-SAS/single-spa-angular-cli) 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mooa 2 | 3 | [![Build Status](https://travis-ci.org/phodal/mooa.svg?branch=master)](https://travis-ci.org/phodal/mooa) 4 | [![Coverage Status](https://coveralls.io/repos/github/phodal/mooa/badge.svg?branch=master)](https://coveralls.io/github/phodal/mooa?branch=master) 5 | [![Maintainability](https://api.codeclimate.com/v1/badges/99132c580e1d74690def/maintainability)](https://codeclimate.com/github/phodal/mooa/maintainability) 6 | [![node](https://img.shields.io/node/v/mooa.svg)](https://www.npmjs.com/package/mooa) 7 | [![npm](https://img.shields.io/npm/v/mooa.svg)](https://www.npmjs.com/mooa) 8 | 9 | > A single SPA Utils for Angular 2+ 10 | 11 | Simliar Projects: [https://github.com/worktile/ngx-planet](https://github.com/worktile/ngx-planet) (Production Ready) 12 | 13 | based on [single-spa](https://github.com/CanopyTax/single-spa) && [single-spa-angular-cli](https://github.com/PlaceMe-SAS/single-spa-angular-cli) 14 | 15 | difference: 16 | 17 | - Host <-> Apps Architecture 18 | - Configurable App (no loader require) 19 | - Independent App in Different Repo and runnable 20 | 21 | ![Mooa Architecture](docs/mooa.png) 22 | 23 | Examples: see in [examples/](examples) 24 | 25 | Online Demo: 26 | 27 | 1. [http://mooa.pho.im/](http://mooa.pho.im/) (host in AWS S3) 28 | 2. [http://mooa.phodal.com/](http://mooa.phodal.com/) (host in GitHub Pages) 29 | 30 | Features: 31 | 32 | 1. SPA by Configurable file, ex: ``apps.json`` 33 | 2. Pluggable APP 34 | 3. support Child APP navigate 35 | 4. CLI for Generate Config 36 | 37 | Goal: 38 | 39 | 1. 构建插件化的 Web 开发平台,满足业务快速变化及分布式多团队并行开发的需求 40 | 2. 构建服务化的中间件,搭建高可用及高复用的前端微服务平台 41 | 3. 支持前端的独立交付及部署 42 | 43 | Usecase 44 | --- 45 | 46 | > If you are mooa, please provide you case to help this project. 47 | 48 | ### Theory 49 | 50 | [Research and Application of Micro Frontends](https://iopscience.iop.org/article/10.1088/1757-899X/490/6/062082/meta) 51 | 52 | Boilerplate 53 | --- 54 | 55 | App Boilerplate: [https://github.com/phodal/mooa-boilerplate](https://github.com/phodal/mooa-boilerplate) 56 | 57 | Usage 58 | --- 59 | 60 | ### 1. Install mooa 61 | 62 | in Host and Child App 63 | 64 | ```sh 65 | yarn add mooa 66 | ``` 67 | 68 | ### 2. Config Host 69 | 70 | 1. add get Apps logic in AppComponent (``app.component.ts``) 71 | 72 | ```typescript 73 | constructor(private renderer: Renderer2, http: HttpClient, private router: Router) { 74 | // config Mooa 75 | this.mooa = new Mooa({ 76 | mode: 'iframe', 77 | debug: false, 78 | parentElement: 'app-home', 79 | urlPrefix: 'app', 80 | switchMode: 'coexist', 81 | preload: true, 82 | includeZone: true 83 | }); 84 | http.get('/assets/apps.json') 85 | .subscribe( 86 | data => { 87 | this.createApps(data); 88 | }, 89 | err => console.log(err) 90 | ); 91 | } 92 | 93 | private createApps(data: IAppOption[]) { 94 | data.map((config) => { 95 | this.mooa.registerApplication(config.name, config, mooaRouter.matchRoute(config.prefix)); 96 | }); 97 | 98 | this.router.events.subscribe((event) => { 99 | if (event instanceof NavigationEnd) { 100 | this.mooa.reRouter(event); 101 | } 102 | }); 103 | 104 | return this.mooa.start(); 105 | } 106 | ``` 107 | 108 | ### 3. Config App 109 | 110 | 1. config App ``main.ts`` for load 111 | 112 | ```typescript 113 | import { mooaPlatform } from 'mooa'; 114 | 115 | if (environment.production) { 116 | enableProdMode(); 117 | } 118 | 119 | mooaPlatform.mount('help').then((opts) => { 120 | platformBrowserDynamic().bootstrapModule(AppModule).then((module) => { 121 | opts['attachUnmount'](module); 122 | }); 123 | }); 124 | 125 | ``` 126 | 127 | 2. setup app routing in ``app.module.ts`` 128 | 129 | ```typescript 130 | const appRoutes: Routes = [ 131 | {path: '*', component: AppComponent} 132 | ... 133 | ]; 134 | 135 | @NgModule({ 136 | declarations: [ 137 | AppComponent, 138 | ... 139 | ], 140 | imports: [ 141 | BrowserModule, 142 | RouterModule.forRoot( 143 | appRoutes 144 | ) 145 | ], 146 | providers: [ 147 | {provide: APP_BASE_HREF, useValue: mooaPlatform.appBase()}, 148 | ], 149 | bootstrap: [AppComponent] 150 | }) 151 | export class AppModule { 152 | 153 | } 154 | ``` 155 | 156 | 3. Add for handle URL Change in ``app.component.ts`` 157 | 158 | ```typescript 159 | constructor(private router: Router) { 160 | mooaPlatform.handleRouterUpdate(this.router, 'app1'); 161 | } 162 | ``` 163 | 164 | ### 4. Setup apps.json with Mooa CLI 165 | 166 | 1. install global cli 167 | 168 | ```bash 169 | npm install -g mooa 170 | ``` 171 | 172 | 2. create URL list files 173 | 174 | Examples: ``apps.txt`` 175 | 176 | ``` 177 | http://mooa.phodal.com/assets/app1 178 | http://mooa.phodal.com/assets/help 179 | ``` 180 | 181 | 3. Generate Config File 182 | 183 | ```bash 184 | mooa -g apps.txt 185 | ``` 186 | 187 | Examples: 188 | 189 | ```json 190 | [ 191 | { 192 | "name": "app1", 193 | "selector": "app-app1", 194 | "baseScriptUrl": "/assets/app1", 195 | "styles": [ 196 | "styles.bundle.css" 197 | ], 198 | "prefix": "app/app1", 199 | "scripts": [ 200 | "inline.bundle.js", 201 | "polyfills.bundle.js", 202 | "main.bundle.js" 203 | ] 204 | } 205 | ] 206 | ``` 207 | 208 | Mooa Config 209 | --- 210 | 211 | config in Host app's ``app.component.ts`` 212 | 213 | ```typescript 214 | this.mooa = new Mooa({ 215 | mode: 'iframe', 216 | debug: false, 217 | parentElement: 'app-home', 218 | urlPrefix: 'app', 219 | switchMode: 'coexist' 220 | }) 221 | ``` 222 | 223 | ### mode: 'iframe' 224 | 225 | use iframe as application container: 226 | 227 | ```html 228 | 229 | 230 | 231 | ``` 232 | 233 | ### switchMode: 'coexist' 234 | 235 | hidden application when inactive: 236 | 237 | ```html 238 | 239 |