├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── app1 ├── .editorconfig ├── .gitignore ├── README.md ├── angular.json ├── browserslist ├── e2e │ ├── protractor.conf.js │ ├── src │ │ ├── app.e2e-spec.ts │ │ └── app.po.ts │ └── tsconfig.json ├── extra-webpack.config.js ├── karma.conf.js ├── package-lock.json ├── package.json ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ └── empty-route │ │ │ └── empty-route.component.ts │ ├── assets │ │ ├── .gitkeep │ │ └── yoshi.png │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.single-spa.ts │ ├── main.ts │ ├── polyfills.ts │ ├── single-spa │ │ ├── asset-url.ts │ │ └── single-spa-props.ts │ ├── styles.css │ └── test.ts ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.spec.json └── tslint.json ├── app2 ├── .editorconfig ├── .gitignore ├── README.md ├── angular.json ├── browserslist ├── e2e │ ├── protractor.conf.js │ ├── src │ │ ├── app.e2e-spec.ts │ │ └── app.po.ts │ └── tsconfig.json ├── extra-webpack.config.js ├── karma.conf.js ├── package-lock.json ├── package.json ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ └── empty-route │ │ │ └── empty-route.component.ts │ ├── assets │ │ └── .gitkeep │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.single-spa.ts │ ├── main.ts │ ├── polyfills.ts │ ├── single-spa │ │ ├── asset-url.ts │ │ └── single-spa-props.ts │ ├── styles.css │ └── test.ts ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.spec.json └── tslint.json ├── navbar ├── .editorconfig ├── .gitignore ├── README.md ├── angular.json ├── browserslist ├── e2e │ ├── protractor.conf.js │ ├── src │ │ ├── app.e2e-spec.ts │ │ └── app.po.ts │ └── tsconfig.json ├── extra-webpack.config.js ├── karma.conf.js ├── package-lock.json ├── package.json ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── empty-route │ │ │ └── empty-route.component.ts │ │ └── primary-nav │ │ │ ├── primary-nav.component.css │ │ │ ├── primary-nav.component.html │ │ │ ├── primary-nav.component.spec.ts │ │ │ └── primary-nav.component.ts │ ├── assets │ │ └── .gitkeep │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.single-spa.ts │ ├── main.ts │ ├── polyfills.ts │ ├── single-spa │ │ ├── asset-url.ts │ │ └── single-spa-props.ts │ ├── styles.css │ └── test.ts ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.spec.json └── tslint.json └── root-html-file ├── index.html ├── package-lock.json └── package.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [joeldenning] 4 | patreon: # Replace with a single Patreon username 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 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # THIS IS NOT HOW YOU SHOULD DO DEPLOYMENTS FOR YOUR ORGANIZATION 2 | # 3 | # First off, the sed stuff for modifying import maps is quite hacky and shouldn't 4 | # be what you rely on. 5 | # 6 | # Secondly, each angular app should be in a separate repo and deployed separately, as 7 | # explained in the README.md. 8 | language: node_js 9 | node_js: 10 | - "node" 11 | script: 12 | - cd navbar && npm ci && npm run build:single-spa 13 | - cd ../app1 && npm ci && npm run build:single-spa 14 | - cd ../app2 && npm ci && npm run build:single-spa 15 | - cd .. && mkdir static && cp -a navbar/dist app1/dist app2/dist static 16 | - cp root-html-file/index.html static 17 | - sed -i 's/http:\/\/localhost:4201\/main/\/dist\/app1\/main/g' static/index.html 18 | - sed -i 's/http:\/\/localhost:4202\/main/\/dist\/app2\/main/g' static/index.html 19 | - sed -i 's/http:\/\/localhost:4300\/main/\/dist\/navbar\/main/g' static/index.html 20 | - cp static/index.html static/200.html 21 | - echo "The static directory uploaded to surge:" 22 | - find static 23 | deploy: 24 | provider: surge 25 | project: ./static/ 26 | domain: coexisting-angular-microfrontends.surge.sh 27 | skip_cleanup: true -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Joel Denning 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/joeldenning/coexisting-angular-microfrontends.svg?branch=master)](https://travis-ci.org/joeldenning/coexisting-angular-microfrontends) 2 | 3 | # Coexisting Angular Microfrontends 4 | Demo: http://coexisting-angular-microfrontends.surge.sh 5 | 6 | This is a starter-kit / example repository for people who want to have multiple angular microfrontends coexist within a single page. Each 7 | of the angular applications was created and is managed by Angular CLI. 8 | 9 | It uses [single-spa](https://single-spa.js.org) to pull this off, which means that you can even add React, Vue, or other frameworks as 10 | additional microfrontends. 11 | 12 | For mapping routes to applications it uses [single-spa-layout](https://single-spa.github.io/single-spa.js.org/docs/layout-overview/). 13 | 14 | ## An important note 15 | This github repository has four projects all in one repo. But when you do this yourself, **you'll want to have one git repo per 16 | angular application**. The root-html-file project should also be in its own repo. This is what lets different teams and developers be in 17 | charge of different microfrontends. 18 | 19 | ## Local development -- one app at a time 20 | [Tutorial video](https://www.youtube.com/watch?v=vjjcuIxqIzY&list=PLLUD8RtHvsAOhtHnyGx57EYXoaNsxGrTU&index=4) 21 | 22 | With single-spa, it is preferred to run `ng serve` in only one single-spa application at a time, while using a deployed 23 | version of the other applications. This makes for an awesome developer experience where you can boot up just one 24 | microfrontend at a time, not even having to clone, npm install, or boot up all of the other ones. 25 | 26 | To try this out, clone the repo and run the following commands: 27 | ```sh 28 | cd app1 29 | npm i 30 | npm start 31 | ``` 32 | 33 | Now go to http://coexisting-angular-microfrontends.surge.sh in a browser. Click on the yellowish rectangle at the bottom right. Then click on `app1`. Change the module url to http://localhost:4201/main.js. Then apply the override and reload the page. This will have change app1 to load from your localhost instead of from surge.sh. As you modify the code locally, it will 34 | reload the page on coexisting-angular-microfrontends.surge.sh. See https://github.com/joeldenning/import-map-overrides for more info on this. 35 | 36 | ## Local development -- all at once 37 | It is preferred to only run one app at a time. But if you need to run them all locally, you can do so with the following instructions 38 | 39 | ```sh 40 | # First terminal tab 41 | cd root-html-file 42 | npm install 43 | npm start 44 | ``` 45 | ```sh 46 | # Second terminal tab 47 | cd app1 48 | npm install 49 | npm start 50 | ``` 51 | 52 | ```sh 53 | # Third terminal tab 54 | cd app2 55 | npm install 56 | npm start 57 | ``` 58 | 59 | ```sh 60 | # Fourth terminal tab 61 | cd navbar 62 | npm install 63 | npm start 64 | ``` 65 | 66 | Now go to http://localhost:4200 in a browser. Note that you can change any of the ports for the projects by modifying the Import Map inside of 67 | root-html-file/index.html. 68 | 69 | If you get serious about deploying your code, you'll want to make it no longer necessary to boot up all of the apps in order to do anything. 70 | When you get to that point, check out [import-map-overrides](https://github.com/joeldenning/import-map-overrides/), which lets you go to 71 | a deployed environment and override the [Import Map](https://github.com/WICG/import-maps) for just one microfrontend at a time. The 72 | import-map-overrides library is already loaded in the index.html of root-html-file, so you can start using it immediately. You can make your 73 | deployed environment overridable, just like you can do overrides on http://coexisting-angular-microfrontends.surge.sh 74 | 75 | ## More documentation 76 | Go to https://github.com/CanopyTax/single-spa-angular to learn how all of this works. 77 | -------------------------------------------------------------------------------- /app1/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://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 | -------------------------------------------------------------------------------- /app1/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events.json 15 | speed-measure-plugin.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | -------------------------------------------------------------------------------- /app1/README.md: -------------------------------------------------------------------------------- 1 | # App1 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.1.0. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /app1/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "app1": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "", 10 | "sourceRoot": "src", 11 | "prefix": "app1", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-builders/custom-webpack:browser", 15 | "options": { 16 | "outputPath": "dist/app1", 17 | "index": "src/index.html", 18 | "main": "src/main.single-spa.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "tsconfig.app.json", 21 | "aot": false, 22 | "assets": [ 23 | "src/favicon.ico", 24 | "src/assets" 25 | ], 26 | "styles": [ 27 | "src/styles.css" 28 | ], 29 | "scripts": [], 30 | "customWebpackConfig": { 31 | "path": "./extra-webpack.config.js" 32 | } 33 | }, 34 | "configurations": { 35 | "production": { 36 | "fileReplacements": [ 37 | { 38 | "replace": "src/environments/environment.ts", 39 | "with": "src/environments/environment.prod.ts" 40 | } 41 | ], 42 | "optimization": true, 43 | "outputHashing": "all", 44 | "sourceMap": false, 45 | "extractCss": true, 46 | "namedChunks": false, 47 | "aot": true, 48 | "extractLicenses": true, 49 | "vendorChunk": false, 50 | "buildOptimizer": true, 51 | "budgets": [ 52 | { 53 | "type": "initial", 54 | "maximumWarning": "2mb", 55 | "maximumError": "5mb" 56 | } 57 | ] 58 | } 59 | } 60 | }, 61 | "serve": { 62 | "builder": "@angular-builders/custom-webpack:dev-server", 63 | "options": { 64 | "browserTarget": "app1:build" 65 | }, 66 | "configurations": { 67 | "production": { 68 | "browserTarget": "app1:build:production" 69 | } 70 | } 71 | }, 72 | "extract-i18n": { 73 | "builder": "@angular-devkit/build-angular:extract-i18n", 74 | "options": { 75 | "browserTarget": "app1:build" 76 | } 77 | }, 78 | "test": { 79 | "builder": "@angular-devkit/build-angular:karma", 80 | "options": { 81 | "main": "src/test.ts", 82 | "polyfills": "src/polyfills.ts", 83 | "tsConfig": "tsconfig.spec.json", 84 | "karmaConfig": "karma.conf.js", 85 | "assets": [ 86 | "src/favicon.ico", 87 | "src/assets" 88 | ], 89 | "styles": [ 90 | "src/styles.css" 91 | ], 92 | "scripts": [] 93 | } 94 | }, 95 | "lint": { 96 | "builder": "@angular-devkit/build-angular:tslint", 97 | "options": { 98 | "tsConfig": [ 99 | "tsconfig.app.json", 100 | "tsconfig.spec.json", 101 | "e2e/tsconfig.json" 102 | ], 103 | "exclude": [ 104 | "**/node_modules/**" 105 | ] 106 | } 107 | }, 108 | "e2e": { 109 | "builder": "@angular-devkit/build-angular:protractor", 110 | "options": { 111 | "protractorConfig": "e2e/protractor.conf.js", 112 | "devServerTarget": "app1:serve" 113 | }, 114 | "configurations": { 115 | "production": { 116 | "devServerTarget": "app1:serve:production" 117 | } 118 | } 119 | } 120 | } 121 | } 122 | }, 123 | "defaultProject": "app1", 124 | "cli": { 125 | "analytics": false 126 | } 127 | } -------------------------------------------------------------------------------- /app1/browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /app1/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | 'browserName': 'chrome' 17 | }, 18 | directConnect: true, 19 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() {} 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 31 | } 32 | }; -------------------------------------------------------------------------------- /app1/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('Welcome to app1!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /app1/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app1-root h1')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app1/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app1/extra-webpack.config.js: -------------------------------------------------------------------------------- 1 | const singleSpaAngularWebpack = require('single-spa-angular/lib/webpack').default 2 | 3 | module.exports = (angularWebpackConfig, options) => { 4 | const singleSpaWebpackConfig = singleSpaAngularWebpack(angularWebpackConfig, options) 5 | 6 | // Feel free to modify this webpack config however you'd like to 7 | return singleSpaWebpackConfig 8 | } -------------------------------------------------------------------------------- /app1/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/app1'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /app1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app1", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "npm run serve:single-spa", 7 | "build": "npm run build:single-spa", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e", 11 | "build:single-spa": "ng build --prod --deploy-url /dist/app1 --output-hashing none", 12 | "serve:single-spa": "ng serve --disable-host-check --port 4201 --deploy-url http://localhost:4201/ --live-reload false" 13 | }, 14 | "private": true, 15 | "dependencies": { 16 | "@angular-builders/custom-webpack": "^8", 17 | "@angular/animations": "~9.1.7", 18 | "@angular/common": "~9.1.7", 19 | "@angular/compiler": "~9.1.7", 20 | "@angular/core": "~9.1.7", 21 | "@angular/forms": "~9.1.7", 22 | "@angular/platform-browser": "~9.1.7", 23 | "@angular/platform-browser-dynamic": "~9.1.7", 24 | "@angular/router": "~9.1.7", 25 | "rxjs": "~6.5.5", 26 | "single-spa": "^5.5.0", 27 | "single-spa-angular": "^4.0.1", 28 | "tslib": "^1.10.0", 29 | "zone.js": "~0.10.2" 30 | }, 31 | "devDependencies": { 32 | "@angular-devkit/build-angular": "~0.901.6", 33 | "@angular/cli": "~9.1.6", 34 | "@angular/compiler-cli": "~9.1.7", 35 | "@angular/language-service": "~9.1.7", 36 | "@types/node": "^12.11.1", 37 | "@types/jasmine": "~3.3.8", 38 | "@types/jasminewd2": "~2.0.3", 39 | "codelyzer": "^5.1.2", 40 | "jasmine-core": "~3.4.0", 41 | "jasmine-spec-reporter": "~4.2.1", 42 | "karma": "~4.1.0", 43 | "karma-chrome-launcher": "~2.2.0", 44 | "karma-coverage-istanbul-reporter": "~2.0.1", 45 | "karma-jasmine": "~2.0.1", 46 | "karma-jasmine-html-reporter": "^1.4.0", 47 | "protractor": "~5.4.0", 48 | "ts-node": "~7.0.0", 49 | "tslint": "~5.15.0", 50 | "typescript": "~3.8.3" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app1/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { EmptyRouteComponent } from './empty-route/empty-route.component'; 4 | import { APP_BASE_HREF } from '@angular/common'; 5 | 6 | 7 | const routes: Routes = [ 8 | { path: '**', component: EmptyRouteComponent }, 9 | ]; 10 | 11 | @NgModule({ 12 | imports: [RouterModule.forRoot(routes)], 13 | exports: [RouterModule], 14 | providers: [ 15 | { provide: APP_BASE_HREF, useValue: '/' }, 16 | ], 17 | }) 18 | export class AppRoutingModule { } 19 | -------------------------------------------------------------------------------- /app1/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jolyndenning/coexisting-angular-microfrontends/89bfde330ce4583bd09ff3e033cb119c4664ceb3/app1/src/app/app.component.css -------------------------------------------------------------------------------- /app1/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

4 | Welcome to {{ title }}! 5 |

6 | Angular Logo 7 |
8 |

Here are some links to help you start:

9 | 10 | 21 | 22 | To App 2 from app1 23 | -------------------------------------------------------------------------------- /app1/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | imports: [ 9 | RouterTestingModule 10 | ], 11 | declarations: [ 12 | AppComponent 13 | ], 14 | }).compileComponents(); 15 | })); 16 | 17 | it('should create the app', () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.debugElement.componentInstance; 20 | expect(app).toBeTruthy(); 21 | }); 22 | 23 | it(`should have as title 'app1'`, () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | const app = fixture.debugElement.componentInstance; 26 | expect(app.title).toEqual('app1'); 27 | }); 28 | 29 | it('should render title in a h1 tag', () => { 30 | const fixture = TestBed.createComponent(AppComponent); 31 | fixture.detectChanges(); 32 | const compiled = fixture.debugElement.nativeElement; 33 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to app1!'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /app1/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { assetUrl } from 'src/single-spa/asset-url'; 3 | 4 | @Component({ 5 | selector: 'app1-root', 6 | templateUrl: './app.component.html', 7 | styleUrls: ['./app.component.css'] 8 | }) 9 | export class AppComponent { 10 | title = 'app1'; 11 | yoshiUrl = assetUrl("yoshi.png"); 12 | } 13 | -------------------------------------------------------------------------------- /app1/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AppRoutingModule } from './app-routing.module'; 5 | import { AppComponent } from './app.component'; 6 | import { EmptyRouteComponent } from './empty-route/empty-route.component'; 7 | 8 | @NgModule({ 9 | declarations: [ 10 | AppComponent, 11 | EmptyRouteComponent, 12 | ], 13 | imports: [ 14 | BrowserModule, 15 | AppRoutingModule 16 | ], 17 | providers: [], 18 | bootstrap: [AppComponent] 19 | }) 20 | export class AppModule { } 21 | -------------------------------------------------------------------------------- /app1/src/app/empty-route/empty-route.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app1-empty-route', 5 | template: '', 6 | }) 7 | export class EmptyRouteComponent { 8 | } 9 | -------------------------------------------------------------------------------- /app1/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jolyndenning/coexisting-angular-microfrontends/89bfde330ce4583bd09ff3e033cb119c4664ceb3/app1/src/assets/.gitkeep -------------------------------------------------------------------------------- /app1/src/assets/yoshi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jolyndenning/coexisting-angular-microfrontends/89bfde330ce4583bd09ff3e033cb119c4664ceb3/app1/src/assets/yoshi.png -------------------------------------------------------------------------------- /app1/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /app1/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /app1/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jolyndenning/coexisting-angular-microfrontends/89bfde330ce4583bd09ff3e033cb119c4664ceb3/app1/src/favicon.ico -------------------------------------------------------------------------------- /app1/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | App1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app1/src/main.single-spa.ts: -------------------------------------------------------------------------------- 1 | 2 | import { enableProdMode, NgZone } from '@angular/core'; 3 | 4 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 5 | import { Router } from '@angular/router'; 6 | import { AppModule } from './app/app.module'; 7 | import { environment } from './environments/environment'; 8 | import { singleSpaAngular, getSingleSpaExtraProviders } from 'single-spa-angular'; 9 | import { singleSpaPropsSubject } from './single-spa/single-spa-props'; 10 | 11 | if (environment.production) { 12 | enableProdMode(); 13 | } 14 | 15 | const lifecycles = singleSpaAngular({ 16 | bootstrapFunction: singleSpaProps => { 17 | singleSpaPropsSubject.next(singleSpaProps); 18 | return platformBrowserDynamic(getSingleSpaExtraProviders()).bootstrapModule(AppModule); 19 | }, 20 | template: '', 21 | Router, 22 | NgZone: NgZone, 23 | }); 24 | 25 | export const bootstrap = lifecycles.bootstrap; 26 | export const mount = lifecycles.mount; 27 | export const unmount = lifecycles.unmount; -------------------------------------------------------------------------------- /app1/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /app1/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | -------------------------------------------------------------------------------- /app1/src/single-spa/asset-url.ts: -------------------------------------------------------------------------------- 1 | // In single-spa, the assets need to be loaded from a dynamic location, 2 | // instead of hard coded to `/assets`. We use webpack public path for this. 3 | // See https://webpack.js.org/guides/public-path/#root 4 | 5 | export function assetUrl(url: string): string { 6 | // @ts-ignore 7 | const publicPath = __webpack_public_path__; 8 | const publicPathSuffix = publicPath.endsWith('/') ? '' : '/'; 9 | const urlPrefix = url.startsWith('/') ? '' : '/' 10 | 11 | return `${publicPath}${publicPathSuffix}assets${urlPrefix}${url}`; 12 | } -------------------------------------------------------------------------------- /app1/src/single-spa/single-spa-props.ts: -------------------------------------------------------------------------------- 1 | import { ReplaySubject } from 'rxjs'; 2 | import { AppProps } from 'single-spa'; 3 | 4 | export const singleSpaPropsSubject = new ReplaySubject(1) 5 | 6 | // Add any custom single-spa props you have to this type def 7 | // https://single-spa.js.org/docs/building-applications.html#custom-props 8 | export type SingleSpaProps = AppProps & { 9 | } -------------------------------------------------------------------------------- /app1/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /app1/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /app1/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "include": [ 8 | "src/**/*.ts" 9 | ], 10 | "exclude": [ 11 | "src/test.ts", 12 | "src/**/*.spec.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /app1/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es5", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ] 21 | }, 22 | "angularCompilerOptions": { 23 | "fullTemplateTypeCheck": true, 24 | "strictInjectionParameters": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app1/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /app1/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "array-type": false, 5 | "arrow-parens": false, 6 | "deprecation": { 7 | "severity": "warning" 8 | }, 9 | "component-class-suffix": true, 10 | "contextual-lifecycle": true, 11 | "directive-class-suffix": true, 12 | "directive-selector": [ 13 | true, 14 | "attribute", 15 | "app1", 16 | "camelCase" 17 | ], 18 | "component-selector": [ 19 | true, 20 | "element", 21 | "app1", 22 | "kebab-case" 23 | ], 24 | "import-blacklist": [ 25 | true, 26 | "rxjs/Rx" 27 | ], 28 | "interface-name": false, 29 | "max-classes-per-file": false, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-consecutive-blank-lines": false, 47 | "no-console": [ 48 | true, 49 | "debug", 50 | "info", 51 | "time", 52 | "timeEnd", 53 | "trace" 54 | ], 55 | "no-empty": false, 56 | "no-inferrable-types": [ 57 | true, 58 | "ignore-params" 59 | ], 60 | "no-non-null-assertion": true, 61 | "no-redundant-jsdoc": true, 62 | "no-switch-case-fall-through": true, 63 | "no-use-before-declare": true, 64 | "no-var-requires": false, 65 | "object-literal-key-quotes": [ 66 | true, 67 | "as-needed" 68 | ], 69 | "object-literal-sort-keys": false, 70 | "ordered-imports": false, 71 | "quotemark": [ 72 | true, 73 | "single" 74 | ], 75 | "trailing-comma": false, 76 | "no-conflicting-lifecycle": true, 77 | "no-host-metadata-property": true, 78 | "no-input-rename": true, 79 | "no-inputs-metadata-property": true, 80 | "no-output-native": true, 81 | "no-output-on-prefix": true, 82 | "no-output-rename": true, 83 | "no-outputs-metadata-property": true, 84 | "template-banana-in-box": true, 85 | "template-no-negated-async": true, 86 | "use-lifecycle-interface": true, 87 | "use-pipe-transform-interface": true 88 | }, 89 | "rulesDirectory": [ 90 | "codelyzer" 91 | ] 92 | } -------------------------------------------------------------------------------- /app2/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://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 | -------------------------------------------------------------------------------- /app2/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events.json 15 | speed-measure-plugin.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | -------------------------------------------------------------------------------- /app2/README.md: -------------------------------------------------------------------------------- 1 | # App2 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.1.0. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /app2/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "app2": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "", 10 | "sourceRoot": "src", 11 | "prefix": "app2", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-builders/custom-webpack:browser", 15 | "options": { 16 | "outputPath": "dist/app2", 17 | "index": "src/index.html", 18 | "main": "src/main.single-spa.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "tsconfig.app.json", 21 | "aot": false, 22 | "assets": [ 23 | "src/favicon.ico", 24 | "src/assets" 25 | ], 26 | "styles": [ 27 | "src/styles.css" 28 | ], 29 | "scripts": [], 30 | "customWebpackConfig": { 31 | "path": "./extra-webpack.config.js" 32 | } 33 | }, 34 | "configurations": { 35 | "production": { 36 | "fileReplacements": [ 37 | { 38 | "replace": "src/environments/environment.ts", 39 | "with": "src/environments/environment.prod.ts" 40 | } 41 | ], 42 | "optimization": true, 43 | "outputHashing": "all", 44 | "sourceMap": false, 45 | "extractCss": true, 46 | "namedChunks": false, 47 | "aot": true, 48 | "extractLicenses": true, 49 | "vendorChunk": false, 50 | "buildOptimizer": true, 51 | "budgets": [ 52 | { 53 | "type": "initial", 54 | "maximumWarning": "2mb", 55 | "maximumError": "5mb" 56 | } 57 | ] 58 | } 59 | } 60 | }, 61 | "serve": { 62 | "builder": "@angular-builders/custom-webpack:dev-server", 63 | "options": { 64 | "browserTarget": "app2:build" 65 | }, 66 | "configurations": { 67 | "production": { 68 | "browserTarget": "app2:build:production" 69 | } 70 | } 71 | }, 72 | "extract-i18n": { 73 | "builder": "@angular-devkit/build-angular:extract-i18n", 74 | "options": { 75 | "browserTarget": "app2:build" 76 | } 77 | }, 78 | "test": { 79 | "builder": "@angular-devkit/build-angular:karma", 80 | "options": { 81 | "main": "src/test.ts", 82 | "polyfills": "src/polyfills.ts", 83 | "tsConfig": "tsconfig.spec.json", 84 | "karmaConfig": "karma.conf.js", 85 | "assets": [ 86 | "src/favicon.ico", 87 | "src/assets" 88 | ], 89 | "styles": [ 90 | "src/styles.css" 91 | ], 92 | "scripts": [] 93 | } 94 | }, 95 | "lint": { 96 | "builder": "@angular-devkit/build-angular:tslint", 97 | "options": { 98 | "tsConfig": [ 99 | "tsconfig.app.json", 100 | "tsconfig.spec.json", 101 | "e2e/tsconfig.json" 102 | ], 103 | "exclude": [ 104 | "**/node_modules/**" 105 | ] 106 | } 107 | }, 108 | "e2e": { 109 | "builder": "@angular-devkit/build-angular:protractor", 110 | "options": { 111 | "protractorConfig": "e2e/protractor.conf.js", 112 | "devServerTarget": "app2:serve" 113 | }, 114 | "configurations": { 115 | "production": { 116 | "devServerTarget": "app2:serve:production" 117 | } 118 | } 119 | } 120 | } 121 | } 122 | }, 123 | "defaultProject": "app2", 124 | "cli": { 125 | "analytics": false 126 | } 127 | } -------------------------------------------------------------------------------- /app2/browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /app2/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | 'browserName': 'chrome' 17 | }, 18 | directConnect: true, 19 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() {} 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 31 | } 32 | }; -------------------------------------------------------------------------------- /app2/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('Welcome to app2!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /app2/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app2-root h1')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app2/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app2/extra-webpack.config.js: -------------------------------------------------------------------------------- 1 | const singleSpaAngularWebpack = require('single-spa-angular/lib/webpack').default 2 | 3 | module.exports = (angularWebpackConfig, options) => { 4 | const singleSpaWebpackConfig = singleSpaAngularWebpack(angularWebpackConfig, options) 5 | 6 | // Feel free to modify this webpack config however you'd like to 7 | return singleSpaWebpackConfig 8 | } -------------------------------------------------------------------------------- /app2/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/app2'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /app2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app2", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "npm run serve:single-spa", 7 | "build": "npm run build:single-spa", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e", 11 | "build:single-spa": "ng build --prod --deploy-url /dist/app2 --output-hashing none", 12 | "serve:single-spa": "ng serve --disable-host-check --port 4202 --deploy-url http://localhost:4202/ --live-reload false" 13 | }, 14 | "private": true, 15 | "dependencies": { 16 | "@angular-builders/custom-webpack": "^8", 17 | "@angular/animations": "~9.1.7", 18 | "@angular/common": "~9.1.7", 19 | "@angular/compiler": "~9.1.7", 20 | "@angular/core": "~9.1.7", 21 | "@angular/forms": "~9.1.7", 22 | "@angular/platform-browser": "~9.1.7", 23 | "@angular/platform-browser-dynamic": "~9.1.7", 24 | "@angular/router": "~9.1.7", 25 | "rxjs": "~6.5.5", 26 | "single-spa": "^5.5.0", 27 | "single-spa-angular": "^4.0.1", 28 | "tslib": "^1.10.0", 29 | "zone.js": "~0.10.2" 30 | }, 31 | "devDependencies": { 32 | "@angular-devkit/build-angular": "~0.901.6", 33 | "@angular/cli": "~9.1.6", 34 | "@angular/compiler-cli": "~9.1.7", 35 | "@angular/language-service": "~9.1.7", 36 | "@types/node": "^12.11.1", 37 | "@types/jasmine": "~3.3.8", 38 | "@types/jasminewd2": "~2.0.3", 39 | "codelyzer": "^5.1.2", 40 | "jasmine-core": "~3.4.0", 41 | "jasmine-spec-reporter": "~4.2.1", 42 | "karma": "~4.1.0", 43 | "karma-chrome-launcher": "~2.2.0", 44 | "karma-coverage-istanbul-reporter": "~2.0.1", 45 | "karma-jasmine": "~2.0.1", 46 | "karma-jasmine-html-reporter": "^1.4.0", 47 | "protractor": "~5.4.0", 48 | "ts-node": "~7.0.0", 49 | "tslint": "~5.15.0", 50 | "typescript": "~3.8.3" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app2/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { EmptyRouteComponent } from './empty-route/empty-route.component'; 4 | import { APP_BASE_HREF } from '@angular/common'; 5 | 6 | 7 | const routes: Routes = [ 8 | { path: '**', component: EmptyRouteComponent }, 9 | ]; 10 | 11 | @NgModule({ 12 | imports: [RouterModule.forRoot(routes)], 13 | exports: [RouterModule], 14 | providers: [ 15 | { provide: APP_BASE_HREF, useValue: '/' }, 16 | ], 17 | }) 18 | export class AppRoutingModule { } 19 | -------------------------------------------------------------------------------- /app2/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jolyndenning/coexisting-angular-microfrontends/89bfde330ce4583bd09ff3e033cb119c4664ceb3/app2/src/app/app.component.css -------------------------------------------------------------------------------- /app2/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

4 | Welcome to {{ title }}! 5 |

6 | Angular Logo 7 |
8 |

Here are some links to help you start:

9 | 20 | 21 | To App 1 from app2 22 | 23 | 24 | -------------------------------------------------------------------------------- /app2/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | imports: [ 9 | RouterTestingModule 10 | ], 11 | declarations: [ 12 | AppComponent 13 | ], 14 | }).compileComponents(); 15 | })); 16 | 17 | it('should create the app', () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.debugElement.componentInstance; 20 | expect(app).toBeTruthy(); 21 | }); 22 | 23 | it(`should have as title 'app2'`, () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | const app = fixture.debugElement.componentInstance; 26 | expect(app.title).toEqual('app2'); 27 | }); 28 | 29 | it('should render title in a h1 tag', () => { 30 | const fixture = TestBed.createComponent(AppComponent); 31 | fixture.detectChanges(); 32 | const compiled = fixture.debugElement.nativeElement; 33 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to app2!'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /app2/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app2-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | title = 'app2'; 10 | } 11 | -------------------------------------------------------------------------------- /app2/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AppRoutingModule } from './app-routing.module'; 5 | import { AppComponent } from './app.component'; 6 | import { EmptyRouteComponent } from './empty-route/empty-route.component'; 7 | 8 | @NgModule({ 9 | declarations: [ 10 | AppComponent, 11 | EmptyRouteComponent, 12 | ], 13 | imports: [ 14 | BrowserModule, 15 | AppRoutingModule 16 | ], 17 | providers: [], 18 | bootstrap: [AppComponent] 19 | }) 20 | export class AppModule { } 21 | -------------------------------------------------------------------------------- /app2/src/app/empty-route/empty-route.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app2-empty-route', 5 | template: '', 6 | }) 7 | export class EmptyRouteComponent { 8 | } 9 | -------------------------------------------------------------------------------- /app2/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jolyndenning/coexisting-angular-microfrontends/89bfde330ce4583bd09ff3e033cb119c4664ceb3/app2/src/assets/.gitkeep -------------------------------------------------------------------------------- /app2/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /app2/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /app2/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jolyndenning/coexisting-angular-microfrontends/89bfde330ce4583bd09ff3e033cb119c4664ceb3/app2/src/favicon.ico -------------------------------------------------------------------------------- /app2/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | App2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app2/src/main.single-spa.ts: -------------------------------------------------------------------------------- 1 | 2 | import { enableProdMode, NgZone } from '@angular/core'; 3 | 4 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 5 | import { Router } from '@angular/router'; 6 | import { AppModule } from './app/app.module'; 7 | import { environment } from './environments/environment'; 8 | import { singleSpaAngular, getSingleSpaExtraProviders } from 'single-spa-angular'; 9 | import { singleSpaPropsSubject } from './single-spa/single-spa-props'; 10 | 11 | if (environment.production) { 12 | enableProdMode(); 13 | } 14 | 15 | const lifecycles = singleSpaAngular({ 16 | bootstrapFunction: singleSpaProps => { 17 | singleSpaPropsSubject.next(singleSpaProps); 18 | return platformBrowserDynamic(getSingleSpaExtraProviders()).bootstrapModule(AppModule); 19 | }, 20 | template: '', 21 | Router, 22 | NgZone: NgZone, 23 | }); 24 | 25 | export const bootstrap = lifecycles.bootstrap; 26 | export const mount = lifecycles.mount; 27 | export const unmount = lifecycles.unmount; -------------------------------------------------------------------------------- /app2/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /app2/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | -------------------------------------------------------------------------------- /app2/src/single-spa/asset-url.ts: -------------------------------------------------------------------------------- 1 | // In single-spa, the assets need to be loaded from a dynamic location, 2 | // instead of hard coded to `/assets`. We use webpack public path for this. 3 | // See https://webpack.js.org/guides/public-path/#root 4 | 5 | export function assetUrl(url: string): string { 6 | // @ts-ignore 7 | const publicPath = __webpack_public_path__; 8 | const publicPathSuffix = publicPath.endsWith('/') ? '' : '/'; 9 | const urlPrefix = url.startsWith('/') ? '' : '/' 10 | 11 | return `${publicPath}${publicPathSuffix}assets${urlPrefix}${url}`; 12 | } -------------------------------------------------------------------------------- /app2/src/single-spa/single-spa-props.ts: -------------------------------------------------------------------------------- 1 | import { ReplaySubject } from 'rxjs'; 2 | import { AppProps } from 'single-spa'; 3 | 4 | export const singleSpaPropsSubject = new ReplaySubject(1) 5 | 6 | // Add any custom single-spa props you have to this type def 7 | // https://single-spa.js.org/docs/building-applications.html#custom-props 8 | export type SingleSpaProps = AppProps & { 9 | } -------------------------------------------------------------------------------- /app2/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /app2/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /app2/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "include": [ 8 | "src/**/*.ts" 9 | ], 10 | "exclude": [ 11 | "src/test.ts", 12 | "src/**/*.spec.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /app2/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es5", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ] 21 | }, 22 | "angularCompilerOptions": { 23 | "fullTemplateTypeCheck": true, 24 | "strictInjectionParameters": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app2/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /app2/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "array-type": false, 5 | "arrow-parens": false, 6 | "deprecation": { 7 | "severity": "warning" 8 | }, 9 | "component-class-suffix": true, 10 | "contextual-lifecycle": true, 11 | "directive-class-suffix": true, 12 | "directive-selector": [ 13 | true, 14 | "attribute", 15 | "app2", 16 | "camelCase" 17 | ], 18 | "component-selector": [ 19 | true, 20 | "element", 21 | "app2", 22 | "kebab-case" 23 | ], 24 | "import-blacklist": [ 25 | true, 26 | "rxjs/Rx" 27 | ], 28 | "interface-name": false, 29 | "max-classes-per-file": false, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-consecutive-blank-lines": false, 47 | "no-console": [ 48 | true, 49 | "debug", 50 | "info", 51 | "time", 52 | "timeEnd", 53 | "trace" 54 | ], 55 | "no-empty": false, 56 | "no-inferrable-types": [ 57 | true, 58 | "ignore-params" 59 | ], 60 | "no-non-null-assertion": true, 61 | "no-redundant-jsdoc": true, 62 | "no-switch-case-fall-through": true, 63 | "no-use-before-declare": true, 64 | "no-var-requires": false, 65 | "object-literal-key-quotes": [ 66 | true, 67 | "as-needed" 68 | ], 69 | "object-literal-sort-keys": false, 70 | "ordered-imports": false, 71 | "quotemark": [ 72 | true, 73 | "single" 74 | ], 75 | "trailing-comma": false, 76 | "no-conflicting-lifecycle": true, 77 | "no-host-metadata-property": true, 78 | "no-input-rename": true, 79 | "no-inputs-metadata-property": true, 80 | "no-output-native": true, 81 | "no-output-on-prefix": true, 82 | "no-output-rename": true, 83 | "no-outputs-metadata-property": true, 84 | "template-banana-in-box": true, 85 | "template-no-negated-async": true, 86 | "use-lifecycle-interface": true, 87 | "use-pipe-transform-interface": true 88 | }, 89 | "rulesDirectory": [ 90 | "codelyzer" 91 | ] 92 | } -------------------------------------------------------------------------------- /navbar/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://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 | -------------------------------------------------------------------------------- /navbar/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events.json 15 | speed-measure-plugin.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | -------------------------------------------------------------------------------- /navbar/README.md: -------------------------------------------------------------------------------- 1 | # Navbar 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.1.0. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /navbar/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "navbar": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "", 10 | "sourceRoot": "src", 11 | "prefix": "navbar", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-builders/custom-webpack:browser", 15 | "options": { 16 | "outputPath": "dist/navbar", 17 | "index": "src/index.html", 18 | "main": "src/main.single-spa.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "tsconfig.app.json", 21 | "aot": false, 22 | "assets": [ 23 | "src/favicon.ico", 24 | "src/assets" 25 | ], 26 | "styles": [ 27 | "src/styles.css" 28 | ], 29 | "scripts": [], 30 | "customWebpackConfig": { 31 | "path": "./extra-webpack.config.js" 32 | } 33 | }, 34 | "configurations": { 35 | "production": { 36 | "fileReplacements": [ 37 | { 38 | "replace": "src/environments/environment.ts", 39 | "with": "src/environments/environment.prod.ts" 40 | } 41 | ], 42 | "optimization": true, 43 | "outputHashing": "all", 44 | "sourceMap": false, 45 | "extractCss": true, 46 | "namedChunks": false, 47 | "aot": true, 48 | "extractLicenses": true, 49 | "vendorChunk": false, 50 | "buildOptimizer": true, 51 | "budgets": [ 52 | { 53 | "type": "initial", 54 | "maximumWarning": "2mb", 55 | "maximumError": "5mb" 56 | } 57 | ] 58 | } 59 | } 60 | }, 61 | "serve": { 62 | "builder": "@angular-builders/custom-webpack:dev-server", 63 | "options": { 64 | "browserTarget": "navbar:build" 65 | }, 66 | "configurations": { 67 | "production": { 68 | "browserTarget": "navbar:build:production" 69 | } 70 | } 71 | }, 72 | "extract-i18n": { 73 | "builder": "@angular-devkit/build-angular:extract-i18n", 74 | "options": { 75 | "browserTarget": "navbar:build" 76 | } 77 | }, 78 | "test": { 79 | "builder": "@angular-devkit/build-angular:karma", 80 | "options": { 81 | "main": "src/test.ts", 82 | "polyfills": "src/polyfills.ts", 83 | "tsConfig": "tsconfig.spec.json", 84 | "karmaConfig": "karma.conf.js", 85 | "assets": [ 86 | "src/favicon.ico", 87 | "src/assets" 88 | ], 89 | "styles": [ 90 | "src/styles.css" 91 | ], 92 | "scripts": [] 93 | } 94 | }, 95 | "lint": { 96 | "builder": "@angular-devkit/build-angular:tslint", 97 | "options": { 98 | "tsConfig": [ 99 | "tsconfig.app.json", 100 | "tsconfig.spec.json", 101 | "e2e/tsconfig.json" 102 | ], 103 | "exclude": [ 104 | "**/node_modules/**" 105 | ] 106 | } 107 | }, 108 | "e2e": { 109 | "builder": "@angular-devkit/build-angular:protractor", 110 | "options": { 111 | "protractorConfig": "e2e/protractor.conf.js", 112 | "devServerTarget": "navbar:serve" 113 | }, 114 | "configurations": { 115 | "production": { 116 | "devServerTarget": "navbar:serve:production" 117 | } 118 | } 119 | } 120 | } 121 | } 122 | }, 123 | "defaultProject": "navbar", 124 | "cli": { 125 | "analytics": false 126 | } 127 | } -------------------------------------------------------------------------------- /navbar/browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /navbar/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | 'browserName': 'chrome' 17 | }, 18 | directConnect: true, 19 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() {} 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 31 | } 32 | }; -------------------------------------------------------------------------------- /navbar/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('Welcome to navbar!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /navbar/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('navbar-root h1')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /navbar/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /navbar/extra-webpack.config.js: -------------------------------------------------------------------------------- 1 | const singleSpaAngularWebpack = require('single-spa-angular/lib/webpack').default 2 | 3 | module.exports = (angularWebpackConfig, options) => { 4 | const singleSpaWebpackConfig = singleSpaAngularWebpack(angularWebpackConfig, options) 5 | 6 | // Feel free to modify this webpack config however you'd like to 7 | return singleSpaWebpackConfig 8 | } -------------------------------------------------------------------------------- /navbar/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/navbar'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /navbar/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "navbar", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "npm run serve:single-spa", 7 | "build": "npm run build:single-spa", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e", 11 | "build:single-spa": "ng build --prod --deploy-url /dist/navbar --output-hashing none", 12 | "serve:single-spa": "ng serve --disable-host-check --port 4300 --deploy-url http://localhost:4300/ --live-reload false" 13 | }, 14 | "private": true, 15 | "dependencies": { 16 | "@angular-builders/custom-webpack": "^8", 17 | "@angular/animations": "~9.1.7", 18 | "@angular/common": "~9.1.7", 19 | "@angular/compiler": "~9.1.7", 20 | "@angular/core": "~9.1.7", 21 | "@angular/forms": "~9.1.7", 22 | "@angular/platform-browser": "~9.1.7", 23 | "@angular/platform-browser-dynamic": "~9.1.7", 24 | "@angular/router": "~9.1.7", 25 | "rxjs": "~6.5.5", 26 | "single-spa": "^5.5.0", 27 | "single-spa-angular": "^4.0.0", 28 | "tslib": "^1.10.0", 29 | "zone.js": "~0.10.2" 30 | }, 31 | "devDependencies": { 32 | "@angular-devkit/build-angular": "~0.901.6", 33 | "@angular/cli": "~9.1.6", 34 | "@angular/compiler-cli": "~9.1.7", 35 | "@angular/language-service": "~9.1.7", 36 | "@types/node": "^12.11.1", 37 | "@types/jasmine": "~3.3.8", 38 | "@types/jasminewd2": "~2.0.3", 39 | "codelyzer": "^5.1.2", 40 | "jasmine-core": "~3.4.0", 41 | "jasmine-spec-reporter": "~4.2.1", 42 | "karma": "~4.1.0", 43 | "karma-chrome-launcher": "~2.2.0", 44 | "karma-coverage-istanbul-reporter": "~2.0.1", 45 | "karma-jasmine": "~2.0.1", 46 | "karma-jasmine-html-reporter": "^1.4.0", 47 | "protractor": "~5.4.0", 48 | "ts-node": "~7.0.0", 49 | "tslint": "~5.15.0", 50 | "typescript": "~3.8.3" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /navbar/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { EmptyRouteComponent } from './empty-route/empty-route.component' 4 | import { APP_BASE_HREF } from '@angular/common'; 5 | 6 | 7 | const routes: Routes = [ 8 | { path: '**', component: EmptyRouteComponent }, 9 | ]; 10 | 11 | @NgModule({ 12 | imports: [RouterModule.forRoot(routes)], 13 | exports: [RouterModule], 14 | providers: [ 15 | { provide: APP_BASE_HREF, useValue: '/' }, 16 | ], 17 | }) 18 | export class AppRoutingModule { } 19 | -------------------------------------------------------------------------------- /navbar/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jolyndenning/coexisting-angular-microfrontends/89bfde330ce4583bd09ff3e033cb119c4664ceb3/navbar/src/app/app.component.css -------------------------------------------------------------------------------- /navbar/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /navbar/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | imports: [ 9 | RouterTestingModule 10 | ], 11 | declarations: [ 12 | AppComponent 13 | ], 14 | }).compileComponents(); 15 | })); 16 | 17 | it('should create the app', () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.debugElement.componentInstance; 20 | expect(app).toBeTruthy(); 21 | }); 22 | 23 | it(`should have as title 'navbar'`, () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | const app = fixture.debugElement.componentInstance; 26 | expect(app.title).toEqual('navbar'); 27 | }); 28 | 29 | it('should render title in a h1 tag', () => { 30 | const fixture = TestBed.createComponent(AppComponent); 31 | fixture.detectChanges(); 32 | const compiled = fixture.debugElement.nativeElement; 33 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to navbar!'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /navbar/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'navbar-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | title = 'navbar'; 10 | } 11 | -------------------------------------------------------------------------------- /navbar/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AppRoutingModule } from './app-routing.module'; 5 | import { AppComponent } from './app.component'; 6 | import { EmptyRouteComponent } from './empty-route/empty-route.component'; 7 | import { PrimaryNavComponent } from './primary-nav/primary-nav.component'; 8 | 9 | @NgModule({ 10 | declarations: [ 11 | AppComponent, 12 | EmptyRouteComponent, 13 | PrimaryNavComponent, 14 | ], 15 | imports: [ 16 | BrowserModule, 17 | AppRoutingModule 18 | ], 19 | providers: [], 20 | bootstrap: [AppComponent] 21 | }) 22 | export class AppModule { } 23 | -------------------------------------------------------------------------------- /navbar/src/app/empty-route/empty-route.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'navbar-empty-route', 5 | template: '', 6 | }) 7 | export class EmptyRouteComponent { 8 | } 9 | -------------------------------------------------------------------------------- /navbar/src/app/primary-nav/primary-nav.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jolyndenning/coexisting-angular-microfrontends/89bfde330ce4583bd09ff3e033cb119c4664ceb3/navbar/src/app/primary-nav/primary-nav.component.css -------------------------------------------------------------------------------- /navbar/src/app/primary-nav/primary-nav.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /navbar/src/app/primary-nav/primary-nav.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { PrimaryNavComponent } from './primary-nav.component'; 4 | 5 | describe('PrimaryNavComponent', () => { 6 | let component: PrimaryNavComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ PrimaryNavComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(PrimaryNavComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /navbar/src/app/primary-nav/primary-nav.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'navbar-primary-nav', 5 | templateUrl: './primary-nav.component.html', 6 | styleUrls: ['./primary-nav.component.css'] 7 | }) 8 | export class PrimaryNavComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /navbar/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jolyndenning/coexisting-angular-microfrontends/89bfde330ce4583bd09ff3e033cb119c4664ceb3/navbar/src/assets/.gitkeep -------------------------------------------------------------------------------- /navbar/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /navbar/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /navbar/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jolyndenning/coexisting-angular-microfrontends/89bfde330ce4583bd09ff3e033cb119c4664ceb3/navbar/src/favicon.ico -------------------------------------------------------------------------------- /navbar/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Navbar 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /navbar/src/main.single-spa.ts: -------------------------------------------------------------------------------- 1 | 2 | import { enableProdMode, NgZone } from '@angular/core'; 3 | 4 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 5 | import { Router } from '@angular/router'; 6 | import { AppModule } from './app/app.module'; 7 | import { environment } from './environments/environment'; 8 | import { singleSpaAngular, getSingleSpaExtraProviders } from 'single-spa-angular'; 9 | import { singleSpaPropsSubject } from './single-spa/single-spa-props'; 10 | 11 | if (environment.production) { 12 | enableProdMode(); 13 | } 14 | 15 | const lifecycles = singleSpaAngular({ 16 | bootstrapFunction: singleSpaProps => { 17 | singleSpaPropsSubject.next(singleSpaProps); 18 | return platformBrowserDynamic(getSingleSpaExtraProviders()).bootstrapModule(AppModule); 19 | }, 20 | template: '', 21 | Router, 22 | NgZone: NgZone, 23 | }); 24 | 25 | export const bootstrap = lifecycles.bootstrap; 26 | export const mount = lifecycles.mount; 27 | export const unmount = lifecycles.unmount; -------------------------------------------------------------------------------- /navbar/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /navbar/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | -------------------------------------------------------------------------------- /navbar/src/single-spa/asset-url.ts: -------------------------------------------------------------------------------- 1 | // In single-spa, the assets need to be loaded from a dynamic location, 2 | // instead of hard coded to `/assets`. We use webpack public path for this. 3 | // See https://webpack.js.org/guides/public-path/#root 4 | 5 | export function assetUrl(url: string): string { 6 | // @ts-ignore 7 | const publicPath = __webpack_public_path__; 8 | const publicPathSuffix = publicPath.endsWith('/') ? '' : '/'; 9 | const urlPrefix = url.startsWith('/') ? '' : '/' 10 | 11 | return `${publicPath}${publicPathSuffix}assets${urlPrefix}${url}`; 12 | } -------------------------------------------------------------------------------- /navbar/src/single-spa/single-spa-props.ts: -------------------------------------------------------------------------------- 1 | import { ReplaySubject } from 'rxjs'; 2 | import { AppProps } from 'single-spa'; 3 | 4 | export const singleSpaPropsSubject = new ReplaySubject(1) 5 | 6 | // Add any custom single-spa props you have to this type def 7 | // https://single-spa.js.org/docs/building-applications.html#custom-props 8 | export type SingleSpaProps = AppProps & { 9 | } -------------------------------------------------------------------------------- /navbar/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /navbar/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /navbar/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "include": [ 8 | "src/**/*.ts" 9 | ], 10 | "exclude": [ 11 | "src/test.ts", 12 | "src/**/*.spec.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /navbar/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es5", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ] 21 | }, 22 | "angularCompilerOptions": { 23 | "fullTemplateTypeCheck": true, 24 | "strictInjectionParameters": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /navbar/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /navbar/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "array-type": false, 5 | "arrow-parens": false, 6 | "deprecation": { 7 | "severity": "warning" 8 | }, 9 | "component-class-suffix": true, 10 | "contextual-lifecycle": true, 11 | "directive-class-suffix": true, 12 | "directive-selector": [ 13 | true, 14 | "attribute", 15 | "navbar", 16 | "camelCase" 17 | ], 18 | "component-selector": [ 19 | true, 20 | "element", 21 | "navbar", 22 | "kebab-case" 23 | ], 24 | "import-blacklist": [ 25 | true, 26 | "rxjs/Rx" 27 | ], 28 | "interface-name": false, 29 | "max-classes-per-file": false, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-consecutive-blank-lines": false, 47 | "no-console": [ 48 | true, 49 | "debug", 50 | "info", 51 | "time", 52 | "timeEnd", 53 | "trace" 54 | ], 55 | "no-empty": false, 56 | "no-inferrable-types": [ 57 | true, 58 | "ignore-params" 59 | ], 60 | "no-non-null-assertion": true, 61 | "no-redundant-jsdoc": true, 62 | "no-switch-case-fall-through": true, 63 | "no-use-before-declare": true, 64 | "no-var-requires": false, 65 | "object-literal-key-quotes": [ 66 | true, 67 | "as-needed" 68 | ], 69 | "object-literal-sort-keys": false, 70 | "ordered-imports": false, 71 | "quotemark": [ 72 | true, 73 | "single" 74 | ], 75 | "trailing-comma": false, 76 | "no-conflicting-lifecycle": true, 77 | "no-host-metadata-property": true, 78 | "no-input-rename": true, 79 | "no-inputs-metadata-property": true, 80 | "no-output-native": true, 81 | "no-output-on-prefix": true, 82 | "no-output-rename": true, 83 | "no-outputs-metadata-property": true, 84 | "template-banana-in-box": true, 85 | "template-no-negated-async": true, 86 | "use-lifecycle-interface": true, 87 | "use-pipe-transform-interface": true 88 | }, 89 | "rulesDirectory": [ 90 | "codelyzer" 91 | ] 92 | } -------------------------------------------------------------------------------- /root-html-file/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Your application 8 | 9 | 10 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 44 | 45 | 46 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /root-html-file/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "root-html-file", 3 | "requires": true, 4 | "lockfileVersion": 1, 5 | "dependencies": { 6 | "@zeit/schemas": { 7 | "version": "2.6.0", 8 | "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.6.0.tgz", 9 | "integrity": "sha512-uUrgZ8AxS+Lio0fZKAipJjAh415JyrOZowliZAzmnJSsf7piVL5w+G0+gFJ0KSu3QRhvui/7zuvpLz03YjXAhg==", 10 | "dev": true 11 | }, 12 | "accepts": { 13 | "version": "1.3.7", 14 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 15 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 16 | "dev": true, 17 | "requires": { 18 | "mime-types": "~2.1.24", 19 | "negotiator": "0.6.2" 20 | } 21 | }, 22 | "ajv": { 23 | "version": "6.5.3", 24 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", 25 | "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", 26 | "dev": true, 27 | "requires": { 28 | "fast-deep-equal": "^2.0.1", 29 | "fast-json-stable-stringify": "^2.0.0", 30 | "json-schema-traverse": "^0.4.1", 31 | "uri-js": "^4.2.2" 32 | } 33 | }, 34 | "ansi-align": { 35 | "version": "2.0.0", 36 | "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", 37 | "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", 38 | "dev": true, 39 | "requires": { 40 | "string-width": "^2.0.0" 41 | } 42 | }, 43 | "ansi-regex": { 44 | "version": "3.0.0", 45 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 46 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", 47 | "dev": true 48 | }, 49 | "ansi-styles": { 50 | "version": "3.2.1", 51 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 52 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 53 | "dev": true, 54 | "requires": { 55 | "color-convert": "^1.9.0" 56 | } 57 | }, 58 | "arch": { 59 | "version": "2.1.1", 60 | "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.1.tgz", 61 | "integrity": "sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg==", 62 | "dev": true 63 | }, 64 | "arg": { 65 | "version": "2.0.0", 66 | "resolved": "https://registry.npmjs.org/arg/-/arg-2.0.0.tgz", 67 | "integrity": "sha512-XxNTUzKnz1ctK3ZIcI2XUPlD96wbHP2nGqkPKpvk/HNRlPveYrXIVSTk9m3LcqOgDPg3B1nMvdV/K8wZd7PG4w==", 68 | "dev": true 69 | }, 70 | "balanced-match": { 71 | "version": "1.0.0", 72 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 73 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 74 | "dev": true 75 | }, 76 | "boxen": { 77 | "version": "1.3.0", 78 | "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", 79 | "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", 80 | "dev": true, 81 | "requires": { 82 | "ansi-align": "^2.0.0", 83 | "camelcase": "^4.0.0", 84 | "chalk": "^2.0.1", 85 | "cli-boxes": "^1.0.0", 86 | "string-width": "^2.0.0", 87 | "term-size": "^1.2.0", 88 | "widest-line": "^2.0.0" 89 | } 90 | }, 91 | "brace-expansion": { 92 | "version": "1.1.11", 93 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 94 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 95 | "dev": true, 96 | "requires": { 97 | "balanced-match": "^1.0.0", 98 | "concat-map": "0.0.1" 99 | } 100 | }, 101 | "bytes": { 102 | "version": "3.0.0", 103 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 104 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", 105 | "dev": true 106 | }, 107 | "camelcase": { 108 | "version": "4.1.0", 109 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", 110 | "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", 111 | "dev": true 112 | }, 113 | "chalk": { 114 | "version": "2.4.1", 115 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", 116 | "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", 117 | "dev": true, 118 | "requires": { 119 | "ansi-styles": "^3.2.1", 120 | "escape-string-regexp": "^1.0.5", 121 | "supports-color": "^5.3.0" 122 | } 123 | }, 124 | "cli-boxes": { 125 | "version": "1.0.0", 126 | "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", 127 | "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", 128 | "dev": true 129 | }, 130 | "clipboardy": { 131 | "version": "1.2.3", 132 | "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-1.2.3.tgz", 133 | "integrity": "sha512-2WNImOvCRe6r63Gk9pShfkwXsVtKCroMAevIbiae021mS850UkWPbevxsBz3tnvjZIEGvlwaqCPsw+4ulzNgJA==", 134 | "dev": true, 135 | "requires": { 136 | "arch": "^2.1.0", 137 | "execa": "^0.8.0" 138 | }, 139 | "dependencies": { 140 | "execa": { 141 | "version": "0.8.0", 142 | "resolved": "https://registry.npmjs.org/execa/-/execa-0.8.0.tgz", 143 | "integrity": "sha1-2NdrvBtVIX7RkP1t1J08d07PyNo=", 144 | "dev": true, 145 | "requires": { 146 | "cross-spawn": "^5.0.1", 147 | "get-stream": "^3.0.0", 148 | "is-stream": "^1.1.0", 149 | "npm-run-path": "^2.0.0", 150 | "p-finally": "^1.0.0", 151 | "signal-exit": "^3.0.0", 152 | "strip-eof": "^1.0.0" 153 | } 154 | } 155 | } 156 | }, 157 | "color-convert": { 158 | "version": "1.9.3", 159 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 160 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 161 | "dev": true, 162 | "requires": { 163 | "color-name": "1.1.3" 164 | } 165 | }, 166 | "color-name": { 167 | "version": "1.1.3", 168 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 169 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 170 | "dev": true 171 | }, 172 | "compressible": { 173 | "version": "2.0.17", 174 | "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz", 175 | "integrity": "sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==", 176 | "dev": true, 177 | "requires": { 178 | "mime-db": ">= 1.40.0 < 2" 179 | } 180 | }, 181 | "compression": { 182 | "version": "1.7.3", 183 | "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz", 184 | "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==", 185 | "dev": true, 186 | "requires": { 187 | "accepts": "~1.3.5", 188 | "bytes": "3.0.0", 189 | "compressible": "~2.0.14", 190 | "debug": "2.6.9", 191 | "on-headers": "~1.0.1", 192 | "safe-buffer": "5.1.2", 193 | "vary": "~1.1.2" 194 | } 195 | }, 196 | "concat-map": { 197 | "version": "0.0.1", 198 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 199 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 200 | "dev": true 201 | }, 202 | "content-disposition": { 203 | "version": "0.5.2", 204 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 205 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", 206 | "dev": true 207 | }, 208 | "cross-spawn": { 209 | "version": "5.1.0", 210 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", 211 | "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", 212 | "dev": true, 213 | "requires": { 214 | "lru-cache": "^4.0.1", 215 | "shebang-command": "^1.2.0", 216 | "which": "^1.2.9" 217 | } 218 | }, 219 | "debug": { 220 | "version": "2.6.9", 221 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 222 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 223 | "dev": true, 224 | "requires": { 225 | "ms": "2.0.0" 226 | } 227 | }, 228 | "deep-extend": { 229 | "version": "0.6.0", 230 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 231 | "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", 232 | "dev": true 233 | }, 234 | "escape-string-regexp": { 235 | "version": "1.0.5", 236 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 237 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 238 | "dev": true 239 | }, 240 | "execa": { 241 | "version": "0.7.0", 242 | "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", 243 | "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", 244 | "dev": true, 245 | "requires": { 246 | "cross-spawn": "^5.0.1", 247 | "get-stream": "^3.0.0", 248 | "is-stream": "^1.1.0", 249 | "npm-run-path": "^2.0.0", 250 | "p-finally": "^1.0.0", 251 | "signal-exit": "^3.0.0", 252 | "strip-eof": "^1.0.0" 253 | } 254 | }, 255 | "fast-deep-equal": { 256 | "version": "2.0.1", 257 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", 258 | "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", 259 | "dev": true 260 | }, 261 | "fast-json-stable-stringify": { 262 | "version": "2.0.0", 263 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 264 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", 265 | "dev": true 266 | }, 267 | "fast-url-parser": { 268 | "version": "1.1.3", 269 | "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", 270 | "integrity": "sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=", 271 | "dev": true, 272 | "requires": { 273 | "punycode": "^1.3.2" 274 | }, 275 | "dependencies": { 276 | "punycode": { 277 | "version": "1.4.1", 278 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 279 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", 280 | "dev": true 281 | } 282 | } 283 | }, 284 | "get-stream": { 285 | "version": "3.0.0", 286 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", 287 | "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", 288 | "dev": true 289 | }, 290 | "has-flag": { 291 | "version": "3.0.0", 292 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 293 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 294 | "dev": true 295 | }, 296 | "ini": { 297 | "version": "1.3.7", 298 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", 299 | "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", 300 | "dev": true 301 | }, 302 | "is-fullwidth-code-point": { 303 | "version": "2.0.0", 304 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 305 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 306 | "dev": true 307 | }, 308 | "is-stream": { 309 | "version": "1.1.0", 310 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 311 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", 312 | "dev": true 313 | }, 314 | "isexe": { 315 | "version": "2.0.0", 316 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 317 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 318 | "dev": true 319 | }, 320 | "json-schema-traverse": { 321 | "version": "0.4.1", 322 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 323 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 324 | "dev": true 325 | }, 326 | "lru-cache": { 327 | "version": "4.1.5", 328 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", 329 | "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", 330 | "dev": true, 331 | "requires": { 332 | "pseudomap": "^1.0.2", 333 | "yallist": "^2.1.2" 334 | } 335 | }, 336 | "mime-db": { 337 | "version": "1.40.0", 338 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", 339 | "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", 340 | "dev": true 341 | }, 342 | "mime-types": { 343 | "version": "2.1.24", 344 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", 345 | "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", 346 | "dev": true, 347 | "requires": { 348 | "mime-db": "1.40.0" 349 | } 350 | }, 351 | "minimatch": { 352 | "version": "3.0.4", 353 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 354 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 355 | "dev": true, 356 | "requires": { 357 | "brace-expansion": "^1.1.7" 358 | } 359 | }, 360 | "minimist": { 361 | "version": "1.2.5", 362 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 363 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 364 | "dev": true 365 | }, 366 | "ms": { 367 | "version": "2.0.0", 368 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 369 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 370 | "dev": true 371 | }, 372 | "negotiator": { 373 | "version": "0.6.2", 374 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 375 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", 376 | "dev": true 377 | }, 378 | "npm-run-path": { 379 | "version": "2.0.2", 380 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", 381 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", 382 | "dev": true, 383 | "requires": { 384 | "path-key": "^2.0.0" 385 | } 386 | }, 387 | "on-headers": { 388 | "version": "1.0.2", 389 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", 390 | "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", 391 | "dev": true 392 | }, 393 | "p-finally": { 394 | "version": "1.0.0", 395 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", 396 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", 397 | "dev": true 398 | }, 399 | "path-is-inside": { 400 | "version": "1.0.2", 401 | "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", 402 | "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", 403 | "dev": true 404 | }, 405 | "path-key": { 406 | "version": "2.0.1", 407 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 408 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", 409 | "dev": true 410 | }, 411 | "path-to-regexp": { 412 | "version": "2.2.1", 413 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", 414 | "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==", 415 | "dev": true 416 | }, 417 | "pseudomap": { 418 | "version": "1.0.2", 419 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 420 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", 421 | "dev": true 422 | }, 423 | "punycode": { 424 | "version": "2.1.1", 425 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 426 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 427 | "dev": true 428 | }, 429 | "range-parser": { 430 | "version": "1.2.0", 431 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 432 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", 433 | "dev": true 434 | }, 435 | "rc": { 436 | "version": "1.2.8", 437 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 438 | "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 439 | "dev": true, 440 | "requires": { 441 | "deep-extend": "^0.6.0", 442 | "ini": "~1.3.0", 443 | "minimist": "^1.2.0", 444 | "strip-json-comments": "~2.0.1" 445 | } 446 | }, 447 | "registry-auth-token": { 448 | "version": "3.3.2", 449 | "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", 450 | "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", 451 | "dev": true, 452 | "requires": { 453 | "rc": "^1.1.6", 454 | "safe-buffer": "^5.0.1" 455 | } 456 | }, 457 | "registry-url": { 458 | "version": "3.1.0", 459 | "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", 460 | "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", 461 | "dev": true, 462 | "requires": { 463 | "rc": "^1.0.1" 464 | } 465 | }, 466 | "safe-buffer": { 467 | "version": "5.1.2", 468 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 469 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 470 | "dev": true 471 | }, 472 | "serve": { 473 | "version": "11.1.0", 474 | "resolved": "https://registry.npmjs.org/serve/-/serve-11.1.0.tgz", 475 | "integrity": "sha512-+4wpDtOSS+4ZLyDWMxThutA3iOTawX2+yDovOI8cjOUOmemyvNlHyFAsezBlSgbZKTYChI3tzA1Mh0z6XZ62qA==", 476 | "dev": true, 477 | "requires": { 478 | "@zeit/schemas": "2.6.0", 479 | "ajv": "6.5.3", 480 | "arg": "2.0.0", 481 | "boxen": "1.3.0", 482 | "chalk": "2.4.1", 483 | "clipboardy": "1.2.3", 484 | "compression": "1.7.3", 485 | "serve-handler": "6.1.0", 486 | "update-check": "1.5.2" 487 | } 488 | }, 489 | "serve-handler": { 490 | "version": "6.1.0", 491 | "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.0.tgz", 492 | "integrity": "sha512-63N075Tn3PsFYcu0NVV7tb367UbiW3gnC+/50ohL4oqOhAG6bmbaWqiRcXQgbzqc0ALBjSAzg7VTfa0Qw4E3hA==", 493 | "dev": true, 494 | "requires": { 495 | "bytes": "3.0.0", 496 | "content-disposition": "0.5.2", 497 | "fast-url-parser": "1.1.3", 498 | "mime-types": "2.1.18", 499 | "minimatch": "3.0.4", 500 | "path-is-inside": "1.0.2", 501 | "path-to-regexp": "2.2.1", 502 | "range-parser": "1.2.0" 503 | }, 504 | "dependencies": { 505 | "mime-db": { 506 | "version": "1.33.0", 507 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", 508 | "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", 509 | "dev": true 510 | }, 511 | "mime-types": { 512 | "version": "2.1.18", 513 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", 514 | "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", 515 | "dev": true, 516 | "requires": { 517 | "mime-db": "~1.33.0" 518 | } 519 | } 520 | } 521 | }, 522 | "shebang-command": { 523 | "version": "1.2.0", 524 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 525 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 526 | "dev": true, 527 | "requires": { 528 | "shebang-regex": "^1.0.0" 529 | } 530 | }, 531 | "shebang-regex": { 532 | "version": "1.0.0", 533 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 534 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", 535 | "dev": true 536 | }, 537 | "signal-exit": { 538 | "version": "3.0.2", 539 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 540 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", 541 | "dev": true 542 | }, 543 | "string-width": { 544 | "version": "2.1.1", 545 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 546 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 547 | "dev": true, 548 | "requires": { 549 | "is-fullwidth-code-point": "^2.0.0", 550 | "strip-ansi": "^4.0.0" 551 | } 552 | }, 553 | "strip-ansi": { 554 | "version": "4.0.0", 555 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 556 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 557 | "dev": true, 558 | "requires": { 559 | "ansi-regex": "^3.0.0" 560 | } 561 | }, 562 | "strip-eof": { 563 | "version": "1.0.0", 564 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", 565 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", 566 | "dev": true 567 | }, 568 | "strip-json-comments": { 569 | "version": "2.0.1", 570 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 571 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", 572 | "dev": true 573 | }, 574 | "supports-color": { 575 | "version": "5.5.0", 576 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 577 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 578 | "dev": true, 579 | "requires": { 580 | "has-flag": "^3.0.0" 581 | } 582 | }, 583 | "term-size": { 584 | "version": "1.2.0", 585 | "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", 586 | "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", 587 | "dev": true, 588 | "requires": { 589 | "execa": "^0.7.0" 590 | } 591 | }, 592 | "update-check": { 593 | "version": "1.5.2", 594 | "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.2.tgz", 595 | "integrity": "sha512-1TrmYLuLj/5ZovwUS7fFd1jMH3NnFDN1y1A8dboedIDt7zs/zJMo6TwwlhYKkSeEwzleeiSBV5/3c9ufAQWDaQ==", 596 | "dev": true, 597 | "requires": { 598 | "registry-auth-token": "3.3.2", 599 | "registry-url": "3.1.0" 600 | } 601 | }, 602 | "uri-js": { 603 | "version": "4.2.2", 604 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 605 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 606 | "dev": true, 607 | "requires": { 608 | "punycode": "^2.1.0" 609 | } 610 | }, 611 | "vary": { 612 | "version": "1.1.2", 613 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 614 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", 615 | "dev": true 616 | }, 617 | "which": { 618 | "version": "1.3.1", 619 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 620 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 621 | "dev": true, 622 | "requires": { 623 | "isexe": "^2.0.0" 624 | } 625 | }, 626 | "widest-line": { 627 | "version": "2.0.1", 628 | "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", 629 | "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", 630 | "dev": true, 631 | "requires": { 632 | "string-width": "^2.1.1" 633 | } 634 | }, 635 | "yallist": { 636 | "version": "2.1.2", 637 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 638 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", 639 | "dev": true 640 | } 641 | } 642 | } 643 | -------------------------------------------------------------------------------- /root-html-file/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "root-html-file", 3 | "description": "The single-spa root config for coexisting-angular-microfrontends", 4 | "main": "index.js", 5 | "scripts": { 6 | "start": "serve -s -l 4200" 7 | }, 8 | "author": "Joel Denning", 9 | "license": "MIT", 10 | "devDependencies": { 11 | "serve": "^11.1.0" 12 | }, 13 | "dependencies": {} 14 | } 15 | --------------------------------------------------------------------------------