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:
@ -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