Skip to content

Commit e250db4

Browse files
AndrewKushnirdylhunn
authored andcommitted
refactor(core): avoid referencing PlatformRef in bootstrap code (angular#45519)
This commit updates an existing bootstrap logic to avoid referencing the `PlatformRef` instance to keep track of the platform status. Instead, we use platform injector, so that the `PlatformRef`can be tree-shaken away in the bootstrap logic for Standalone Components. The motivation for this change is that retaining the `PlatformRef` class also retains NgModule-based bootstrap code, which would not be needed in case of Standalone Components. PR Close angular#45519
1 parent fbbed3d commit e250db4

File tree

5 files changed

+64
-26
lines changed

5 files changed

+64
-26
lines changed

packages/core/src/application_ref.ts

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,21 @@ import {scheduleMicroTask} from './util/microtask';
4343
import {stringify} from './util/stringify';
4444
import {NgZone, NoopNgZone} from './zone/ng_zone';
4545

46-
let _platform: PlatformRef;
46+
let _platformInjector: Injector|null = null;
47+
48+
/**
49+
* Internal token to indicate whether having multiple bootstrapped platform should be allowed (only
50+
* one bootstrapped platform is allowed by default). This token helps to support SSR scenarios.
51+
*/
52+
export const ALLOW_MULTIPLE_PLATFORMS = new InjectionToken<boolean>('AllowMultipleToken');
53+
54+
/**
55+
* Internal token that allows to register extra callbacks that should be invoked during the
56+
* `PlatformRef.destroy` operation. This token is needed to avoid a direct reference to the
57+
* `PlatformRef` class (i.e. register the callback via `PlatformRef.onDestroy`), thus making the
58+
* entire class tree-shakeable.
59+
*/
60+
const PLATFORM_ON_DESTROY = new InjectionToken<() => void>('PlatformOnDestroy');
4761

4862
export function compileNgModuleFactory<M>(
4963
injector: Injector, options: CompilerOptions,
@@ -102,10 +116,6 @@ export function isBoundToModule<C>(cf: ComponentFactory<C>): boolean {
102116
return (cf as R3ComponentFactory<C>).isBoundToModule;
103117
}
104118

105-
export const ALLOW_MULTIPLE_PLATFORMS = new InjectionToken<boolean>('AllowMultipleToken');
106-
107-
108-
109119
/**
110120
* A token for third-party components that can register themselves with NgProbe.
111121
*
@@ -122,18 +132,18 @@ export class NgProbeToken {
122132
* @publicApi
123133
*/
124134
export function createPlatform(injector: Injector): PlatformRef {
125-
if (_platform && !_platform.destroyed &&
126-
!_platform.injector.get(ALLOW_MULTIPLE_PLATFORMS, false)) {
135+
if (_platformInjector && !_platformInjector.get(ALLOW_MULTIPLE_PLATFORMS, false)) {
127136
const errorMessage = (typeof ngDevMode === 'undefined' || ngDevMode) ?
128137
'There can be only one platform. Destroy the previous one to create a new one.' :
129138
'';
130139
throw new RuntimeError(RuntimeErrorCode.MULTIPLE_PLATFORMS, errorMessage);
131140
}
132141
publishDefaultGlobalUtils();
133-
_platform = injector.get(PlatformRef);
142+
_platformInjector = injector;
143+
const platform = injector.get(PlatformRef);
134144
const inits = injector.get(PLATFORM_INITIALIZER, null);
135-
if (inits) inits.forEach((init: any) => init());
136-
return _platform;
145+
if (inits) inits.forEach(initFn => initFn());
146+
return platform;
137147
}
138148

139149
/**
@@ -155,16 +165,15 @@ export function createPlatformFactory(
155165
return (extraProviders: StaticProvider[] = []) => {
156166
let platform = getPlatform();
157167
if (!platform || platform.injector.get(ALLOW_MULTIPLE_PLATFORMS, false)) {
168+
const platformProviders: StaticProvider[] = [
169+
...providers, //
170+
...extraProviders, //
171+
{provide: marker, useValue: true}
172+
];
158173
if (parentPlatformFactory) {
159-
parentPlatformFactory(
160-
providers.concat(extraProviders).concat({provide: marker, useValue: true}));
174+
parentPlatformFactory(platformProviders);
161175
} else {
162-
const injectedProviders: StaticProvider[] =
163-
providers.concat(extraProviders).concat({provide: marker, useValue: true}, {
164-
provide: INJECTOR_SCOPE,
165-
useValue: 'platform'
166-
});
167-
createPlatform(Injector.create({providers: injectedProviders, name: desc}));
176+
createPlatform(createPlatformInjector(platformProviders, desc));
168177
}
169178
}
170179
return assertPlatform(marker);
@@ -195,16 +204,29 @@ export function assertPlatform(requiredToken: any): PlatformRef {
195204
return platform;
196205
}
197206

207+
/**
208+
* Helper function to create an instance of a platform injector (that maintains the 'platform'
209+
* scope).
210+
*/
211+
export function createPlatformInjector(providers: StaticProvider[] = [], name?: string): Injector {
212+
return Injector.create({
213+
name,
214+
providers: [
215+
{provide: INJECTOR_SCOPE, useValue: 'platform'},
216+
{provide: PLATFORM_ON_DESTROY, useValue: () => _platformInjector = null}, //
217+
...providers
218+
],
219+
});
220+
}
221+
198222
/**
199223
* Destroys the current Angular platform and all Angular applications on the page.
200224
* Destroys all modules and listeners registered with the platform.
201225
*
202226
* @publicApi
203227
*/
204228
export function destroyPlatform(): void {
205-
if (_platform && !_platform.destroyed) {
206-
_platform.destroy();
207-
}
229+
getPlatform()?.destroy();
208230
}
209231

210232
/**
@@ -213,7 +235,7 @@ export function destroyPlatform(): void {
213235
* @publicApi
214236
*/
215237
export function getPlatform(): PlatformRef|null {
216-
return _platform && !_platform.destroyed ? _platform : null;
238+
return _platformInjector?.get(PlatformRef) ?? null;
217239
}
218240

219241
/**
@@ -417,6 +439,10 @@ export class PlatformRef {
417439
}
418440
this._modules.slice().forEach(module => module.destroy());
419441
this._destroyListeners.forEach(listener => listener());
442+
443+
const destroyListener = this._injector.get(PLATFORM_ON_DESTROY, null);
444+
destroyListener?.();
445+
420446
this._destroyed = true;
421447
}
422448

packages/core/test/bundling/animations/bundle.golden_symbols.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,9 @@
353353
{
354354
"name": "PLATFORM_INITIALIZER"
355355
},
356+
{
357+
"name": "PLATFORM_ON_DESTROY"
358+
},
356359
{
357360
"name": "PlatformRef"
358361
},
@@ -531,7 +534,7 @@
531534
"name": "_keyMap"
532535
},
533536
{
534-
"name": "_platform"
537+
"name": "_platformInjector"
535538
},
536539
{
537540
"name": "_query"

packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,9 @@
356356
{
357357
"name": "PLATFORM_INITIALIZER"
358358
},
359+
{
360+
"name": "PLATFORM_ON_DESTROY"
361+
},
359362
{
360363
"name": "PlatformRef"
361364
},
@@ -534,7 +537,7 @@
534537
"name": "_keyMap"
535538
},
536539
{
537-
"name": "_platform"
540+
"name": "_platformInjector"
538541
},
539542
{
540543
"name": "_randomChar"

packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,9 @@
350350
{
351351
"name": "PLATFORM_INITIALIZER"
352352
},
353+
{
354+
"name": "PLATFORM_ON_DESTROY"
355+
},
353356
{
354357
"name": "PlatformRef"
355358
},
@@ -525,7 +528,7 @@
525528
"name": "_keyMap"
526529
},
527530
{
528-
"name": "_platform"
531+
"name": "_platformInjector"
529532
},
530533
{
531534
"name": "_randomChar"

packages/core/test/bundling/router/bundle.golden_symbols.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,9 @@
422422
{
423423
"name": "PLATFORM_INITIALIZER"
424424
},
425+
{
426+
"name": "PLATFORM_ON_DESTROY"
427+
},
425428
{
426429
"name": "PathLocationStrategy"
427430
},
@@ -762,7 +765,7 @@
762765
"name": "_keyMap"
763766
},
764767
{
765-
"name": "_platform"
768+
"name": "_platformInjector"
766769
},
767770
{
768771
"name": "_randomChar"

0 commit comments

Comments
 (0)