UNPKG

piral-ng

Version:

Plugin for integrating Angular components in Piral.

167 lines (144 loc) 5.48 kB
import type { ComponentContext } from 'piral-core'; import type { NgModuleFlags, NgOptions } from './types'; import * as browserDynamic from '@angular/platform-browser-dynamic'; import { createPlatformFactory, enableProdMode, NgModuleRef, NgZone, PlatformRef, ɵALLOW_MULTIPLE_PLATFORMS as ALLOW_MULTIPLE_PLATFORMS, VERSION, } from '@angular/core'; import { APP_BASE_HREF } from '@angular/common'; import { contextName } from './constants'; import { CONTEXT } from './injection'; import { getNgVersion } from './utils'; const legacyCall = 'ɵplatformCoreDynamic'; const platformCoreDynamic = browserDynamic.platformBrowserDynamic || browserDynamic[legacyCall]; const INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS = browserDynamic.ɵINTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS; function getVersionHandler(versions: Record<string, () => void>) { const major = getNgVersion(); const version = `v${major}`; return versions[version]; } // Equivalent to platformBrowserDynamic, but with support for multiple platforms const customPlatformDynamicFactory = createPlatformFactory(platformCoreDynamic, 'piralDynamic', [ ...INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS, { provide: ALLOW_MULTIPLE_PLATFORMS, useValue: true, }, ]); const runningModules: Array<[any, NgModuleInt, PlatformRef]> = []; function startNew(BootstrapModule: any, context: ComponentContext, ngOptions?: NgOptions, ngFlags?: NgModuleFlags) { const path = context.publicPath || '/'; const platform = customPlatformDynamicFactory([ { provide: contextName, useValue: context }, { provide: CONTEXT, useValue: context }, { provide: APP_BASE_HREF, useValue: path }, { provide: 'NgFlags', useValue: ngFlags }, ]); // We need to bind the version-specific NgZone to its ID // this will not be MF-dependent, but Angular dependent // i.e., to allow using Zone.js with multiple versions of Angular const zoneIdentifier = `piral-ng:${VERSION.full}`; // This is a hack, since NgZone doesn't allow you to configure the property that identifies your zone. // See: // - https://github.com/PlaceMe-SAS/single-spa-angular-cli/issues/33 // - https://github.com/angular/angular/blob/a14dc2d7a4821a19f20a9547053a5734798f541e/packages/core/src/zone/ng_zone.ts#L144 // - https://github.com/angular/angular/blob/a14dc2d7a4821a19f20a9547053a5734798f541e/packages/core/src/zone/ng_zone.ts#L257 // @ts-ignore NgZone.isInAngularZone = () => window.Zone.current._properties[zoneIdentifier] === true; // We disable those checks as they are misleading and might cause trouble NgZone.assertInAngularZone = () => {}; NgZone.assertNotInAngularZone = () => {}; return platform .bootstrapModule(BootstrapModule, ngOptions) .catch((err) => console.error(err)) .then((instance: NgModuleInt) => { if (instance) { const zone = instance.injector.get(NgZone); // @ts-ignore const z = zone?._inner ?? zone?.inner; if (z && '_properties' in z) { z._properties[zoneIdentifier] = true; } runningModules.push([BootstrapModule, instance, platform]); } return instance; }); } export type NgModuleInt = NgModuleRef<any> & { _destroyed: boolean }; export function teardown(BootstrapModule: any) { const runningModuleIndex = runningModules.findIndex(([ref]) => ref === BootstrapModule); if (runningModuleIndex !== -1) { const [, , platform] = runningModules[runningModuleIndex]; runningModules.splice(runningModuleIndex, 1); if (!platform.destroyed) { platform.destroy(); } } } export function startup( BootstrapModule: any, context: ComponentContext, ngOptions?: NgOptions, ngFlags?: NgModuleFlags, ): Promise<void | NgModuleInt> { const runningModule = runningModules.find(([ref]) => ref === BootstrapModule); if (runningModule) { const [, instance, platform] = runningModule; if (platform.destroyed) { teardown(BootstrapModule); } else { return Promise.resolve(instance); } } return startNew(BootstrapModule, context, ngOptions, ngFlags); } if (process.env.NODE_ENV === 'development') { // May be used later for something useful. Right now only debugging output. const versionHandlers = { legacy() { console.log('Running in legacy mode (Angular 2-8)'); }, outdated() { console.log('Running in outdated mode (Angular 9-13)'); }, current() { console.log('Running in current mode (Angular 14-19)'); }, next() { console.log('Running in next mode (Angular 20)'); }, unknown() { console.log('Running with an unknown version of Angular'); }, }; const versions = { v2: versionHandlers.legacy, v4: versionHandlers.legacy, v5: versionHandlers.legacy, v6: versionHandlers.legacy, v7: versionHandlers.legacy, v8: versionHandlers.legacy, v9: versionHandlers.outdated, v10: versionHandlers.outdated, v11: versionHandlers.outdated, v12: versionHandlers.outdated, v13: versionHandlers.outdated, v14: versionHandlers.current, v15: versionHandlers.current, v16: versionHandlers.current, v17: versionHandlers.current, v18: versionHandlers.current, v19: versionHandlers.current, v20: versionHandlers.next, }; const handler = getVersionHandler(versions) || versionHandlers.unknown; handler(); } if (process.env.NODE_ENV === 'production') { enableProdMode(); }