diff --git a/packages/core/src/core_private_export.ts b/packages/core/src/core_private_export.ts index 9ef6d446bb7269..0734acee89d8b1 100644 --- a/packages/core/src/core_private_export.ts +++ b/packages/core/src/core_private_export.ts @@ -107,6 +107,7 @@ export { PendingTasksInternal as ɵPendingTasks, // TODO(atscott): remove once there is a release with PendingTasksInternal so adev can be updated PendingTasksInternal as ɵPendingTasksInternal, } from './pending_tasks'; +export {DISABLE_COMPONENT_BOOTSTRAP as ɵDISABLE_COMPONENT_BOOTSTRAP} from './platform/bootstrap'; export {ALLOW_MULTIPLE_PLATFORMS as ɵALLOW_MULTIPLE_PLATFORMS} from './platform/platform'; export {ReflectionCapabilities as ɵReflectionCapabilities} from './reflection/reflection_capabilities'; export {AnimationRendererType as ɵAnimationRendererType} from './render/api'; diff --git a/packages/core/src/platform/bootstrap.ts b/packages/core/src/platform/bootstrap.ts index c9d802754770d6..e79158fbb56d7a 100644 --- a/packages/core/src/platform/bootstrap.ts +++ b/packages/core/src/platform/bootstrap.ts @@ -8,7 +8,7 @@ import {Subscription} from 'rxjs'; import {PROVIDED_NG_ZONE} from '../change_detection/scheduling/ng_zone_scheduling'; -import {EnvironmentInjector, R3Injector} from '../di/r3_injector'; +import {R3Injector} from '../di/r3_injector'; import {ErrorHandler} from '../error_handler'; import {RuntimeError, RuntimeErrorCode} from '../errors'; import {DEFAULT_LOCALE_ID} from '../i18n/localization'; @@ -22,10 +22,29 @@ import {NgZone} from '../zone/ng_zone'; import {ApplicationInitStatus} from '../application/application_init'; import {_callAndReportToErrorHandler, ApplicationRef, remove} from '../application/application_ref'; import {PROVIDED_ZONELESS} from '../change_detection/scheduling/zoneless_scheduling'; -import {Injector} from '../di'; +import {InjectionToken, Injector} from '../di'; import {InternalNgModuleRef, NgModuleRef} from '../linker/ng_module_factory'; import {stringify} from '../util/stringify'; +/** + * InjectionToken used to disable the bootstrap of components. + * + * This token is used by the Angular CLI in the `@angular/ssr` package to control + * whether components should be bootstrapped during the application initialization process. + * Specifically, it is used during route extraction when server-side rendering is enabled. + * + * By setting this token to `true`, Angular prevents the root component from being bootstrapped + * and avoids invoking it during the route extraction phase, which is necessary for SSR + * to extract routes without requiring new APIs to be exposed. This mechanism helps separate + * the logic for route extraction and component bootstrapping during SSR rendering. + * + * When not provided or set to `false`, Angular will continue with the default behavior + * of bootstrapping the root component(s) during initialization. + */ +export const DISABLE_COMPONENT_BOOTSTRAP = new InjectionToken( + ngDevMode ? '' : 'DISABLE_ROOT_COMPONENT_BOOTSTRAP', +); + export interface BootstrapConfig { platformInjector: Injector; } @@ -125,6 +144,19 @@ export function bootstrap( // If the `LOCALE_ID` provider is defined at bootstrap then we set the value for ivy const localeId = envInjector.get(LOCALE_ID, DEFAULT_LOCALE_ID); setLocaleId(localeId || DEFAULT_LOCALE_ID); + + const disableComponentsBootstrap = envInjector.get(DISABLE_COMPONENT_BOOTSTRAP, false, { + optional: true, + }); + if (disableComponentsBootstrap) { + if (isApplicationBootstrapConfig(config)) { + return envInjector.get(ApplicationRef); + } + + config.allPlatformModules.push(config.moduleRef); + return config.moduleRef; + } + if (typeof ngDevMode === 'undefined' || ngDevMode) { const imagePerformanceService = envInjector.get(ImagePerformanceWarning); imagePerformanceService.start();