diff --git a/apps/multiple-parcels-same-config/child/project.json b/apps/multiple-parcels-same-config/child/project.json new file mode 100644 index 00000000..3fede48d --- /dev/null +++ b/apps/multiple-parcels-same-config/child/project.json @@ -0,0 +1,77 @@ +{ + "name": "multiple-parcels-same-config-child", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/multiple-parcels-same-config/child/src", + "prefix": "single-spa-angular", + "targets": { + "build": { + "executor": "@angular-builders/custom-webpack:browser", + "options": { + "customWebpackConfig": { + "libraryTarget": "system", + "excludeAngularDependencies": true, + "path": "apps/multiple-parcels-same-config/child/webpack.config.ts" + }, + "outputPath": "dist/apps/multiple-parcels-same-config-child", + "index": "apps/multiple-parcels-same-config/child/src/index.html", + "main": "apps/multiple-parcels-same-config/child/src/main.single-spa.ts", + "tsConfig": "apps/multiple-parcels-same-config/child/tsconfig.app.json", + "aot": true, + "assets": ["apps/multiple-parcels-same-config/child/src/favicon.ico", "apps/multiple-parcels-same-config/child/src/assets"], + "styles": ["apps/multiple-parcels-same-config/child/src/styles.scss"], + "deployUrl": "http://localhost:9000/" + }, + "configurations": { + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + }, + "production": { + "fileReplacements": [ + { + "replace": "apps/multiple-parcels-same-config/child/src/environments/environment.ts", + "with": "apps/multiple-parcels-same-config/child/src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "none", + "sourceMap": false, + "namedChunks": false, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "2mb", + "maximumError": "5mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "6kb", + "maximumError": "10kb" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-builders/custom-webpack:dev-server", + "options": { + "browserTarget": "multiple-parcels-same-config-child:build:development" + }, + "configurations": { + "production": { + "browserTarget": "multiple-parcels-same-config-child:build:production" + } + } + } + }, + "tags": [] +} diff --git a/apps/multiple-parcels-same-config/child/src/app/app-routing.module.ts b/apps/multiple-parcels-same-config/child/src/app/app-routing.module.ts new file mode 100755 index 00000000..ad10774a --- /dev/null +++ b/apps/multiple-parcels-same-config/child/src/app/app-routing.module.ts @@ -0,0 +1,15 @@ +import { APP_BASE_HREF } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { EmptyRouteComponent } from './empty-route/empty-route.component'; + +const routes: Routes = [ + { path: '**', component: EmptyRouteComponent } +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes)], + exports: [RouterModule], + providers: [{ provide: APP_BASE_HREF, useValue: '/' }] +}) +export class AppRoutingModule { } diff --git a/apps/multiple-parcels-same-config/child/src/app/app.component.html b/apps/multiple-parcels-same-config/child/src/app/app.component.html new file mode 100644 index 00000000..971fbd39 --- /dev/null +++ b/apps/multiple-parcels-same-config/child/src/app/app.component.html @@ -0,0 +1,4 @@ +
+

parcel name: {{props.name}}

+

time now is {{time}}

+
\ No newline at end of file diff --git a/apps/multiple-parcels-same-config/child/src/app/app.component.ts b/apps/multiple-parcels-same-config/child/src/app/app.component.ts new file mode 100644 index 00000000..8d79e319 --- /dev/null +++ b/apps/multiple-parcels-same-config/child/src/app/app.component.ts @@ -0,0 +1,26 @@ +import { Component } from '@angular/core'; +import { singleSpaPropsSubject } from '../single-spa/single-spa-props'; +import { first } from 'rxjs'; + +@Component({ + selector: 'multiple-parcels-same-config-child', + templateUrl: './app.component.html' +}) +export class AppComponent { + constructor() {} + + props: any = null; + time: number = Date.now(); + + ngOnInit() { + singleSpaPropsSubject + .pipe(first()) + .subscribe((props) => { + this.props = props; + }); + + setInterval(() => { + this.time = Date.now(); + }, 1000); + } +} diff --git a/apps/multiple-parcels-same-config/child/src/app/app.module.ts b/apps/multiple-parcels-same-config/child/src/app/app.module.ts new file mode 100644 index 00000000..097863ad --- /dev/null +++ b/apps/multiple-parcels-same-config/child/src/app/app.module.ts @@ -0,0 +1,21 @@ +import { ApplicationRef, DoBootstrap, NgModule } from '@angular/core'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { APP_BASE_HREF } from '@angular/common'; + +import { AppComponent } from './app.component'; +import { AppRoutingModule } from './app-routing.module'; +import { EmptyRouteComponent } from './empty-route/empty-route.component'; + +export const AppModule = (name: string) => { + @NgModule({ + declarations: [AppComponent, EmptyRouteComponent], + imports: [BrowserAnimationsModule, AppRoutingModule], + providers: [], + }) + class AppModule implements DoBootstrap { + ngDoBootstrap(appRef: ApplicationRef) { + appRef.bootstrap(AppComponent, `multiple-parcels-same-config-child-${name}`); + } + } + return AppModule; +} diff --git a/apps/multiple-parcels-same-config/child/src/app/empty-route/empty-route.component.ts b/apps/multiple-parcels-same-config/child/src/app/empty-route/empty-route.component.ts new file mode 100755 index 00000000..b8380398 --- /dev/null +++ b/apps/multiple-parcels-same-config/child/src/app/empty-route/empty-route.component.ts @@ -0,0 +1,7 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app1-empty-route', + template: '', +}) +export class EmptyRouteComponent {} diff --git a/apps/multiple-parcels-same-config/child/src/assets/.gitkeep b/apps/multiple-parcels-same-config/child/src/assets/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/apps/multiple-parcels-same-config/child/src/environments/environment.prod.ts b/apps/multiple-parcels-same-config/child/src/environments/environment.prod.ts new file mode 100644 index 00000000..c9669790 --- /dev/null +++ b/apps/multiple-parcels-same-config/child/src/environments/environment.prod.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true, +}; diff --git a/apps/multiple-parcels-same-config/child/src/environments/environment.ts b/apps/multiple-parcels-same-config/child/src/environments/environment.ts new file mode 100644 index 00000000..a20cfe55 --- /dev/null +++ b/apps/multiple-parcels-same-config/child/src/environments/environment.ts @@ -0,0 +1,3 @@ +export const environment = { + production: false, +}; diff --git a/apps/multiple-parcels-same-config/child/src/favicon.ico b/apps/multiple-parcels-same-config/child/src/favicon.ico new file mode 100644 index 00000000..997406ad Binary files /dev/null and b/apps/multiple-parcels-same-config/child/src/favicon.ico differ diff --git a/apps/multiple-parcels-same-config/child/src/index.html b/apps/multiple-parcels-same-config/child/src/index.html new file mode 100644 index 00000000..27968ca3 --- /dev/null +++ b/apps/multiple-parcels-same-config/child/src/index.html @@ -0,0 +1,13 @@ + + + + + AngularMultipleParcelsSameConfigChild + + + + + + + + diff --git a/apps/multiple-parcels-same-config/child/src/main.single-spa.ts b/apps/multiple-parcels-same-config/child/src/main.single-spa.ts new file mode 100644 index 00000000..72756bcd --- /dev/null +++ b/apps/multiple-parcels-same-config/child/src/main.single-spa.ts @@ -0,0 +1,31 @@ +import { NgZone } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { NavigationStart, Router } from '@angular/router'; +import { singleSpaAngular, getSingleSpaExtraProviders, enableProdMode } from 'single-spa-angular'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; +import { singleSpaPropsSubject } from './single-spa/single-spa-props'; + +if (environment.production) { + enableProdMode(); +} + +const lifecycles = singleSpaAngular({ + bootstrapFunction: singleSpaProps => { + singleSpaPropsSubject.next(singleSpaProps); + return platformBrowserDynamic(getSingleSpaExtraProviders()).bootstrapModule( + AppModule(singleSpaProps.name) + ); + }, + template: singleSpaProps => { + return ``; + }, + NgZone, + Router, + NavigationStart, +}); + +export const bootstrap = lifecycles.bootstrap; +export const mount = lifecycles.mount; +export const unmount = lifecycles.unmount; diff --git a/apps/multiple-parcels-same-config/child/src/single-spa/asset-url.ts b/apps/multiple-parcels-same-config/child/src/single-spa/asset-url.ts new file mode 100644 index 00000000..d8cb3be3 --- /dev/null +++ b/apps/multiple-parcels-same-config/child/src/single-spa/asset-url.ts @@ -0,0 +1,12 @@ +// In single-spa, the assets need to be loaded from a dynamic location, +// instead of hard coded to `/assets`. We use webpack public path for this. +// See https://webpack.js.org/guides/public-path/#root + +export function assetUrl(url: string): string { + // @ts-ignore + const publicPath = __webpack_public_path__; + const publicPathSuffix = publicPath.endsWith('/') ? '' : '/'; + const urlPrefix = url.startsWith('/') ? '' : '/'; + + return `${publicPath}${publicPathSuffix}assets${urlPrefix}${url}`; +} diff --git a/apps/multiple-parcels-same-config/child/src/single-spa/single-spa-props.ts b/apps/multiple-parcels-same-config/child/src/single-spa/single-spa-props.ts new file mode 100644 index 00000000..fc133a22 --- /dev/null +++ b/apps/multiple-parcels-same-config/child/src/single-spa/single-spa-props.ts @@ -0,0 +1,8 @@ +import { ReplaySubject } from 'rxjs'; +import { AppProps } from 'single-spa'; + +export const singleSpaPropsSubject = new ReplaySubject(1); + +// Add any custom single-spa props you have to this type def +// https://single-spa.js.org/docs/building-applications.html#custom-props +export type SingleSpaProps = AppProps & Record; diff --git a/apps/multiple-parcels-same-config/child/src/styles.scss b/apps/multiple-parcels-same-config/child/src/styles.scss new file mode 100644 index 00000000..90d4ee00 --- /dev/null +++ b/apps/multiple-parcels-same-config/child/src/styles.scss @@ -0,0 +1 @@ +/* You can add global styles to this file, and also import other style files */ diff --git a/apps/multiple-parcels-same-config/child/tsconfig.app.json b/apps/multiple-parcels-same-config/child/tsconfig.app.json new file mode 100644 index 00000000..79f5c904 --- /dev/null +++ b/apps/multiple-parcels-same-config/child/tsconfig.app.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "files": ["src/main.single-spa.ts"] +} diff --git a/apps/multiple-parcels-same-config/child/tsconfig.json b/apps/multiple-parcels-same-config/child/tsconfig.json new file mode 100644 index 00000000..11a1eafd --- /dev/null +++ b/apps/multiple-parcels-same-config/child/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../../tsconfig.base.json", + "references": [ + { + "path": "./tsconfig.app.json" + } + ], + "compilerOptions": { + "target": "es2020" + } +} diff --git a/apps/multiple-parcels-same-config/child/webpack.config.ts b/apps/multiple-parcels-same-config/child/webpack.config.ts new file mode 100644 index 00000000..4411334e --- /dev/null +++ b/apps/multiple-parcels-same-config/child/webpack.config.ts @@ -0,0 +1 @@ +export { default } from '../../../libs/single-spa-angular/webpack'; diff --git a/apps/multiple-parcels-same-config/parent/project.json b/apps/multiple-parcels-same-config/parent/project.json new file mode 100644 index 00000000..76038e0a --- /dev/null +++ b/apps/multiple-parcels-same-config/parent/project.json @@ -0,0 +1,77 @@ +{ + "name": "multiple-parcels-same-config", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/multiple-parcels-same-config/parent/src", + "prefix": "single-spa-angular", + "targets": { + "build": { + "executor": "@angular-builders/custom-webpack:browser", + "options": { + "customWebpackConfig": { + "libraryTarget": "system", + "excludeAngularDependencies": true, + "path": "apps/multiple-parcels-same-config/parent/webpack.config.ts" + }, + "outputPath": "dist/apps/multiple-parcels-same-config", + "index": "apps/multiple-parcels-same-config/parent/src/index.html", + "main": "apps/multiple-parcels-same-config/parent/src/main.single-spa.ts", + "tsConfig": "apps/multiple-parcels-same-config/parent/tsconfig.app.json", + "aot": true, + "assets": ["apps/multiple-parcels-same-config/parent/src/favicon.ico", "apps/multiple-parcels-same-config/parent/src/assets"], + "styles": ["apps/multiple-parcels-same-config/parent/src/styles.scss"], + "deployUrl": "http://localhost:9000/" + }, + "configurations": { + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + }, + "production": { + "fileReplacements": [ + { + "replace": "apps/multiple-parcels-same-config/parent/src/environments/environment.ts", + "with": "apps/multiple-parcels-same-config/parent/src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "none", + "sourceMap": false, + "namedChunks": false, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "2mb", + "maximumError": "5mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "6kb", + "maximumError": "10kb" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-builders/custom-webpack:dev-server", + "options": { + "browserTarget": "multiple-parcels-same-config:build:development" + }, + "configurations": { + "production": { + "browserTarget": "multiple-parcels-same-config:build:production" + } + } + } + }, + "tags": [] +} diff --git a/apps/multiple-parcels-same-config/parent/src/app/app-routing.module.ts b/apps/multiple-parcels-same-config/parent/src/app/app-routing.module.ts new file mode 100755 index 00000000..ad10774a --- /dev/null +++ b/apps/multiple-parcels-same-config/parent/src/app/app-routing.module.ts @@ -0,0 +1,15 @@ +import { APP_BASE_HREF } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { EmptyRouteComponent } from './empty-route/empty-route.component'; + +const routes: Routes = [ + { path: '**', component: EmptyRouteComponent } +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes)], + exports: [RouterModule], + providers: [{ provide: APP_BASE_HREF, useValue: '/' }] +}) +export class AppRoutingModule { } diff --git a/apps/multiple-parcels-same-config/parent/src/app/app.component.html b/apps/multiple-parcels-same-config/parent/src/app/app.component.html new file mode 100644 index 00000000..090b621b --- /dev/null +++ b/apps/multiple-parcels-same-config/parent/src/app/app.component.html @@ -0,0 +1,14 @@ + + + + + +
+ parcel 1 +
+
+ + parcel 2 +
+
+
diff --git a/apps/multiple-parcels-same-config/parent/src/app/app.component.ts b/apps/multiple-parcels-same-config/parent/src/app/app.component.ts new file mode 100644 index 00000000..dcab473d --- /dev/null +++ b/apps/multiple-parcels-same-config/parent/src/app/app.component.ts @@ -0,0 +1,51 @@ +import { Component, ElementRef, ViewChild } from '@angular/core'; +import { mountRootParcel } from 'single-spa'; + +@Component({ + selector: 'multiple-parcels-same-config', + templateUrl: './app.component.html', +}) +export class AppComponent { + constructor() {} + + @ViewChild('parcelContainer1') parcelContainer1!: ElementRef; + @ViewChild('parcelContainer2') parcelContainer2!: ElementRef; + + mountRootParcel = mountRootParcel; + + props1 = { id: 1 }; + props2 = { id: 2 }; + + parcel1: any; + parcel2: any; + + config() { + return (window as any).System.import('multiple-parcels-same-config-child'); + } + + mount1() { + this.parcel1 = this.mountRootParcel(this.config, { + domElement: this.parcelContainer1.nativeElement, + ...this.props1, + }); + } + + mount2() { + this.parcel2 = this.mountRootParcel(this.config, { + domElement: this.parcelContainer2.nativeElement, + ...this.props2, + }); + } + + unmount1() { + if (this.parcel1) { + this.parcel1.unmount(); + } + } + + unmount2() { + if (this.parcel2) { + this.parcel2.unmount(); + } + } +} diff --git a/apps/multiple-parcels-same-config/parent/src/app/app.module.ts b/apps/multiple-parcels-same-config/parent/src/app/app.module.ts new file mode 100644 index 00000000..a742cd14 --- /dev/null +++ b/apps/multiple-parcels-same-config/parent/src/app/app.module.ts @@ -0,0 +1,15 @@ +import { NgModule } from '@angular/core'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { APP_BASE_HREF } from '@angular/common'; + +import { AppComponent } from './app.component'; +import { AppRoutingModule } from './app-routing.module'; +import { EmptyRouteComponent } from './empty-route/empty-route.component'; + +@NgModule({ + imports: [BrowserAnimationsModule, AppRoutingModule], + declarations: [AppComponent, EmptyRouteComponent], + bootstrap: [AppComponent], + providers: [], +}) +export class AppModule {} diff --git a/apps/multiple-parcels-same-config/parent/src/app/empty-route/empty-route.component.ts b/apps/multiple-parcels-same-config/parent/src/app/empty-route/empty-route.component.ts new file mode 100755 index 00000000..b8380398 --- /dev/null +++ b/apps/multiple-parcels-same-config/parent/src/app/empty-route/empty-route.component.ts @@ -0,0 +1,7 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app1-empty-route', + template: '', +}) +export class EmptyRouteComponent {} diff --git a/apps/multiple-parcels-same-config/parent/src/assets/.gitkeep b/apps/multiple-parcels-same-config/parent/src/assets/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/apps/multiple-parcels-same-config/parent/src/environments/environment.prod.ts b/apps/multiple-parcels-same-config/parent/src/environments/environment.prod.ts new file mode 100644 index 00000000..c9669790 --- /dev/null +++ b/apps/multiple-parcels-same-config/parent/src/environments/environment.prod.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true, +}; diff --git a/apps/multiple-parcels-same-config/parent/src/environments/environment.ts b/apps/multiple-parcels-same-config/parent/src/environments/environment.ts new file mode 100644 index 00000000..a20cfe55 --- /dev/null +++ b/apps/multiple-parcels-same-config/parent/src/environments/environment.ts @@ -0,0 +1,3 @@ +export const environment = { + production: false, +}; diff --git a/apps/multiple-parcels-same-config/parent/src/favicon.ico b/apps/multiple-parcels-same-config/parent/src/favicon.ico new file mode 100644 index 00000000..997406ad Binary files /dev/null and b/apps/multiple-parcels-same-config/parent/src/favicon.ico differ diff --git a/apps/multiple-parcels-same-config/parent/src/index.html b/apps/multiple-parcels-same-config/parent/src/index.html new file mode 100644 index 00000000..adb5f057 --- /dev/null +++ b/apps/multiple-parcels-same-config/parent/src/index.html @@ -0,0 +1,13 @@ + + + + + AngularMultipleParcelsSameConfigParent + + + + + + + + diff --git a/apps/multiple-parcels-same-config/parent/src/main.single-spa.ts b/apps/multiple-parcels-same-config/parent/src/main.single-spa.ts new file mode 100644 index 00000000..39507b4f --- /dev/null +++ b/apps/multiple-parcels-same-config/parent/src/main.single-spa.ts @@ -0,0 +1,29 @@ +import { NgZone } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { NavigationStart, Router } from '@angular/router'; +import { singleSpaAngular, getSingleSpaExtraProviders, enableProdMode } from 'single-spa-angular'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; +import { singleSpaPropsSubject } from './single-spa/single-spa-props'; + +if (environment.production) { + enableProdMode(); +} + +const lifecycles = singleSpaAngular({ + bootstrapFunction: singleSpaProps => { + singleSpaPropsSubject.next(singleSpaProps); + return platformBrowserDynamic(getSingleSpaExtraProviders()).bootstrapModule( + AppModule, + ); + }, + template: '', + NgZone, + Router, + NavigationStart, +}); + +export const bootstrap = lifecycles.bootstrap; +export const mount = lifecycles.mount; +export const unmount = lifecycles.unmount; diff --git a/apps/multiple-parcels-same-config/parent/src/single-spa/asset-url.ts b/apps/multiple-parcels-same-config/parent/src/single-spa/asset-url.ts new file mode 100644 index 00000000..d8cb3be3 --- /dev/null +++ b/apps/multiple-parcels-same-config/parent/src/single-spa/asset-url.ts @@ -0,0 +1,12 @@ +// In single-spa, the assets need to be loaded from a dynamic location, +// instead of hard coded to `/assets`. We use webpack public path for this. +// See https://webpack.js.org/guides/public-path/#root + +export function assetUrl(url: string): string { + // @ts-ignore + const publicPath = __webpack_public_path__; + const publicPathSuffix = publicPath.endsWith('/') ? '' : '/'; + const urlPrefix = url.startsWith('/') ? '' : '/'; + + return `${publicPath}${publicPathSuffix}assets${urlPrefix}${url}`; +} diff --git a/apps/multiple-parcels-same-config/parent/src/single-spa/single-spa-props.ts b/apps/multiple-parcels-same-config/parent/src/single-spa/single-spa-props.ts new file mode 100644 index 00000000..fc133a22 --- /dev/null +++ b/apps/multiple-parcels-same-config/parent/src/single-spa/single-spa-props.ts @@ -0,0 +1,8 @@ +import { ReplaySubject } from 'rxjs'; +import { AppProps } from 'single-spa'; + +export const singleSpaPropsSubject = new ReplaySubject(1); + +// Add any custom single-spa props you have to this type def +// https://single-spa.js.org/docs/building-applications.html#custom-props +export type SingleSpaProps = AppProps & Record; diff --git a/apps/multiple-parcels-same-config/parent/src/styles.scss b/apps/multiple-parcels-same-config/parent/src/styles.scss new file mode 100644 index 00000000..90d4ee00 --- /dev/null +++ b/apps/multiple-parcels-same-config/parent/src/styles.scss @@ -0,0 +1 @@ +/* You can add global styles to this file, and also import other style files */ diff --git a/apps/multiple-parcels-same-config/parent/tsconfig.app.json b/apps/multiple-parcels-same-config/parent/tsconfig.app.json new file mode 100644 index 00000000..79f5c904 --- /dev/null +++ b/apps/multiple-parcels-same-config/parent/tsconfig.app.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "files": ["src/main.single-spa.ts"] +} diff --git a/apps/multiple-parcels-same-config/parent/tsconfig.json b/apps/multiple-parcels-same-config/parent/tsconfig.json new file mode 100644 index 00000000..11a1eafd --- /dev/null +++ b/apps/multiple-parcels-same-config/parent/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../../tsconfig.base.json", + "references": [ + { + "path": "./tsconfig.app.json" + } + ], + "compilerOptions": { + "target": "es2020" + } +} diff --git a/apps/multiple-parcels-same-config/parent/webpack.config.ts b/apps/multiple-parcels-same-config/parent/webpack.config.ts new file mode 100644 index 00000000..4411334e --- /dev/null +++ b/apps/multiple-parcels-same-config/parent/webpack.config.ts @@ -0,0 +1 @@ +export { default } from '../../../libs/single-spa-angular/webpack'; diff --git a/apps/navbar/src/app/components/primary-nav/primary-nav.component.ts b/apps/navbar/src/app/components/primary-nav/primary-nav.component.ts index d3cccdf8..14a31bf4 100644 --- a/apps/navbar/src/app/components/primary-nav/primary-nav.component.ts +++ b/apps/navbar/src/app/components/primary-nav/primary-nav.component.ts @@ -32,6 +32,10 @@ export class PrimaryNavComponent { label: 'Angular standalone', url: '/standalone', }, + { + label: "Multiple parcels from same config", + url: "/multiple-parcels-same-config" + } ]; constructor(private router: Router) {} diff --git a/apps/root-config/src/index.ejs b/apps/root-config/src/index.ejs index c1b4e4b9..2155dcc8 100644 --- a/apps/root-config/src/index.ejs +++ b/apps/root-config/src/index.ejs @@ -34,6 +34,8 @@ "elements": "http://localhost:4000/main.js", "parcel": "http://localhost:4400/main.js", "standalone": "http://localhost:4500/main.js", + "multiple-parcels-same-config": "http://localhost:4600/main.js", + "multiple-parcels-same-config-child": "http://localhost:4700/main.js", "rxjs": "https://cdn.jsdelivr.net/npm/@esm-bundle/rxjs/system/es2015/rxjs.min.js", "rxjs/operators": "https://cdn.jsdelivr.net/npm/@esm-bundle/rxjs/system/es2015/rxjs-operators.min.js" } diff --git a/apps/root-config/src/main.js b/apps/root-config/src/main.js index 15ac2d0f..299fae7c 100644 --- a/apps/root-config/src/main.js +++ b/apps/root-config/src/main.js @@ -41,5 +41,11 @@ System.import('single-spa').then(({ registerApplication, start }) => { activeWhen: location => location.pathname.startsWith('/standalone'), }); + registerApplication({ + name: 'multiple-parcels-same-config', + app: () => System.import('multiple-parcels-same-config'), + activeWhen: location => location.pathname.startsWith('/multiple-parcels-same-config'), + }); + start(); }); diff --git a/cypress/integration/multiple-parcels-same-config.spec.js b/cypress/integration/multiple-parcels-same-config.spec.js new file mode 100644 index 00000000..f9d0f50d --- /dev/null +++ b/cypress/integration/multiple-parcels-same-config.spec.js @@ -0,0 +1,50 @@ +/// + +Cypress.Screenshot.defaults({ + screenshotOnRunFailure: false, +}); + +describe('https://github.com/single-spa/single-spa-angular/issues/234', () => { + it('should render the same Angular parcel twice', () => { + cy.visit('/multiple-parcels-same-config') + // Mount the first parcel. + .get('button.mount1') + .click() + // The `wait` command is used because Cypress on the CI level is slower than locally, + // basically it clicks the `