import { NgModuleRef , NgZone } from '@angular/core' ; import { Subscription } from 'rxjs' ; import { LifeCycles } from 'single-spa' ; import { getContainerElementAndSetTemplate } from 'single-spa-angular/internals' ; import { SingleSpaPlatformLocation } from './extra-providers' ; import { SingleSpaAngularOptions , BootstrappedSingleSpaAngularOptions } from './types' ; const defaultOptions = { NgZone : null ! , bootstrapFunction : null ! , template : null ! , Router : undefined , domElementGetter : undefined , updateFunction : ( ) => Promise . resolve ( ) , bootstrappedModule : null , } ; export function singleSpaAngular < T > ( userOptions ) { if ( typeof userOptions !== 'object' ) { throw Error ( 'single-spa-angular requires a configuration object' ) ; } const options = { ... defaultOptions , ... userOptions , } ; if ( typeof options . bootstrapFunction !== 'function' ) { throw Error ( 'single-spa-angular must be passed an options.bootstrapFunction' ) ; } if ( typeof options . template !== 'string' ) { throw Error ( 'single-spa-angular must be passed options.template string' ) ; } if ( ! options . NgZone ) { throw Error ( `single-spa-angular must be passed the NgZone option` ) ; } if ( options . Router && ! options . NavigationStart ) { console . warn ( `single-spa-angular must be passed the NavigationStart option` ) ; } return { bootstrap : bootstrap . bind ( null , options as BootstrappedSingleSpaAngularOptions ) , mount : mount . bind ( null , options ) , unmount : unmount . bind ( null , options as BootstrappedSingleSpaAngularOptions ) , update : options . updateFunction , } ; } async function bootstrap ( options , props ) { if ( options . NgZone === 'noop' ) { return ; } options . zoneIdentifier = `single-spa-angular:${ props . name || props . appName }` ; options . NgZone . isInAngularZone = ( ) => { return window . Zone . current . _properties [ options . zoneIdentifier ] === true ; } ; options . routingEventListener = ( ) => { options . bootstrappedNgZone ! . run ( ( ) => { } ) ; } ; } async function mount ( options , props ) { getContainerElementAndSetTemplate ( options , props ) ; const bootstrapPromise = options . bootstrapFunction ( props ) ; if ( ! ( bootstrapPromise instanceof Promise ) ) { throw Error ( `single-spa-angular: the options.bootstrapFunction must return a promise, but instead returned a '${ typeof bootstrapPromise }' that is not a Promise` ) ; } const module = await bootstrapPromise ; if ( ! module || typeof module . destroy !== 'function' ) { throw Error ( `single-spa-angular: the options.bootstrapFunction returned a promise that did not resolve with a valid Angular module. Did you call platformBrowserDynamic().bootstrapModule() correctly?` ) ; } const singleSpaPlatformLocation = module . injector . get ( SingleSpaPlatformLocation , null ) ; const ngZoneEnabled = options . NgZone !== 'noop' ; if ( ngZoneEnabled && options . Router && singleSpaPlatformLocation === null ) { throw new Error ( `
      single-spa-angular: could not retrieve extra providers from the platform injector. Did you call platformBrowserDynamic(getSingleSpaExtraProviders()).bootstrapModule()?
    ` ) ; } const bootstrappedOptions = options as BootstrappedSingleSpaAngularOptions ; if ( ngZoneEnabled ) { const ngZone = module . injector . get ( options . NgZone ) ; const zoneIdentifier = bootstrappedOptions . zoneIdentifier ! ; if ( singleSpaPlatformLocation !== null ) { skipLocationChangeOnNonImperativeRoutingTriggers ( module , options ) ; } bootstrappedOptions . bootstrappedNgZone = ngZone ; bootstrappedOptions . bootstrappedNgZone [ '_inner' ] . _properties [ zoneIdentifier ] = true ; window . addEventListener ( 'single-spa:routing-event' , bootstrappedOptions . routingEventListener ! ) ; } bootstrappedOptions . bootstrappedModule = module ; return module ; } function unmount ( options ) { return Promise . resolve ( ) . then ( ( ) => { if ( options . routingEventListener ) { window . removeEventListener ( 'single-spa:routing-event' , options . routingEventListener ) ; } options . bootstrappedModule ! . destroy ( ) ; options . bootstrappedModule = null ; } ) ; } function skipLocationChangeOnNonImperativeRoutingTriggers ( module , options ) { if ( ! options . NavigationStart ) { return ; } const router = module . injector . get ( options . Router ) ; const subscription = router . events . subscribe ( ( event ) => { if ( event instanceof options . NavigationStart ! ) { const currentNavigation = router . getCurrentNavigation ( ) ; if ( currentNavigation . trigger !== 'imperative' ) { currentNavigation . extras . skipLocationChange = true ; currentNavigation . extras . replaceUrl = false ; } } } ) ; module . onDestroy ( ( ) => { subscription . unsubscribe ( ) ; } ) ; }