feat(core): allow to add precompiled tokens via a provider

Introduces the new `ANALYZE_FOR_PRECOMPILE` token. This token can be used to
create a virtual provider that will populate the `precompile` fields of
components and app modules based on its
`useValue`. All components that are referenced in the `useValue`
value (either directly or in a nested array or map) will be added
to the `precompile` property.

closes #9874
related to #9726
This commit is contained in:
Tobias Bosch
2016-07-07 10:05:55 -07:00
parent 9d265b6f61
commit 7073cf74fe
12 changed files with 215 additions and 30 deletions

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AppModuleFactory, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ElementRef, Injector, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
import {ANALYZE_FOR_PRECOMPILE, AppModuleFactory, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ElementRef, Injector, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
import {AnimationGroupPlayer as AnimationGroupPlayer_, AnimationKeyframe as AnimationKeyframe_, AnimationSequencePlayer as AnimationSequencePlayer_, AnimationStyles as AnimationStyles_, AppElement, AppModuleInjector, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, DebugAppView, DebugContext, EMPTY_ARRAY, EMPTY_MAP, NoOpAnimationPlayer as NoOpAnimationPlayer_, StaticNodeDebugInfo, TemplateRef_, ValueUnwrapper, ViewType, ViewUtils, balanceAnimationKeyframes as impBalanceAnimationKeyframes, castByValue, checkBinding, clearStyles as impClearStyles, collectAndResolveStyles as impCollectAndResolveStyles, devModeEqual, flattenNestedViewRenderNodes, interpolate, prepareFinalAnimationStyles as impBalanceAnimationStyles, pureProxy1, pureProxy10, pureProxy2, pureProxy3, pureProxy4, pureProxy5, pureProxy6, pureProxy7, pureProxy8, pureProxy9, renderStyles as impRenderStyles, uninitialized} from '../core_private';
@ -58,6 +58,11 @@ var impNoOpAnimationPlayer = NoOpAnimationPlayer_;
var ANIMATION_STYLE_UTIL_ASSET_URL = assetUrl('core', 'animation/animation_style_util');
export class Identifiers {
static ANALYZE_FOR_PRECOMPILE = new CompileIdentifierMetadata({
name: 'ANALYZE_FOR_PRECOMPILE',
moduleUrl: assetUrl('core', 'metadata/di'),
runtime: ANALYZE_FOR_PRECOMPILE
});
static ViewUtils = new CompileIdentifierMetadata(
{name: 'ViewUtils', moduleUrl: assetUrl('core', 'linker/view_utils'), runtime: impViewUtils});
static AppView = new CompileIdentifierMetadata(

View File

@ -18,6 +18,7 @@ import * as cpl from './compile_metadata';
import {CompilerConfig} from './config';
import {hasLifecycleHook} from './directive_lifecycle_reflector';
import {DirectiveResolver} from './directive_resolver';
import {Identifiers, identifierToken} from './identifiers';
import {PipeResolver} from './pipe_resolver';
import {getUrlScheme} from './url_resolver';
import {MODULE_SUFFIX, ValueTransformer, sanitizeIdentifier, visitValue} from './util';
@ -137,7 +138,7 @@ export class CompileMetadataResolver {
changeDetectionStrategy = cmpMeta.changeDetection;
if (isPresent(dirMeta.viewProviders)) {
viewProviders = this.getProvidersMetadata(
verifyNonBlankProviders(directiveType, dirMeta.viewProviders, 'viewProviders'));
verifyNonBlankProviders(directiveType, dirMeta.viewProviders, 'viewProviders'), []);
}
moduleUrl = componentModuleUrl(this._reflector, directiveType, cmpMeta);
if (cmpMeta.precompile) {
@ -149,10 +150,11 @@ export class CompileMetadataResolver {
var providers: any[] /** TODO #9100 */ = [];
if (isPresent(dirMeta.providers)) {
providers = this.getProvidersMetadata(
verifyNonBlankProviders(directiveType, dirMeta.providers, 'providers'));
verifyNonBlankProviders(directiveType, dirMeta.providers, 'providers'),
precompileTypes);
}
var queries: any[] /** TODO #9100 */ = [];
var viewQueries: any[] /** TODO #9100 */ = [];
var queries: cpl.CompileQueryMetadata[] = [];
var viewQueries: cpl.CompileQueryMetadata[] = [];
if (isPresent(dirMeta.queries)) {
queries = this.getQueriesMetadata(dirMeta.queries, false, directiveType);
viewQueries = this.getQueriesMetadata(dirMeta.queries, true, directiveType);
@ -214,7 +216,7 @@ export class CompileMetadataResolver {
}
if (meta.providers) {
providers.push(...this.getProvidersMetadata(meta.providers));
providers.push(...this.getProvidersMetadata(meta.providers, precompile));
}
if (meta.directives) {
directives.push(...flattenArray(meta.directives)
@ -410,23 +412,54 @@ export class CompileMetadataResolver {
return compileToken;
}
getProvidersMetadata(providers: any[]):
getProvidersMetadata(providers: any[], targetPrecompileComponents: cpl.CompileTypeMetadata[]):
Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> {
return providers.map((provider) => {
const compileProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
providers.forEach((provider) => {
provider = resolveForwardRef(provider);
if (isProviderLiteral(provider)) {
provider = createProvider(provider);
}
let compileProvider: cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[];
if (isArray(provider)) {
return this.getProvidersMetadata(provider);
compileProvider = this.getProvidersMetadata(provider, targetPrecompileComponents);
} else if (provider instanceof Provider) {
return this.getProviderMetadata(provider);
} else if (isProviderLiteral(provider)) {
return this.getProviderMetadata(createProvider(provider));
let tokenMeta = this.getTokenMetadata(provider.token);
if (tokenMeta.equalsTo(identifierToken(Identifiers.ANALYZE_FOR_PRECOMPILE))) {
targetPrecompileComponents.push(...this.getPrecompileComponentsFromProvider(provider));
} else {
compileProvider = this.getProviderMetadata(provider);
}
} else if (isValidType(provider)) {
return this.getTypeMetadata(provider, staticTypeModuleUrl(provider));
compileProvider = this.getTypeMetadata(provider, staticTypeModuleUrl(provider));
} else {
throw new BaseException(
`Invalid provider - only instances of Provider and Type are allowed, got: ${stringify(provider)}`);
}
if (compileProvider) {
compileProviders.push(compileProvider);
}
});
return compileProviders;
}
getPrecompileComponentsFromProvider(provider: Provider): cpl.CompileTypeMetadata[] {
let components: cpl.CompileTypeMetadata[] = [];
let collectedIdentifiers: cpl.CompileIdentifierMetadata[] = [];
if (provider.useFactory || provider.useExisting || provider.useClass) {
throw new BaseException(`The ANALYZE_FOR_PRECOMPILE token only supports useValue!`);
}
if (!provider.multi) {
throw new BaseException(`The ANALYZE_FOR_PRECOMPILE token only supports 'multi = true'!`);
}
convertToCompileValue(provider.useValue, collectedIdentifiers);
collectedIdentifiers.forEach((identifier) => {
let dirMeta = this.maybeGetDirectiveMetadata(identifier.runtime);
if (dirMeta) {
components.push(dirMeta.type);
}
});
return components;
}
getProviderMetadata(provider: Provider): cpl.CompileProviderMetadata {
@ -447,7 +480,7 @@ export class CompileMetadataResolver {
return new cpl.CompileProviderMetadata({
token: this.getTokenMetadata(provider.token),
useClass: compileTypeMetadata,
useValue: convertToCompileValue(provider.useValue),
useValue: convertToCompileValue(provider.useValue, []),
useFactory: compileFactoryMetadata,
useExisting: isPresent(provider.useExisting) ? this.getTokenMetadata(provider.useExisting) :
null,
@ -566,17 +599,21 @@ function componentModuleUrl(
return reflector.importUri(type);
}
// Only fill CompileIdentifierMetadata.runtime if needed...
function convertToCompileValue(value: any): any {
return visitValue(value, new _CompileValueConverter(), null);
function convertToCompileValue(
value: any, targetIdentifiers: cpl.CompileIdentifierMetadata[]): any {
return visitValue(value, new _CompileValueConverter(), targetIdentifiers);
}
class _CompileValueConverter extends ValueTransformer {
visitOther(value: any, context: any): any {
visitOther(value: any, targetIdentifiers: cpl.CompileIdentifierMetadata[]): any {
let identifier: cpl.CompileIdentifierMetadata;
if (cpl.isStaticSymbol(value)) {
return new cpl.CompileIdentifierMetadata({name: value.name, moduleUrl: value.filePath});
identifier = new cpl.CompileIdentifierMetadata(
{name: value.name, moduleUrl: value.filePath, runtime: value});
} else {
return new cpl.CompileIdentifierMetadata({runtime: value});
identifier = new cpl.CompileIdentifierMetadata({runtime: value});
}
targetIdentifiers.push(identifier);
return identifier;
}
}