Fixes #25018. Instantiating a NgModuleRef from NgModuleFactory reuses the NgModuleDefinition if it is already present. However the NgModuleDefinition has a providers array which modified when tree shakable providers are instantiated. This corrupts the provider definitions the next time the same factory is used to create a new NgModuleRef - Two provider definitions can end up with the same index anf the injector could potentially return a completely wrong object for a provider token. This scenario is more likely on the server where the same NgModuleFactory is reused across requests. The fix clones the cached NgModuleDefinition so that any tree shakable providers added later do not affect the cached copy. PR Close #25022
		
			
				
	
	
		
			74 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			74 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| /**
 | |
|  * @license
 | |
|  * Copyright Google Inc. All Rights Reserved.
 | |
|  *
 | |
|  * Use of this source code is governed by an MIT-style license that can be
 | |
|  * found in the LICENSE file at https://angular.io/license
 | |
|  */
 | |
| 
 | |
| import {Injector} from '../di/injector';
 | |
| import {ComponentFactory} from '../linker/component_factory';
 | |
| import {NgModuleFactory, NgModuleRef} from '../linker/ng_module_factory';
 | |
| import {Type} from '../type';
 | |
| 
 | |
| import {initServicesIfNeeded} from './services';
 | |
| import {NgModuleDefinition, NgModuleDefinitionFactory, NgModuleProviderDef, ProviderOverride, Services, ViewDefinition} from './types';
 | |
| import {resolveDefinition} from './util';
 | |
| 
 | |
| export function overrideProvider(override: ProviderOverride) {
 | |
|   initServicesIfNeeded();
 | |
|   return Services.overrideProvider(override);
 | |
| }
 | |
| 
 | |
| export function overrideComponentView(comp: Type<any>, componentFactory: ComponentFactory<any>) {
 | |
|   initServicesIfNeeded();
 | |
|   return Services.overrideComponentView(comp, componentFactory);
 | |
| }
 | |
| 
 | |
| export function clearOverrides() {
 | |
|   initServicesIfNeeded();
 | |
|   return Services.clearOverrides();
 | |
| }
 | |
| 
 | |
| // Attention: this function is called as top level function.
 | |
| // Putting any logic in here will destroy closure tree shaking!
 | |
| export function createNgModuleFactory(
 | |
|     ngModuleType: Type<any>, bootstrapComponents: Type<any>[],
 | |
|     defFactory: NgModuleDefinitionFactory): NgModuleFactory<any> {
 | |
|   return new NgModuleFactory_(ngModuleType, bootstrapComponents, defFactory);
 | |
| }
 | |
| 
 | |
| function cloneNgModuleDefinition(def: NgModuleDefinition): NgModuleDefinition {
 | |
|   const providers = Array.from(def.providers);
 | |
|   const modules = Array.from(def.modules);
 | |
|   const providersByKey: {[tokenKey: string]: NgModuleProviderDef} = {};
 | |
|   for (const key in def.providersByKey) {
 | |
|     providersByKey[key] = def.providersByKey[key];
 | |
|   }
 | |
| 
 | |
|   return {
 | |
|     factory: def.factory,
 | |
|     isRoot: def.isRoot, providers, modules, providersByKey,
 | |
|   };
 | |
| }
 | |
| 
 | |
| class NgModuleFactory_ extends NgModuleFactory<any> {
 | |
|   constructor(
 | |
|       public readonly moduleType: Type<any>, private _bootstrapComponents: Type<any>[],
 | |
|       private _ngModuleDefFactory: NgModuleDefinitionFactory) {
 | |
|     // Attention: this ctor is called as top level function.
 | |
|     // Putting any logic in here will destroy closure tree shaking!
 | |
|     super();
 | |
|   }
 | |
| 
 | |
|   create(parentInjector: Injector|null): NgModuleRef<any> {
 | |
|     initServicesIfNeeded();
 | |
|     // Clone the NgModuleDefinition so that any tree shakeable provider definition
 | |
|     // added to this instance of the NgModuleRef doesn't affect the cached copy.
 | |
|     // See https://github.com/angular/angular/issues/25018.
 | |
|     const def = cloneNgModuleDefinition(resolveDefinition(this._ngModuleDefFactory));
 | |
|     return Services.createNgModuleRef(
 | |
|         this.moduleType, parentInjector || Injector.NULL, this._bootstrapComponents, def);
 | |
|   }
 | |
| }
 |