Last active
May 11, 2022 18:59
-
-
Save drewwiens/99d26db1f55dd81c965028e695572496 to your computer and use it in GitHub Desktop.
Silly: Extract a single-spa-angular app's base path from the parent application's routes using ngContext that's added by Ivy to root component's DOM element. This relies on at least one component importing Router somewhere in the app. It's not that performant either, taking a couple hundred ms to find it.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { enableProdMode, NgZone } from '@angular/core'; | |
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; | |
import { Router, NavigationStart } from '@angular/router'; | |
import { AppProps } from 'single-spa'; | |
import { | |
singleSpaAngular, | |
getSingleSpaExtraProviders, | |
} from 'single-spa-angular'; | |
import { AppModule } from './app/app.module'; | |
import { BootstrapErrorModule } from './app/bootstrap-error/bootstrap-error.module'; | |
import { environment } from './environments/environment'; | |
import { Routes } from '@angular/router'; | |
import { findDeep } from 'deepdash-es/standalone'; | |
import { isArray, isString } from 'lodash'; | |
import { AppProps } from 'single-spa'; | |
import './cycle'; // Doug Crockford's cycle.js file that decycles objects | |
if (environment.production) { | |
enableProdMode(); | |
} | |
async function initConfig(_singleSpaProps: AppProps) { | |
// Other code here | |
const appRootElement = document.getElementsByTagName('app-root')[0]; | |
const foo = (JSON as any).decycle(appRootElement ?? {}); | |
const bar = JSON.parse(JSON.stringify(foo)); | |
const pathPart = window.location.pathname.split('/')[1]; | |
const routesOfAllChildApps: Routes = | |
findDeep(bar, (value, key, _parentValue, ctx) => { | |
const routes = ctx.parent?.parent?.value; | |
return ( | |
key === 'path' && | |
isString(value) && | |
value.startsWith(pathPart) && | |
ctx.path?.includes('config') && | |
isArray(routes) && | |
routes.every((r) => isString(r.path)) | |
); | |
})?.context?.parent?.parent?.value ?? []; | |
function sortByLength(array: string[]) { | |
return array.sort((x, y) => y.length - x.length); | |
} | |
const paths = sortByLength(routesOfAllChildApps.map((app: any) => app.path)); | |
const basePath = paths.find((l) => | |
window.location.pathname.startsWith('/' + l), | |
); | |
if (basePath) { | |
// Set app's basePath wherever your app defines its config, for use in all | |
// routerLink's, router.navigate's, RouterModule.forRoot(routes), etc | |
} | |
// Other code here | |
} | |
async function bootstrapFunction(singleSpaProps: AppProps) { | |
// Configure app before bootstrapping: | |
let error: unknown = null; | |
try { | |
// Most of the code for this bootstrapFunction is in this other file | |
// "main.common.ts" because it's inside "app" folder and therefore is | |
// watched by live reload by nx serve in local development: | |
await initConfig(singleSpaProps); | |
} catch (e) { | |
error = e; | |
} | |
// If no error occurred, bootstrap app. If an error occurred, app can't | |
// start safely, so bootstrap a small app that just shows an error message: | |
return platformBrowserDynamic(getSingleSpaExtraProviders()).bootstrapModule( | |
error ? BootstrapErrorModule.forRoot(error) : AppModule, | |
); | |
} | |
const lifecycles = singleSpaAngular({ | |
bootstrapFunction, | |
template: '<app-root />', | |
Router, | |
NavigationStart, | |
NgZone, | |
}); | |
export const bootstrap = lifecycles.bootstrap; | |
export const mount = lifecycles.mount; | |
export const unmount = lifecycles.unmount; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment