Skip to content

Commit

Permalink
fix: custom module bootstrap with router
Browse files Browse the repository at this point in the history
  • Loading branch information
markuczy committed Jul 2, 2024
1 parent 47ee1c9 commit bbd13b9
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -1,37 +1,123 @@
import { createCustomElement } from '@angular/elements'
import { createApplication } from '@angular/platform-browser'
import { EnvironmentProviders, NgZone, PlatformRef, Provider, Type, VERSION, Version, getPlatform } from '@angular/core'
import { createApplication, platformBrowser } from '@angular/platform-browser'
import {
EnvironmentProviders,
Injector,
NgModuleRef,
NgZone,
PlatformRef,
Provider,
Type,
VERSION,
Version,
enableProdMode,
getPlatform,
} from '@angular/core'
import { Router } from '@angular/router'
import { getLocation } from '@onecx/accelerator'

/**
* Implementation inspired by @angular-architects/module-federation-plugin https://github.com/angular-architects/module-federation-plugin/blob/main/libs/mf-tools/src/lib/web-components/bootstrap-utils.ts#L191
*/

export type AppType = 'shell' | 'microfrontend'

export function bootstrapModule<M>(module: Type<M>, appType: AppType, production: boolean): Promise<NgModuleRef<M>> {
return cachePlatform(production)
.bootstrapModule(module, {
ngZone: getNgZone(),
})
.then((ref) => {
if (appType === 'shell') {
setShellZone(ref.injector)
} else if (appType === 'microfrontend') {
connectMicroFrontendRouter(ref.injector)
}
return ref
})
}

export async function bootstrapRemoteComponent(
component: Type<any>,
elementName: string,
production: boolean,
providers: (Provider | EnvironmentProviders)[]
): Promise<void> {
const app = await createApplication({
providers: [
(window as any)['@angular-architects/module-federation-tools'].ngZone
getNgZone()
? {
provide: NgZone,
useValue: (window as any)['@angular-architects/module-federation-tools'].ngZone,
useValue: getNgZone(),
}
: [],
...providers,
],
})

const platform = getPlatform()
let platformCache: Map<Version, PlatformRef> = (window as any)['@angular-architects/module-federation-tools']
.platformCache
if (!platformCache) {
platformCache = new Map<Version, PlatformRef>()
;(window as any)['@angular-architects/module-federation-tools'].platformCache = platformCache
}
const version = VERSION
platform && platformCache.set(version, platform)
cachePlatform(production)

const myRemoteComponentAsWebComponent = createCustomElement(component, {
injector: app.injector,
})

customElements.define(elementName, myRemoteComponentAsWebComponent)
}

function getWindowState(): any {
const state = window as any
state['@onecx/angular-webcomponents'] = state['@onecx/angular-webcomponents'] || ({} as unknown)
return state
}

function setShellZone(injector: Injector) {
const ngZone = injector.get(NgZone, null)
if (!ngZone) {
console.warn('No NgZone to share found')
return
}
setNgZone(ngZone)
}

function setNgZone(ngZone: NgZone): void {
getWindowState().ngZone = ngZone
}

function getNgZone(): NgZone {
return getWindowState().ngZone
}

function cachePlatform(production: boolean): PlatformRef {
let platformCache: Map<Version, PlatformRef> = getWindowState().platformCache
if (!platformCache) {
platformCache = new Map<Version, PlatformRef>()
getWindowState().platformCache = platformCache
}
const version = VERSION
let platform: any = platformCache.get(version)
if (!platform) {
platform = getPlatform() ?? platformBrowser()
platform && platformCache.set(version, platform)
production ?? enableProdMode()
}
return platform
}

function connectMicroFrontendRouter(injector: Injector) {
const router = injector.get(Router)

if (!router) {
console.warn('No router to connect found')
return
}

connectRouter(router)
}

function connectRouter(router: Router): void {
let url = `${location.pathname.substring(getLocation().deploymentPath.length)}${location.search}`

Check failure on line 118 in libs/angular-webcomponents/src/lib/utils/webcomponent-bootstrap.utils.ts

View workflow job for this annotation

GitHub Actions / main

'url' is never reassigned. Use 'const' instead
router.navigateByUrl(url)
window.addEventListener('popstate', () => {
router.navigateByUrl(url)
})
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Route, UrlMatcher, UrlSegment, UrlSegmentGroup } from '@angular/router'
import { getLocation } from '@onecx/accelerator'

export function startsWith(prefix: string): UrlMatcher {
return (url: UrlSegment[], UrlSegmentGroup: UrlSegmentGroup, route: Route) => {
Expand All @@ -22,10 +21,7 @@ export function sliceBaseHref(route: Route, url: UrlSegment[]): UrlSegment[] {
)
}

const baseHrefSegmentAmount = getLocation()
.deploymentPath.concat(mfeBaseHref)
.split('/')
.filter((value) => value).length
const baseHrefSegmentAmount = mfeBaseHref.split('/').filter((value) => value).length
return url.slice(baseHrefSegmentAmount)
}

Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit bbd13b9

Please sign in to comment.