Skip to content

Instantly share code, notes, and snippets.

@drewwiens
Last active May 11, 2022 18:59
Show Gist options
  • Save drewwiens/99d26db1f55dd81c965028e695572496 to your computer and use it in GitHub Desktop.
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.
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