@@ -15,10 +15,12 @@ import {ApplicationInitStatus} from './application_init';
1515import { APP_BOOTSTRAP_LISTENER , PLATFORM_INITIALIZER } from './application_tokens' ;
1616import { getCompilerFacade , JitCompilerUsage } from './compiler/compiler_facade' ;
1717import { Console } from './console' ;
18+ import { importProvidersFrom } from './di' ;
1819import { Injectable } from './di/injectable' ;
1920import { InjectionToken } from './di/injection_token' ;
2021import { Injector } from './di/injector' ;
21- import { StaticProvider } from './di/interface/provider' ;
22+ import { Provider , StaticProvider } from './di/interface/provider' ;
23+ import { EnvironmentInjector } from './di/r3_injector' ;
2224import { INJECTOR_SCOPE } from './di/scope' ;
2325import { ErrorHandler } from './error_handler' ;
2426import { formatRuntimeError , RuntimeError , RuntimeErrorCode } from './errors' ;
@@ -33,9 +35,13 @@ import {InternalViewRef, ViewRef} from './linker/view_ref';
3335import { isComponentResourceResolutionQueueEmpty , resolveComponentResources } from './metadata/resource_loading' ;
3436import { assertNgModuleType } from './render3/assert' ;
3537import { ComponentFactory as R3ComponentFactory } from './render3/component_ref' ;
38+ import { getComponentDef } from './render3/definition' ;
39+ import { assertStandaloneComponentType } from './render3/errors' ;
40+ import { StandaloneService } from './render3/features/standalone_feature' ;
3641import { setLocaleId } from './render3/i18n/i18n_locale_id' ;
3742import { setJitOptions } from './render3/jit/jit_options' ;
38- import { NgModuleFactory as R3NgModuleFactory } from './render3/ng_module_ref' ;
43+ import { isStandalone } from './render3/jit/module' ;
44+ import { createEnvironmentInjector , NgModuleFactory as R3NgModuleFactory } from './render3/ng_module_ref' ;
3945import { publishDefaultGlobalUtils as _publishDefaultGlobalUtils } from './render3/util/global_utils' ;
4046import { Testability , TestabilityRegistry } from './testability/testability' ;
4147import { isPromise } from './util/lang' ;
@@ -143,11 +149,107 @@ export function createPlatform(injector: Injector): PlatformRef {
143149 publishDefaultGlobalUtils ( ) ;
144150 _platformInjector = injector ;
145151 const platform = injector . get ( PlatformRef ) ;
146- const inits = injector . get ( PLATFORM_INITIALIZER , null ) ;
147- if ( inits ) inits . forEach ( initFn => initFn ( ) ) ;
152+ runPlatformInitializers ( injector ) ;
148153 return platform ;
149154}
150155
156+ /**
157+ * The goal of this function is to bootstrap a platform injector,
158+ * but avoid referencing `PlatformRef` class.
159+ * This function is needed for bootstrapping a Standalone Component.
160+ */
161+ export function createOrReusePlatformInjector ( providers : StaticProvider [ ] = [ ] ) : Injector {
162+ // If a platform injector already exists, it means that the platform
163+ // is already bootstrapped and no additional actions are required.
164+ if ( _platformInjector ) return _platformInjector ;
165+
166+ // Otherwise, setup a new platform injector and run platform initializers.
167+ const injector = createPlatformInjector ( providers ) ;
168+ _platformInjector = injector ;
169+ publishDefaultGlobalUtils ( ) ;
170+ runPlatformInitializers ( injector ) ;
171+ return injector ;
172+ }
173+
174+ export function runPlatformInitializers ( injector : Injector ) : void {
175+ const inits = injector . get ( PLATFORM_INITIALIZER , null ) ;
176+ if ( inits ) {
177+ inits . forEach ( ( init : any ) => init ( ) ) ;
178+ }
179+ }
180+
181+ /**
182+ * Internal bootstrap application API that implements the core bootstrap logic.
183+ *
184+ * Platforms (such as `platform-browser`) may require different set of application and platform
185+ * providers for an application to function correctly. As a result, platforms may use this function
186+ * internally and supply the necessary providers during the bootstrap, while exposing
187+ * platform-specific APIs as a part of their public API.
188+ *
189+ * @returns A promise that returns an `ApplicationRef` instance once resolved.
190+ */
191+ export function bootstrapApplication ( config : {
192+ rootComponent : Type < unknown > ,
193+ appProviders ?: Provider [ ] ,
194+ platformProviders ?: Provider [ ] ,
195+ } ) : Promise < ApplicationRef > {
196+ const { rootComponent, appProviders, platformProviders} = config ;
197+ NG_DEV_MODE && assertStandaloneComponentType ( rootComponent ) ;
198+
199+ const platformInjector = createOrReusePlatformInjector ( platformProviders as StaticProvider [ ] ) ;
200+
201+ const ngZone = new NgZone ( getNgZoneOptions ( ) ) ;
202+
203+ return ngZone . run ( ( ) => {
204+ // Create root application injector based on a set of providers configured at the platform
205+ // bootstrap level as well as providers passed to the bootstrap call by a user.
206+ const allAppProviders = [
207+ { provide : NgZone , useValue : ngZone } , //
208+ ...( appProviders || [ ] ) , //
209+ // Collect providers from the root standalone component
210+ // and all of its dependencies (NgModule, other standalone Components)
211+ // and make those providers available in the DI tree.
212+ ...importProvidersFrom ( rootComponent ) ,
213+ ] ;
214+ const appInjector = createEnvironmentInjector (
215+ allAppProviders , platformInjector as EnvironmentInjector , 'Environment Injector' ) ;
216+
217+ // Instruct `StandaloneService` to use the `appInjector` as an injector for this
218+ // root component, so that there is no extra injector instance created.
219+ const componentDef = getComponentDef ( rootComponent ) ! ;
220+ appInjector . get ( StandaloneService ) . setInjector ( componentDef , appInjector ) ;
221+
222+ const exceptionHandler : ErrorHandler | null = appInjector . get ( ErrorHandler , null ) ;
223+ if ( NG_DEV_MODE && ! exceptionHandler ) {
224+ throw new RuntimeError (
225+ RuntimeErrorCode . ERROR_HANDLER_NOT_FOUND ,
226+ 'No `ErrorHandler` found in the Dependency Injection tree.' ) ;
227+ }
228+
229+ let onErrorSubscription : Subscription ;
230+ ngZone . runOutsideAngular ( ( ) => {
231+ onErrorSubscription = ngZone . onError . subscribe ( {
232+ next : ( error : any ) => {
233+ exceptionHandler ! . handleError ( error ) ;
234+ }
235+ } ) ;
236+ } ) ;
237+ return _callAndReportToErrorHandler ( exceptionHandler ! , ngZone , ( ) => {
238+ const initStatus = appInjector . get ( ApplicationInitStatus ) ;
239+ initStatus . runInitializers ( ) ;
240+ return initStatus . donePromise . then ( ( ) => {
241+ const localeId = appInjector . get ( LOCALE_ID , DEFAULT_LOCALE_ID ) ;
242+ setLocaleId ( localeId || DEFAULT_LOCALE_ID ) ;
243+
244+ const appRef = appInjector . get ( ApplicationRef ) ;
245+ appRef . onDestroy ( ( ) => onErrorSubscription . unsubscribe ( ) ) ;
246+ appRef . bootstrap ( rootComponent ) ;
247+ return appRef ;
248+ } ) ;
249+ } ) ;
250+ } ) ;
251+ }
252+
151253/**
152254 * Creates a factory for a platform. Can be used to provide or override `Providers` specific to
153255 * your application's runtime needs, such as `PLATFORM_INITIALIZER` and `PLATFORM_ID`.
@@ -242,8 +344,6 @@ export function getPlatform(): PlatformRef|null {
242344
243345/**
244346 * Provides additional options to the bootstraping process.
245- *
246- *
247347 */
248348export interface BootstrapOptions {
249349 /**
@@ -326,10 +426,7 @@ export class PlatformRef {
326426 // as instantiating the module creates some providers eagerly.
327427 // So we create a mini parent injector that just contains the new NgZone and
328428 // pass that as parent to the NgModuleFactory.
329- const ngZoneOption = options ? options . ngZone : undefined ;
330- const ngZoneEventCoalescing = ( options && options . ngZoneEventCoalescing ) || false ;
331- const ngZoneRunCoalescing = ( options && options . ngZoneRunCoalescing ) || false ;
332- const ngZone = getNgZone ( ngZoneOption , { ngZoneEventCoalescing, ngZoneRunCoalescing} ) ;
429+ const ngZone = getNgZone ( options ?. ngZone , getNgZoneOptions ( options ) ) ;
333430 const providers : StaticProvider [ ] = [ { provide : NgZone , useValue : ngZone } ] ;
334431 // Note: Create ngZoneInjector within ngZone.run so that all of the instantiated services are
335432 // created within the Angular zone
@@ -456,19 +553,31 @@ export class PlatformRef {
456553 }
457554}
458555
459- function getNgZone (
460- ngZoneOption : NgZone | 'zone.js' | 'noop' | undefined ,
461- extra ?: { ngZoneEventCoalescing : boolean , ngZoneRunCoalescing : boolean } ) : NgZone {
556+ // Set of options recognized by the NgZone.
557+ interface NgZoneOptions {
558+ enableLongStackTrace : boolean ;
559+ shouldCoalesceEventChangeDetection : boolean ;
560+ shouldCoalesceRunChangeDetection : boolean ;
561+ }
562+
563+ // Transforms a set of `BootstrapOptions` (supported by the NgModule-based bootstrap APIs) ->
564+ // `NgZoneOptions` that are recognized by the NgZone constructor. Passing no options will result in
565+ // a set of default options returned.
566+ function getNgZoneOptions ( options ?: BootstrapOptions ) : NgZoneOptions {
567+ return {
568+ enableLongStackTrace : typeof ngDevMode === 'undefined' ? false : ! ! ngDevMode ,
569+ shouldCoalesceEventChangeDetection : ! ! ( options && options . ngZoneEventCoalescing ) || false ,
570+ shouldCoalesceRunChangeDetection : ! ! ( options && options . ngZoneRunCoalescing ) || false ,
571+ } ;
572+ }
573+
574+ function getNgZone ( ngZoneToUse : NgZone | 'zone.js' | 'noop' | undefined , options : NgZoneOptions ) : NgZone {
462575 let ngZone : NgZone ;
463576
464- if ( ngZoneOption === 'noop' ) {
577+ if ( ngZoneToUse === 'noop' ) {
465578 ngZone = new NoopNgZone ( ) ;
466579 } else {
467- ngZone = ( ngZoneOption === 'zone.js' ? undefined : ngZoneOption ) || new NgZone ( {
468- enableLongStackTrace : typeof ngDevMode === 'undefined' ? false : ! ! ngDevMode ,
469- shouldCoalesceEventChangeDetection : ! ! extra ?. ngZoneEventCoalescing ,
470- shouldCoalesceRunChangeDetection : ! ! extra ?. ngZoneRunCoalescing
471- } ) ;
580+ ngZone = ( ngZoneToUse === 'zone.js' ? undefined : ngZoneToUse ) || new NgZone ( options ) ;
472581 }
473582 return ngZone ;
474583}
@@ -815,15 +924,20 @@ export class ApplicationRef {
815924 bootstrap < C > ( componentOrFactory : ComponentFactory < C > | Type < C > , rootSelectorOrNode ?: string | any ) :
816925 ComponentRef < C > {
817926 NG_DEV_MODE && this . warnIfDestroyed ( ) ;
927+ const isComponentFactory = componentOrFactory instanceof ComponentFactory ;
928+
818929 if ( ! this . _initStatus . done ) {
819- const errorMessage = ( typeof ngDevMode === 'undefined' || ngDevMode ) ?
820- 'Cannot bootstrap as there are still asynchronous initializers running. ' +
821- 'Bootstrap components in the `ngDoBootstrap` method of the root module.' :
822- '' ;
823- throw new RuntimeError ( RuntimeErrorCode . ASYNC_INITIALIZERS_STILL_RUNNING , errorMessage ) ;
930+ const standalone = ! isComponentFactory && isStandalone ( componentOrFactory ) ;
931+ const errorMessage =
932+ 'Cannot bootstrap as there are still asynchronous initializers running.' +
933+ ( standalone ? '' :
934+ ' Bootstrap components in the `ngDoBootstrap` method of the root module.' ) ;
935+ throw new RuntimeError (
936+ RuntimeErrorCode . ASYNC_INITIALIZERS_STILL_RUNNING , NG_DEV_MODE && errorMessage ) ;
824937 }
938+
825939 let componentFactory : ComponentFactory < C > ;
826- if ( componentOrFactory instanceof ComponentFactory ) {
940+ if ( isComponentFactory ) {
827941 componentFactory = componentOrFactory ;
828942 } else {
829943 const resolver = this . _injector . get ( ComponentFactoryResolver ) ;
0 commit comments