From 2d5e7d1b52c4dae58f0b635667cd2e8a9c1e063b Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Fri, 2 Feb 2018 09:31:58 -0800 Subject: [PATCH] feat(compiler): mark @NgModules in provider lists for identification at runtime (#22005) All of the providers in a module get compiled into a module definition in the factory file. Some of these providers are for the actual module types, as those are available for injection in Angular. For tree-shakeable tokens, the runtime needs to be able to distinguish which modules are present in an injector. This change adds a NodeFlag which tags those module providers for later identification. PR Close #22005 --- packages/compiler/src/core.ts | 1 + packages/compiler/src/provider_analyzer.ts | 16 ++++++++-------- .../compiler/src/template_parser/template_ast.ts | 3 ++- .../src/view_compiler/provider_compiler.ts | 3 +++ packages/core/src/view/ng_module.ts | 7 ++++++- packages/core/src/view/refs.ts | 1 + packages/core/src/view/types.ts | 7 +++++++ 7 files changed, 28 insertions(+), 10 deletions(-) diff --git a/packages/compiler/src/core.ts b/packages/compiler/src/core.ts index e1bc491676..4f0161dcaf 100644 --- a/packages/compiler/src/core.ts +++ b/packages/compiler/src/core.ts @@ -194,6 +194,7 @@ export const enum NodeFlags { TypeViewQuery = 1 << 27, StaticQuery = 1 << 28, DynamicQuery = 1 << 29, + TypeModuleProvider = 1 << 30, CatQuery = TypeContentQuery | TypeViewQuery, // mutually exclusive values... diff --git a/packages/compiler/src/provider_analyzer.ts b/packages/compiler/src/provider_analyzer.ts index cdcfce3362..8bd133a7f9 100644 --- a/packages/compiler/src/provider_analyzer.ts +++ b/packages/compiler/src/provider_analyzer.ts @@ -321,11 +321,11 @@ export class NgModuleProviderAnalyzer { const ngModuleProvider = {token: {identifier: ngModuleType}, useClass: ngModuleType}; _resolveProviders( [ngModuleProvider], ProviderAstType.PublicService, true, sourceSpan, this._errors, - this._allProviders); + this._allProviders, true); }); _resolveProviders( ngModule.transitiveModule.providers.map(entry => entry.provider).concat(extraProviders), - ProviderAstType.PublicService, false, sourceSpan, this._errors, this._allProviders); + ProviderAstType.PublicService, false, sourceSpan, this._errors, this._allProviders, false); } parse(): ProviderAst[] { @@ -448,7 +448,7 @@ function _transformProviderAst( {eager, providers}: {eager: boolean, providers: CompileProviderMetadata[]}): ProviderAst { return new ProviderAst( provider.token, provider.multiProvider, provider.eager || eager, providers, - provider.providerType, provider.lifecycleHooks, provider.sourceSpan); + provider.providerType, provider.lifecycleHooks, provider.sourceSpan, provider.isModule); } function _resolveProvidersFromDirectives( @@ -461,7 +461,7 @@ function _resolveProvidersFromDirectives( _resolveProviders( [dirProvider], directive.isComponent ? ProviderAstType.Component : ProviderAstType.Directive, true, - sourceSpan, targetErrors, providersByToken); + sourceSpan, targetErrors, providersByToken, false); }); // Note: directives need to be able to overwrite providers of a component! @@ -470,10 +470,10 @@ function _resolveProvidersFromDirectives( directivesWithComponentFirst.forEach((directive) => { _resolveProviders( directive.providers, ProviderAstType.PublicService, false, sourceSpan, targetErrors, - providersByToken); + providersByToken, false); _resolveProviders( directive.viewProviders, ProviderAstType.PrivateService, false, sourceSpan, targetErrors, - providersByToken); + providersByToken, false); }); return providersByToken; } @@ -481,7 +481,7 @@ function _resolveProvidersFromDirectives( function _resolveProviders( providers: CompileProviderMetadata[], providerType: ProviderAstType, eager: boolean, sourceSpan: ParseSourceSpan, targetErrors: ParseError[], - targetProvidersByToken: Map) { + targetProvidersByToken: Map, isModule: boolean) { providers.forEach((provider) => { let resolvedProvider = targetProvidersByToken.get(tokenReference(provider.token)); if (resolvedProvider != null && !!resolvedProvider.multiProvider !== !!provider.multi) { @@ -497,7 +497,7 @@ function _resolveProviders( const isUseValue = !(provider.useClass || provider.useExisting || provider.useFactory); resolvedProvider = new ProviderAst( provider.token, !!provider.multi, eager || isUseValue, [provider], providerType, - lifecycleHooks, sourceSpan); + lifecycleHooks, sourceSpan, isModule); targetProvidersByToken.set(tokenReference(provider.token), resolvedProvider); } else { if (!provider.multi) { diff --git a/packages/compiler/src/template_parser/template_ast.ts b/packages/compiler/src/template_parser/template_ast.ts index a4ead18fa0..c09fb8ed09 100644 --- a/packages/compiler/src/template_parser/template_ast.ts +++ b/packages/compiler/src/template_parser/template_ast.ts @@ -192,7 +192,8 @@ export class ProviderAst implements TemplateAst { constructor( public token: CompileTokenMetadata, public multiProvider: boolean, public eager: boolean, public providers: CompileProviderMetadata[], public providerType: ProviderAstType, - public lifecycleHooks: LifecycleHooks[], public sourceSpan: ParseSourceSpan) {} + public lifecycleHooks: LifecycleHooks[], public sourceSpan: ParseSourceSpan, + readonly isModule: boolean) {} visit(visitor: TemplateAstVisitor, context: any): any { // No visit method in the visitor for now... diff --git a/packages/compiler/src/view_compiler/provider_compiler.ts b/packages/compiler/src/view_compiler/provider_compiler.ts index 0babaf57bc..09b53e42ae 100644 --- a/packages/compiler/src/view_compiler/provider_compiler.ts +++ b/packages/compiler/src/view_compiler/provider_compiler.ts @@ -29,6 +29,9 @@ export function providerDef(ctx: OutputContext, providerAst: ProviderAst): { if (providerAst.providerType === ProviderAstType.PrivateService) { flags |= NodeFlags.PrivateProvider; } + if (providerAst.isModule) { + flags |= NodeFlags.TypeModuleProvider; + } providerAst.lifecycleHooks.forEach((lifecycleHook) => { // for regular providers, we only support ngOnDestroy if (lifecycleHook === LifecycleHooks.OnDestroy || diff --git a/packages/core/src/view/ng_module.ts b/packages/core/src/view/ng_module.ts index 74fc91ddb2..b438555a83 100644 --- a/packages/core/src/view/ng_module.ts +++ b/packages/core/src/view/ng_module.ts @@ -36,8 +36,12 @@ export function moduleProvideDef( export function moduleDef(providers: NgModuleProviderDef[]): NgModuleDefinition { const providersByKey: {[key: string]: NgModuleProviderDef} = {}; + const modules = []; for (let i = 0; i < providers.length; i++) { const provider = providers[i]; + if (provider.flags & NodeFlags.TypeNgModule) { + modules.push(provider.token); + } provider.index = i; providersByKey[tokenKey(provider.token)] = provider; } @@ -45,7 +49,8 @@ export function moduleDef(providers: NgModuleProviderDef[]): NgModuleDefinition // Will be filled later... factory: null, providersByKey, - providers + providers, + modules, }; } diff --git a/packages/core/src/view/refs.ts b/packages/core/src/view/refs.ts index 72651b7d14..010d6d8a6d 100644 --- a/packages/core/src/view/refs.ts +++ b/packages/core/src/view/refs.ts @@ -480,6 +480,7 @@ class NgModuleRef_ implements NgModuleData, InternalNgModuleRef { private _destroyed: boolean = false; /** @internal */ _providers: any[]; + _modules: any[]; readonly injector: Injector = this; diff --git a/packages/core/src/view/types.ts b/packages/core/src/view/types.ts index dd6cbeaacb..d6dab11ee6 100644 --- a/packages/core/src/view/types.ts +++ b/packages/core/src/view/types.ts @@ -42,6 +42,7 @@ export interface Definition> { factory: DF|nul export interface NgModuleDefinition extends Definition { providers: NgModuleProviderDef[]; providersByKey: {[tokenKey: string]: NgModuleProviderDef}; + modules: any[]; } export interface NgModuleDefinitionFactory extends DefinitionFactory {} @@ -192,6 +193,7 @@ export const enum NodeFlags { TypeViewQuery = 1 << 27, StaticQuery = 1 << 28, DynamicQuery = 1 << 29, + TypeNgModule = 1 << 30, CatQuery = TypeContentQuery | TypeViewQuery, // mutually exclusive values... @@ -293,6 +295,11 @@ export const enum DepFlags { Value = 2 << 2, } +export interface InjectableDef { + scope: any; + factory: () => any; +} + export interface TextDef { prefix: string; } export interface QueryDef {