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:
@ -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(
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user