Skip to content

Commit 26ade4a

Browse files
atscottAndrewKushnir
authored andcommitted
fix(core): Ensure application remains unstable during bootstrap (#62631)
This commit ensures the application remains unstable during the entire bootstrap process. This ensures all bootstrap listeners and app initializers observe the application as being unstable until each one has gotten a chance to execute the synchronous block (potentially adding more pending tasks). Prior to this commit, application initializers or bootstrap listeners may observe the application as being stable, even though other initializers/listeners had not yet executed. This created an ordering issue whereby the hydration bootstrap listener would observe the application as stable prior to the router performing its initial navigation. fixes #62592 PR Close #62631
1 parent a81f0fa commit 26ade4a

File tree

2 files changed

+35
-25
lines changed

2 files changed

+35
-25
lines changed

packages/core/src/platform/bootstrap.ts

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {InjectionToken, Injector} from '../di';
2626
import {InternalNgModuleRef, NgModuleRef} from '../linker/ng_module_factory';
2727
import {stringify} from '../util/stringify';
2828
import {isPromise} from '../util/lang';
29+
import {PendingTasksInternal} from '../pending_tasks';
2930

3031
/**
3132
* InjectionToken to control root component bootstrap behavior.
@@ -132,38 +133,47 @@ export function bootstrap<M>(
132133
}
133134

134135
return _callAndReportToErrorHandler(exceptionHandler, ngZone, () => {
136+
const pendingTasks = envInjector.get(PendingTasksInternal);
137+
const taskId = pendingTasks.add();
135138
const initStatus = envInjector.get(ApplicationInitStatus);
136139
initStatus.runInitializers();
137140

138141
return initStatus.donePromise.then(() => {
139-
// If the `LOCALE_ID` provider is defined at bootstrap then we set the value for ivy
140-
const localeId = envInjector.get(LOCALE_ID, DEFAULT_LOCALE_ID);
141-
setLocaleId(localeId || DEFAULT_LOCALE_ID);
142-
143-
const enableRootComponentBoostrap = envInjector.get(ENABLE_ROOT_COMPONENT_BOOTSTRAP, true);
144-
if (!enableRootComponentBoostrap) {
145-
if (isApplicationBootstrapConfig(config)) {
146-
return envInjector.get(ApplicationRef);
142+
try {
143+
// If the `LOCALE_ID` provider is defined at bootstrap then we set the value for ivy
144+
const localeId = envInjector.get(LOCALE_ID, DEFAULT_LOCALE_ID);
145+
setLocaleId(localeId || DEFAULT_LOCALE_ID);
146+
147+
const enableRootComponentBoostrap = envInjector.get(
148+
ENABLE_ROOT_COMPONENT_BOOTSTRAP,
149+
true,
150+
);
151+
if (!enableRootComponentBoostrap) {
152+
if (isApplicationBootstrapConfig(config)) {
153+
return envInjector.get(ApplicationRef);
154+
}
155+
156+
config.allPlatformModules.push(config.moduleRef);
157+
return config.moduleRef;
147158
}
148159

149-
config.allPlatformModules.push(config.moduleRef);
150-
return config.moduleRef;
151-
}
152-
153-
if (typeof ngDevMode === 'undefined' || ngDevMode) {
154-
const imagePerformanceService = envInjector.get(ImagePerformanceWarning);
155-
imagePerformanceService.start();
156-
}
160+
if (typeof ngDevMode === 'undefined' || ngDevMode) {
161+
const imagePerformanceService = envInjector.get(ImagePerformanceWarning);
162+
imagePerformanceService.start();
163+
}
157164

158-
if (isApplicationBootstrapConfig(config)) {
159-
const appRef = envInjector.get(ApplicationRef);
160-
if (config.rootComponent !== undefined) {
161-
appRef.bootstrap(config.rootComponent);
165+
if (isApplicationBootstrapConfig(config)) {
166+
const appRef = envInjector.get(ApplicationRef);
167+
if (config.rootComponent !== undefined) {
168+
appRef.bootstrap(config.rootComponent);
169+
}
170+
return appRef;
171+
} else {
172+
moduleBootstrapImpl?.(config.moduleRef, config.allPlatformModules);
173+
return config.moduleRef;
162174
}
163-
return appRef;
164-
} else {
165-
moduleBootstrapImpl?.(config.moduleRef, config.allPlatformModules);
166-
return config.moduleRef;
175+
} finally {
176+
pendingTasks.remove(taskId);
167177
}
168178
});
169179
});

packages/platform-server/test/dom_utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,10 @@ export function hydrate(
110110
global.document = doc;
111111

112112
const providers = [
113-
...envProviders,
114113
{provide: PLATFORM_ID, useValue: 'browser'},
115114
{provide: DOCUMENT, useFactory: () => doc},
116115
provideClientHydration(...hydrationFeatures()),
116+
...envProviders,
117117
];
118118

119119
return bootstrapApplication(component, {providers});

0 commit comments

Comments
 (0)