piral-ng
Version:
Plugin for integrating Angular components in Piral.
167 lines (144 loc) • 5.48 kB
text/typescript
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();
}