refactor(compiler): never create CompileDirectiveMetadata with not loaded resources (#12788)
Part of #12787
This commit is contained in:
parent
c3c0e2e2a2
commit
79383ce150
|
@ -129,10 +129,10 @@ export class CodeGenerator {
|
||||||
const resolver = new compiler.CompileMetadataResolver(
|
const resolver = new compiler.CompileMetadataResolver(
|
||||||
new compiler.NgModuleResolver(staticReflector),
|
new compiler.NgModuleResolver(staticReflector),
|
||||||
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
||||||
elementSchemaRegistry, staticReflector);
|
elementSchemaRegistry, normalizer, staticReflector);
|
||||||
// TODO(vicb): do not pass cliOptions.i18nFormat here
|
// TODO(vicb): do not pass cliOptions.i18nFormat here
|
||||||
const offlineCompiler = new compiler.OfflineCompiler(
|
const offlineCompiler = new compiler.OfflineCompiler(
|
||||||
resolver, normalizer, tmplParser, new compiler.StyleCompiler(urlResolver),
|
resolver, tmplParser, new compiler.StyleCompiler(urlResolver),
|
||||||
new compiler.ViewCompiler(config, elementSchemaRegistry),
|
new compiler.ViewCompiler(config, elementSchemaRegistry),
|
||||||
new compiler.DirectiveWrapperCompiler(
|
new compiler.DirectiveWrapperCompiler(
|
||||||
config, expressionParser, elementSchemaRegistry, console),
|
config, expressionParser, elementSchemaRegistry, console),
|
||||||
|
|
|
@ -28,50 +28,40 @@ export class Extractor {
|
||||||
private options: tsc.AngularCompilerOptions, private program: ts.Program,
|
private options: tsc.AngularCompilerOptions, private program: ts.Program,
|
||||||
public host: ts.CompilerHost, private staticReflector: StaticReflector,
|
public host: ts.CompilerHost, private staticReflector: StaticReflector,
|
||||||
private messageBundle: compiler.MessageBundle, private reflectorHost: ReflectorHost,
|
private messageBundle: compiler.MessageBundle, private reflectorHost: ReflectorHost,
|
||||||
private metadataResolver: compiler.CompileMetadataResolver,
|
private metadataResolver: compiler.CompileMetadataResolver) {}
|
||||||
private directiveNormalizer: compiler.DirectiveNormalizer) {}
|
|
||||||
|
|
||||||
extract(): Promise<compiler.MessageBundle> {
|
extract(): Promise<compiler.MessageBundle> {
|
||||||
const programSymbols: StaticSymbol[] =
|
const programSymbols: StaticSymbol[] =
|
||||||
extractProgramSymbols(this.program, this.staticReflector, this.reflectorHost, this.options);
|
extractProgramSymbols(this.program, this.staticReflector, this.reflectorHost, this.options);
|
||||||
|
|
||||||
const files =
|
return compiler
|
||||||
compiler.analyzeNgModules(programSymbols, {transitiveModules: true}, this.metadataResolver)
|
.analyzeNgModules(programSymbols, {transitiveModules: true}, this.metadataResolver)
|
||||||
.files;
|
.then(({files}) => {
|
||||||
const errors: compiler.ParseError[] = [];
|
const errors: compiler.ParseError[] = [];
|
||||||
const filePromises: Promise<any>[] = [];
|
|
||||||
|
|
||||||
files.forEach(file => {
|
files.forEach(file => {
|
||||||
const cmpPromises: Promise<compiler.CompileDirectiveMetadata>[] = [];
|
const compMetas: compiler.CompileDirectiveMetadata[] = [];
|
||||||
file.directives.forEach(directiveType => {
|
file.directives.forEach(directiveType => {
|
||||||
const dirMeta = this.metadataResolver.getDirectiveMetadata(directiveType);
|
const dirMeta = this.metadataResolver.getDirectiveMetadata(directiveType);
|
||||||
if (dirMeta.isComponent) {
|
if (dirMeta && dirMeta.isComponent) {
|
||||||
cmpPromises.push(this.directiveNormalizer.normalizeDirective(dirMeta).asyncResult);
|
compMetas.push(dirMeta);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (cmpPromises.length) {
|
|
||||||
const done =
|
|
||||||
Promise.all(cmpPromises).then((compMetas: compiler.CompileDirectiveMetadata[]) => {
|
|
||||||
compMetas.forEach(compMeta => {
|
compMetas.forEach(compMeta => {
|
||||||
const html = compMeta.template.template;
|
const html = compMeta.template.template;
|
||||||
const interpolationConfig =
|
const interpolationConfig =
|
||||||
compiler.InterpolationConfig.fromArray(compMeta.template.interpolation);
|
compiler.InterpolationConfig.fromArray(compMeta.template.interpolation);
|
||||||
errors.push(...this.messageBundle.updateFromTemplate(
|
errors.push(
|
||||||
html, file.srcUrl, interpolationConfig));
|
...this.messageBundle.updateFromTemplate(html, file.srcUrl, interpolationConfig));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
filePromises.push(done);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
if (errors.length) {
|
if (errors.length) {
|
||||||
throw new Error(errors.map(e => e.toString()).join('\n'));
|
throw new Error(errors.map(e => e.toString()).join('\n'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(filePromises).then(_ => this.messageBundle);
|
return this.messageBundle;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static create(
|
static create(
|
||||||
|
@ -98,13 +88,12 @@ export class Extractor {
|
||||||
const resolver = new compiler.CompileMetadataResolver(
|
const resolver = new compiler.CompileMetadataResolver(
|
||||||
new compiler.NgModuleResolver(staticReflector),
|
new compiler.NgModuleResolver(staticReflector),
|
||||||
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
||||||
elementSchemaRegistry, staticReflector);
|
elementSchemaRegistry, normalizer, staticReflector);
|
||||||
|
|
||||||
// TODO(vicb): implicit tags & attributes
|
// TODO(vicb): implicit tags & attributes
|
||||||
let messageBundle = new compiler.MessageBundle(htmlParser, [], {});
|
let messageBundle = new compiler.MessageBundle(htmlParser, [], {});
|
||||||
|
|
||||||
return new Extractor(
|
return new Extractor(
|
||||||
options, program, compilerHost, staticReflector, messageBundle, reflectorHost, resolver,
|
options, program, compilerHost, staticReflector, messageBundle, reflectorHost, resolver);
|
||||||
normalizer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -326,9 +326,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
||||||
queries?: CompileQueryMetadata[],
|
queries?: CompileQueryMetadata[],
|
||||||
viewQueries?: CompileQueryMetadata[],
|
viewQueries?: CompileQueryMetadata[],
|
||||||
entryComponents?: CompileTypeMetadata[],
|
entryComponents?: CompileIdentifierMetadata[],
|
||||||
viewDirectives?: CompileTypeMetadata[],
|
|
||||||
viewPipes?: CompileTypeMetadata[],
|
|
||||||
template?: CompileTemplateMetadata
|
template?: CompileTemplateMetadata
|
||||||
} = {}): CompileDirectiveMetadata {
|
} = {}): CompileDirectiveMetadata {
|
||||||
var hostListeners: {[key: string]: string} = {};
|
var hostListeners: {[key: string]: string} = {};
|
||||||
|
@ -397,7 +395,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||||
queries: CompileQueryMetadata[];
|
queries: CompileQueryMetadata[];
|
||||||
viewQueries: CompileQueryMetadata[];
|
viewQueries: CompileQueryMetadata[];
|
||||||
// Note: Need to keep types here to prevent cycles!
|
// Note: Need to keep types here to prevent cycles!
|
||||||
entryComponents: CompileTypeMetadata[];
|
entryComponents: CompileIdentifierMetadata[];
|
||||||
|
|
||||||
template: CompileTemplateMetadata;
|
template: CompileTemplateMetadata;
|
||||||
|
|
||||||
|
@ -421,9 +419,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
||||||
queries?: CompileQueryMetadata[],
|
queries?: CompileQueryMetadata[],
|
||||||
viewQueries?: CompileQueryMetadata[],
|
viewQueries?: CompileQueryMetadata[],
|
||||||
entryComponents?: CompileTypeMetadata[],
|
entryComponents?: CompileIdentifierMetadata[],
|
||||||
viewDirectives?: CompileTypeMetadata[],
|
|
||||||
viewPipes?: CompileTypeMetadata[],
|
|
||||||
template?: CompileTemplateMetadata,
|
template?: CompileTemplateMetadata,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
@ -506,13 +502,13 @@ export class CompilePipeMetadata implements CompileMetadataWithIdentifier {
|
||||||
*/
|
*/
|
||||||
export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
||||||
type: CompileTypeMetadata;
|
type: CompileTypeMetadata;
|
||||||
declaredDirectives: CompileDirectiveMetadata[];
|
declaredDirectives: CompileIdentifierMetadata[];
|
||||||
exportedDirectives: CompileDirectiveMetadata[];
|
exportedDirectives: CompileIdentifierMetadata[];
|
||||||
declaredPipes: CompilePipeMetadata[];
|
declaredPipes: CompileIdentifierMetadata[];
|
||||||
exportedPipes: CompilePipeMetadata[];
|
exportedPipes: CompileIdentifierMetadata[];
|
||||||
// Note: See CompileDirectiveMetadata.entryComponents why this has to be a type.
|
// Note: See CompileDirectiveMetadata.entryComponents why this has to be a type.
|
||||||
entryComponents: CompileTypeMetadata[];
|
entryComponents: CompileIdentifierMetadata[];
|
||||||
bootstrapComponents: CompileTypeMetadata[];
|
bootstrapComponents: CompileIdentifierMetadata[];
|
||||||
providers: CompileProviderMetadata[];
|
providers: CompileProviderMetadata[];
|
||||||
|
|
||||||
importedModules: CompileNgModuleMetadata[];
|
importedModules: CompileNgModuleMetadata[];
|
||||||
|
@ -529,12 +525,12 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
||||||
type?: CompileTypeMetadata,
|
type?: CompileTypeMetadata,
|
||||||
providers?:
|
providers?:
|
||||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
||||||
declaredDirectives?: CompileDirectiveMetadata[],
|
declaredDirectives?: CompileIdentifierMetadata[],
|
||||||
exportedDirectives?: CompileDirectiveMetadata[],
|
exportedDirectives?: CompileIdentifierMetadata[],
|
||||||
declaredPipes?: CompilePipeMetadata[],
|
declaredPipes?: CompileIdentifierMetadata[],
|
||||||
exportedPipes?: CompilePipeMetadata[],
|
exportedPipes?: CompileIdentifierMetadata[],
|
||||||
entryComponents?: CompileTypeMetadata[],
|
entryComponents?: CompileIdentifierMetadata[],
|
||||||
bootstrapComponents?: CompileTypeMetadata[],
|
bootstrapComponents?: CompileIdentifierMetadata[],
|
||||||
importedModules?: CompileNgModuleMetadata[],
|
importedModules?: CompileNgModuleMetadata[],
|
||||||
exportedModules?: CompileNgModuleMetadata[],
|
exportedModules?: CompileNgModuleMetadata[],
|
||||||
transitiveModule?: TransitiveCompileNgModuleMetadata,
|
transitiveModule?: TransitiveCompileNgModuleMetadata,
|
||||||
|
@ -560,15 +556,16 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TransitiveCompileNgModuleMetadata {
|
export class TransitiveCompileNgModuleMetadata {
|
||||||
directivesSet = new Set<Type<any>>();
|
directivesSet = new Set<any>();
|
||||||
pipesSet = new Set<Type<any>>();
|
pipesSet = new Set<any>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public modules: CompileNgModuleMetadata[], public providers: CompileProviderMetadata[],
|
public modules: CompileNgModuleMetadata[], public providers: CompileProviderMetadata[],
|
||||||
public entryComponents: CompileTypeMetadata[], public directives: CompileDirectiveMetadata[],
|
public entryComponents: CompileIdentifierMetadata[],
|
||||||
public pipes: CompilePipeMetadata[]) {
|
public directives: CompileIdentifierMetadata[], public pipes: CompileIdentifierMetadata[],
|
||||||
directives.forEach(dir => this.directivesSet.add(dir.type.reference));
|
public loadingPromises: Promise<any>[]) {
|
||||||
pipes.forEach(pipe => this.pipesSet.add(pipe.type.reference));
|
directives.forEach(dir => this.directivesSet.add(dir.reference));
|
||||||
|
pipes.forEach(pipe => this.pipesSet.add(pipe.reference));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,11 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injectable, ViewEncapsulation} from '@angular/core';
|
import {Component, Injectable, ViewEncapsulation} from '@angular/core';
|
||||||
|
|
||||||
import {CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from './compile_metadata';
|
import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from './compile_metadata';
|
||||||
import {CompilerConfig} from './config';
|
import {CompilerConfig} from './config';
|
||||||
import {isBlank, isPresent} from './facade/lang';
|
import {isBlank, isPresent, stringify} from './facade/lang';
|
||||||
import * as html from './ml_parser/ast';
|
import * as html from './ml_parser/ast';
|
||||||
import {HtmlParser} from './ml_parser/html_parser';
|
import {HtmlParser} from './ml_parser/html_parser';
|
||||||
import {InterpolationConfig} from './ml_parser/interpolation_config';
|
import {InterpolationConfig} from './ml_parser/interpolation_config';
|
||||||
|
@ -20,6 +20,18 @@ import {PreparsedElementType, preparseElement} from './template_parser/template_
|
||||||
import {UrlResolver} from './url_resolver';
|
import {UrlResolver} from './url_resolver';
|
||||||
import {SyncAsyncResult} from './util';
|
import {SyncAsyncResult} from './util';
|
||||||
|
|
||||||
|
export interface PrenormalizedTemplateMetadata {
|
||||||
|
componentType: any;
|
||||||
|
moduleUrl: string;
|
||||||
|
template?: string;
|
||||||
|
templateUrl?: string;
|
||||||
|
styles?: string[];
|
||||||
|
styleUrls?: string[];
|
||||||
|
interpolation?: [string, string];
|
||||||
|
encapsulation?: ViewEncapsulation;
|
||||||
|
animations?: CompileAnimationEntryMetadata[];
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DirectiveNormalizer {
|
export class DirectiveNormalizer {
|
||||||
private _resourceLoaderCache = new Map<string, Promise<string>>();
|
private _resourceLoaderCache = new Map<string, Promise<string>>();
|
||||||
|
@ -48,65 +60,56 @@ export class DirectiveNormalizer {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeDirective(directive: CompileDirectiveMetadata):
|
normalizeTemplate(prenormData: PrenormalizedTemplateMetadata):
|
||||||
SyncAsyncResult<CompileDirectiveMetadata> {
|
SyncAsyncResult<CompileTemplateMetadata> {
|
||||||
if (!directive.isComponent) {
|
|
||||||
// For non components there is nothing to be normalized yet.
|
|
||||||
return new SyncAsyncResult(directive, Promise.resolve(directive));
|
|
||||||
}
|
|
||||||
let normalizedTemplateSync: CompileTemplateMetadata = null;
|
let normalizedTemplateSync: CompileTemplateMetadata = null;
|
||||||
let normalizedTemplateAsync: Promise<CompileTemplateMetadata>;
|
let normalizedTemplateAsync: Promise<CompileTemplateMetadata>;
|
||||||
if (isPresent(directive.template.template)) {
|
if (isPresent(prenormData.template)) {
|
||||||
normalizedTemplateSync = this.normalizeTemplateSync(directive.type, directive.template);
|
normalizedTemplateSync = this.normalizeTemplateSync(prenormData);
|
||||||
normalizedTemplateAsync = Promise.resolve(normalizedTemplateSync);
|
normalizedTemplateAsync = Promise.resolve(normalizedTemplateSync);
|
||||||
} else if (directive.template.templateUrl) {
|
} else if (prenormData.templateUrl) {
|
||||||
normalizedTemplateAsync = this.normalizeTemplateAsync(directive.type, directive.template);
|
normalizedTemplateAsync = this.normalizeTemplateAsync(prenormData);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`No template specified for component ${directive.type.name}`);
|
throw new Error(
|
||||||
|
`No template specified for component ${stringify(prenormData.componentType)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (normalizedTemplateSync && normalizedTemplateSync.styleUrls.length === 0) {
|
if (normalizedTemplateSync && normalizedTemplateSync.styleUrls.length === 0) {
|
||||||
// sync case
|
// sync case
|
||||||
let normalizedDirective = _cloneDirectiveWithTemplate(directive, normalizedTemplateSync);
|
return new SyncAsyncResult(normalizedTemplateSync);
|
||||||
return new SyncAsyncResult(normalizedDirective, Promise.resolve(normalizedDirective));
|
|
||||||
} else {
|
} else {
|
||||||
// async case
|
// async case
|
||||||
return new SyncAsyncResult(
|
return new SyncAsyncResult(
|
||||||
null,
|
null, normalizedTemplateAsync.then(
|
||||||
normalizedTemplateAsync
|
(normalizedTemplate) => this.normalizeExternalStylesheets(normalizedTemplate)));
|
||||||
.then((normalizedTemplate) => this.normalizeExternalStylesheets(normalizedTemplate))
|
|
||||||
.then(
|
|
||||||
(normalizedTemplate) =>
|
|
||||||
_cloneDirectiveWithTemplate(directive, normalizedTemplate)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeTemplateSync(directiveType: CompileTypeMetadata, template: CompileTemplateMetadata):
|
normalizeTemplateSync(prenomData: PrenormalizedTemplateMetadata): CompileTemplateMetadata {
|
||||||
CompileTemplateMetadata {
|
return this.normalizeLoadedTemplate(prenomData, prenomData.template, prenomData.moduleUrl);
|
||||||
return this.normalizeLoadedTemplate(
|
|
||||||
directiveType, template, template.template, directiveType.moduleUrl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeTemplateAsync(directiveType: CompileTypeMetadata, template: CompileTemplateMetadata):
|
normalizeTemplateAsync(prenomData: PrenormalizedTemplateMetadata):
|
||||||
Promise<CompileTemplateMetadata> {
|
Promise<CompileTemplateMetadata> {
|
||||||
let templateUrl = this._urlResolver.resolve(directiveType.moduleUrl, template.templateUrl);
|
let templateUrl = this._urlResolver.resolve(prenomData.moduleUrl, prenomData.templateUrl);
|
||||||
return this._fetch(templateUrl)
|
return this._fetch(templateUrl)
|
||||||
.then((value) => this.normalizeLoadedTemplate(directiveType, template, value, templateUrl));
|
.then((value) => this.normalizeLoadedTemplate(prenomData, value, templateUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeLoadedTemplate(
|
normalizeLoadedTemplate(
|
||||||
directiveType: CompileTypeMetadata, templateMeta: CompileTemplateMetadata, template: string,
|
prenomData: PrenormalizedTemplateMetadata, template: string,
|
||||||
templateAbsUrl: string): CompileTemplateMetadata {
|
templateAbsUrl: string): CompileTemplateMetadata {
|
||||||
const interpolationConfig = InterpolationConfig.fromArray(templateMeta.interpolation);
|
const interpolationConfig = InterpolationConfig.fromArray(prenomData.interpolation);
|
||||||
const rootNodesAndErrors =
|
const rootNodesAndErrors = this._htmlParser.parse(
|
||||||
this._htmlParser.parse(template, directiveType.name, false, interpolationConfig);
|
template, stringify(prenomData.componentType), false, interpolationConfig);
|
||||||
if (rootNodesAndErrors.errors.length > 0) {
|
if (rootNodesAndErrors.errors.length > 0) {
|
||||||
const errorString = rootNodesAndErrors.errors.join('\n');
|
const errorString = rootNodesAndErrors.errors.join('\n');
|
||||||
throw new Error(`Template parse errors:\n${errorString}`);
|
throw new Error(`Template parse errors:\n${errorString}`);
|
||||||
}
|
}
|
||||||
const templateMetadataStyles = this.normalizeStylesheet(new CompileStylesheetMetadata({
|
const templateMetadataStyles = this.normalizeStylesheet(new CompileStylesheetMetadata({
|
||||||
styles: templateMeta.styles,
|
styles: prenomData.styles,
|
||||||
styleUrls: templateMeta.styleUrls,
|
styleUrls: prenomData.styleUrls,
|
||||||
moduleUrl: directiveType.moduleUrl
|
moduleUrl: prenomData.moduleUrl
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const visitor = new TemplatePreparseVisitor();
|
const visitor = new TemplatePreparseVisitor();
|
||||||
|
@ -114,7 +117,7 @@ export class DirectiveNormalizer {
|
||||||
const templateStyles = this.normalizeStylesheet(new CompileStylesheetMetadata(
|
const templateStyles = this.normalizeStylesheet(new CompileStylesheetMetadata(
|
||||||
{styles: visitor.styles, styleUrls: visitor.styleUrls, moduleUrl: templateAbsUrl}));
|
{styles: visitor.styles, styleUrls: visitor.styleUrls, moduleUrl: templateAbsUrl}));
|
||||||
|
|
||||||
let encapsulation = templateMeta.encapsulation;
|
let encapsulation = prenomData.encapsulation;
|
||||||
if (isBlank(encapsulation)) {
|
if (isBlank(encapsulation)) {
|
||||||
encapsulation = this._config.defaultEncapsulation;
|
encapsulation = this._config.defaultEncapsulation;
|
||||||
}
|
}
|
||||||
|
@ -131,10 +134,9 @@ export class DirectiveNormalizer {
|
||||||
encapsulation,
|
encapsulation,
|
||||||
template,
|
template,
|
||||||
templateUrl: templateAbsUrl, styles, styleUrls,
|
templateUrl: templateAbsUrl, styles, styleUrls,
|
||||||
externalStylesheets: templateMeta.externalStylesheets,
|
|
||||||
ngContentSelectors: visitor.ngContentSelectors,
|
ngContentSelectors: visitor.ngContentSelectors,
|
||||||
animations: templateMeta.animations,
|
animations: prenomData.animations,
|
||||||
interpolation: templateMeta.interpolation,
|
interpolation: prenomData.interpolation,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,25 +233,3 @@ class TemplatePreparseVisitor implements html.Visitor {
|
||||||
visitExpansion(ast: html.Expansion, context: any): any { return null; }
|
visitExpansion(ast: html.Expansion, context: any): any { return null; }
|
||||||
visitExpansionCase(ast: html.ExpansionCase, context: any): any { return null; }
|
visitExpansionCase(ast: html.ExpansionCase, context: any): any { return null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
function _cloneDirectiveWithTemplate(
|
|
||||||
directive: CompileDirectiveMetadata,
|
|
||||||
template: CompileTemplateMetadata): CompileDirectiveMetadata {
|
|
||||||
return new CompileDirectiveMetadata({
|
|
||||||
type: directive.type,
|
|
||||||
isComponent: directive.isComponent,
|
|
||||||
selector: directive.selector,
|
|
||||||
exportAs: directive.exportAs,
|
|
||||||
changeDetection: directive.changeDetection,
|
|
||||||
inputs: directive.inputs,
|
|
||||||
outputs: directive.outputs,
|
|
||||||
hostListeners: directive.hostListeners,
|
|
||||||
hostProperties: directive.hostProperties,
|
|
||||||
hostAttributes: directive.hostAttributes,
|
|
||||||
providers: directive.providers,
|
|
||||||
viewProviders: directive.viewProviders,
|
|
||||||
queries: directive.queries,
|
|
||||||
viewQueries: directive.viewQueries,
|
|
||||||
entryComponents: directive.entryComponents, template,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -24,6 +24,11 @@ import {splitAtColon} from './util';
|
||||||
export class DirectiveResolver {
|
export class DirectiveResolver {
|
||||||
constructor(private _reflector: ReflectorReader = reflector) {}
|
constructor(private _reflector: ReflectorReader = reflector) {}
|
||||||
|
|
||||||
|
isDirective(type: Type<any>) {
|
||||||
|
const typeMetadata = this._reflector.annotations(resolveForwardRef(type));
|
||||||
|
return typeMetadata && typeMetadata.some(isDirectiveMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return {@link Directive} for a given `Type`.
|
* Return {@link Directive} for a given `Type`.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -10,17 +10,27 @@ import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata
|
||||||
|
|
||||||
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
|
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
|
||||||
import * as cpl from './compile_metadata';
|
import * as cpl from './compile_metadata';
|
||||||
|
import {DirectiveNormalizer} from './directive_normalizer';
|
||||||
import {DirectiveResolver} from './directive_resolver';
|
import {DirectiveResolver} from './directive_resolver';
|
||||||
|
import {ListWrapper} from './facade/collection';
|
||||||
import {isBlank, isPresent, stringify} from './facade/lang';
|
import {isBlank, isPresent, stringify} from './facade/lang';
|
||||||
import {Identifiers, resolveIdentifierToken} from './identifiers';
|
import {Identifiers, resolveIdentifierToken} from './identifiers';
|
||||||
import {hasLifecycleHook} from './lifecycle_reflector';
|
import {hasLifecycleHook} from './lifecycle_reflector';
|
||||||
import {NgModuleResolver} from './ng_module_resolver';
|
import {NgModuleResolver} from './ng_module_resolver';
|
||||||
import {PipeResolver} from './pipe_resolver';
|
import {PipeResolver} from './pipe_resolver';
|
||||||
import {LIFECYCLE_HOOKS_VALUES, ReflectorReader, reflector} from './private_import_core';
|
import {ComponentStillLoadingError, LIFECYCLE_HOOKS_VALUES, ReflectorReader, reflector} from './private_import_core';
|
||||||
import {ElementSchemaRegistry} from './schema/element_schema_registry';
|
import {ElementSchemaRegistry} from './schema/element_schema_registry';
|
||||||
import {getUrlScheme} from './url_resolver';
|
import {getUrlScheme} from './url_resolver';
|
||||||
import {MODULE_SUFFIX, ValueTransformer, sanitizeIdentifier, visitValue} from './util';
|
import {MODULE_SUFFIX, SyncAsyncResult, ValueTransformer, sanitizeIdentifier, visitValue} from './util';
|
||||||
|
|
||||||
|
|
||||||
|
// Design notes:
|
||||||
|
// - don't lazily create metadata:
|
||||||
|
// For some metadata, we need to do async work sometimes,
|
||||||
|
// so the user has to kick off this loading.
|
||||||
|
// But we want to report errors even when the async work is
|
||||||
|
// not required to check that the user would have been able
|
||||||
|
// to wait correctly.
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CompileMetadataResolver {
|
export class CompileMetadataResolver {
|
||||||
private _directiveCache = new Map<Type<any>, cpl.CompileDirectiveMetadata>();
|
private _directiveCache = new Map<Type<any>, cpl.CompileDirectiveMetadata>();
|
||||||
|
@ -33,6 +43,7 @@ export class CompileMetadataResolver {
|
||||||
constructor(
|
constructor(
|
||||||
private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver,
|
private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver,
|
||||||
private _pipeResolver: PipeResolver, private _schemaRegistry: ElementSchemaRegistry,
|
private _pipeResolver: PipeResolver, private _schemaRegistry: ElementSchemaRegistry,
|
||||||
|
private _directiveNormalizer: DirectiveNormalizer,
|
||||||
private _reflector: ReflectorReader = reflector) {}
|
private _reflector: ReflectorReader = reflector) {}
|
||||||
|
|
||||||
private sanitizeTokenName(token: any): string {
|
private sanitizeTokenName(token: any): string {
|
||||||
|
@ -50,11 +61,15 @@ export class CompileMetadataResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCacheFor(type: Type<any>) {
|
clearCacheFor(type: Type<any>) {
|
||||||
|
const dirMeta = this._directiveCache.get(type);
|
||||||
this._directiveCache.delete(type);
|
this._directiveCache.delete(type);
|
||||||
this._pipeCache.delete(type);
|
this._pipeCache.delete(type);
|
||||||
this._ngModuleOfTypes.delete(type);
|
this._ngModuleOfTypes.delete(type);
|
||||||
// Clear all of the NgModule as they contain transitive information!
|
// Clear all of the NgModule as they contain transitive information!
|
||||||
this._ngModuleCache.clear();
|
this._ngModuleCache.clear();
|
||||||
|
if (dirMeta) {
|
||||||
|
this._directiveNormalizer.clearCacheFor(dirMeta);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCache() {
|
clearCache() {
|
||||||
|
@ -62,50 +77,53 @@ export class CompileMetadataResolver {
|
||||||
this._pipeCache.clear();
|
this._pipeCache.clear();
|
||||||
this._ngModuleCache.clear();
|
this._ngModuleCache.clear();
|
||||||
this._ngModuleOfTypes.clear();
|
this._ngModuleOfTypes.clear();
|
||||||
|
this._directiveNormalizer.clearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
getAnimationEntryMetadata(entry: AnimationEntryMetadata): cpl.CompileAnimationEntryMetadata {
|
getAnimationEntryMetadata(entry: AnimationEntryMetadata): cpl.CompileAnimationEntryMetadata {
|
||||||
const defs = entry.definitions.map(def => this.getAnimationStateMetadata(def));
|
const defs = entry.definitions.map(def => this._getAnimationStateMetadata(def));
|
||||||
return new cpl.CompileAnimationEntryMetadata(entry.name, defs);
|
return new cpl.CompileAnimationEntryMetadata(entry.name, defs);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAnimationStateMetadata(value: AnimationStateMetadata): cpl.CompileAnimationStateMetadata {
|
private _getAnimationStateMetadata(value: AnimationStateMetadata):
|
||||||
|
cpl.CompileAnimationStateMetadata {
|
||||||
if (value instanceof AnimationStateDeclarationMetadata) {
|
if (value instanceof AnimationStateDeclarationMetadata) {
|
||||||
const styles = this.getAnimationStyleMetadata(value.styles);
|
const styles = this._getAnimationStyleMetadata(value.styles);
|
||||||
return new cpl.CompileAnimationStateDeclarationMetadata(value.stateNameExpr, styles);
|
return new cpl.CompileAnimationStateDeclarationMetadata(value.stateNameExpr, styles);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value instanceof AnimationStateTransitionMetadata) {
|
if (value instanceof AnimationStateTransitionMetadata) {
|
||||||
return new cpl.CompileAnimationStateTransitionMetadata(
|
return new cpl.CompileAnimationStateTransitionMetadata(
|
||||||
value.stateChangeExpr, this.getAnimationMetadata(value.steps));
|
value.stateChangeExpr, this._getAnimationMetadata(value.steps));
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAnimationStyleMetadata(value: AnimationStyleMetadata): cpl.CompileAnimationStyleMetadata {
|
private _getAnimationStyleMetadata(value: AnimationStyleMetadata):
|
||||||
|
cpl.CompileAnimationStyleMetadata {
|
||||||
return new cpl.CompileAnimationStyleMetadata(value.offset, value.styles);
|
return new cpl.CompileAnimationStyleMetadata(value.offset, value.styles);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAnimationMetadata(value: AnimationMetadata): cpl.CompileAnimationMetadata {
|
private _getAnimationMetadata(value: AnimationMetadata): cpl.CompileAnimationMetadata {
|
||||||
if (value instanceof AnimationStyleMetadata) {
|
if (value instanceof AnimationStyleMetadata) {
|
||||||
return this.getAnimationStyleMetadata(value);
|
return this._getAnimationStyleMetadata(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value instanceof AnimationKeyframesSequenceMetadata) {
|
if (value instanceof AnimationKeyframesSequenceMetadata) {
|
||||||
return new cpl.CompileAnimationKeyframesSequenceMetadata(
|
return new cpl.CompileAnimationKeyframesSequenceMetadata(
|
||||||
value.steps.map(entry => this.getAnimationStyleMetadata(entry)));
|
value.steps.map(entry => this._getAnimationStyleMetadata(entry)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value instanceof AnimationAnimateMetadata) {
|
if (value instanceof AnimationAnimateMetadata) {
|
||||||
const animateData =
|
const animateData =
|
||||||
<cpl.CompileAnimationStyleMetadata|cpl.CompileAnimationKeyframesSequenceMetadata>this
|
<cpl.CompileAnimationStyleMetadata|cpl.CompileAnimationKeyframesSequenceMetadata>this
|
||||||
.getAnimationMetadata(value.styles);
|
._getAnimationMetadata(value.styles);
|
||||||
return new cpl.CompileAnimationAnimateMetadata(value.timings, animateData);
|
return new cpl.CompileAnimationAnimateMetadata(value.timings, animateData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value instanceof AnimationWithStepsMetadata) {
|
if (value instanceof AnimationWithStepsMetadata) {
|
||||||
const steps = value.steps.map(step => this.getAnimationMetadata(step));
|
const steps = value.steps.map(step => this._getAnimationMetadata(step));
|
||||||
|
|
||||||
if (value instanceof AnimationGroupMetadata) {
|
if (value instanceof AnimationGroupMetadata) {
|
||||||
return new cpl.CompileAnimationGroupMetadata(steps);
|
return new cpl.CompileAnimationGroupMetadata(steps);
|
||||||
|
@ -116,52 +134,35 @@ export class CompileMetadataResolver {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDirectiveMetadata(directiveType: any, throwIfNotFound = true): cpl.CompileDirectiveMetadata {
|
private _loadDirectiveMetadata(directiveType: any, isSync: boolean): Promise<any> {
|
||||||
|
if (this._directiveCache.has(directiveType)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
directiveType = resolveForwardRef(directiveType);
|
directiveType = resolveForwardRef(directiveType);
|
||||||
let meta = this._directiveCache.get(directiveType);
|
const dirMeta = this._directiveResolver.resolve(directiveType);
|
||||||
if (!meta) {
|
|
||||||
const dirMeta = this._directiveResolver.resolve(directiveType, throwIfNotFound);
|
|
||||||
if (!dirMeta) {
|
if (!dirMeta) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let templateMeta: cpl.CompileTemplateMetadata = null;
|
let moduleUrl = staticTypeModuleUrl(directiveType);
|
||||||
|
|
||||||
|
const createDirectiveMetadata = (templateMeta: cpl.CompileTemplateMetadata) => {
|
||||||
let changeDetectionStrategy: ChangeDetectionStrategy = null;
|
let changeDetectionStrategy: ChangeDetectionStrategy = null;
|
||||||
let viewProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
|
let viewProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
|
||||||
let moduleUrl = staticTypeModuleUrl(directiveType);
|
let entryComponentMetadata: cpl.CompileIdentifierMetadata[] = [];
|
||||||
let entryComponentMetadata: cpl.CompileTypeMetadata[] = [];
|
|
||||||
let selector = dirMeta.selector;
|
let selector = dirMeta.selector;
|
||||||
|
|
||||||
if (dirMeta instanceof Component) {
|
if (dirMeta instanceof Component) {
|
||||||
// Component
|
// Component
|
||||||
assertArrayOfStrings('styles', dirMeta.styles);
|
|
||||||
assertArrayOfStrings('styleUrls', dirMeta.styleUrls);
|
|
||||||
assertInterpolationSymbols('interpolation', dirMeta.interpolation);
|
|
||||||
|
|
||||||
const animations = dirMeta.animations ?
|
|
||||||
dirMeta.animations.map(e => this.getAnimationEntryMetadata(e)) :
|
|
||||||
null;
|
|
||||||
|
|
||||||
templateMeta = new cpl.CompileTemplateMetadata({
|
|
||||||
encapsulation: dirMeta.encapsulation,
|
|
||||||
template: dirMeta.template,
|
|
||||||
templateUrl: dirMeta.templateUrl,
|
|
||||||
styles: dirMeta.styles,
|
|
||||||
styleUrls: dirMeta.styleUrls,
|
|
||||||
animations: animations,
|
|
||||||
interpolation: dirMeta.interpolation
|
|
||||||
});
|
|
||||||
|
|
||||||
changeDetectionStrategy = dirMeta.changeDetection;
|
changeDetectionStrategy = dirMeta.changeDetection;
|
||||||
if (dirMeta.viewProviders) {
|
if (dirMeta.viewProviders) {
|
||||||
viewProviders = this.getProvidersMetadata(
|
viewProviders = this._getProvidersMetadata(
|
||||||
dirMeta.viewProviders, entryComponentMetadata,
|
dirMeta.viewProviders, entryComponentMetadata,
|
||||||
`viewProviders for "${stringify(directiveType)}"`);
|
`viewProviders for "${stringify(directiveType)}"`);
|
||||||
}
|
}
|
||||||
moduleUrl = componentModuleUrl(this._reflector, directiveType, dirMeta);
|
|
||||||
if (dirMeta.entryComponents) {
|
if (dirMeta.entryComponents) {
|
||||||
entryComponentMetadata =
|
entryComponentMetadata =
|
||||||
flattenAndDedupeArray(dirMeta.entryComponents)
|
flattenAndDedupeArray(dirMeta.entryComponents)
|
||||||
.map((type) => this.getTypeMetadata(type, staticTypeModuleUrl(type)))
|
.map((type) => this._getIdentifierMetadata(type, staticTypeModuleUrl(type)))
|
||||||
.concat(entryComponentMetadata);
|
.concat(entryComponentMetadata);
|
||||||
}
|
}
|
||||||
if (!selector) {
|
if (!selector) {
|
||||||
|
@ -176,22 +177,22 @@ export class CompileMetadataResolver {
|
||||||
|
|
||||||
let providers: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
|
let providers: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
|
||||||
if (isPresent(dirMeta.providers)) {
|
if (isPresent(dirMeta.providers)) {
|
||||||
providers = this.getProvidersMetadata(
|
providers = this._getProvidersMetadata(
|
||||||
dirMeta.providers, entryComponentMetadata,
|
dirMeta.providers, entryComponentMetadata,
|
||||||
`providers for "${stringify(directiveType)}"`);
|
`providers for "${stringify(directiveType)}"`);
|
||||||
}
|
}
|
||||||
let queries: cpl.CompileQueryMetadata[] = [];
|
let queries: cpl.CompileQueryMetadata[] = [];
|
||||||
let viewQueries: cpl.CompileQueryMetadata[] = [];
|
let viewQueries: cpl.CompileQueryMetadata[] = [];
|
||||||
if (isPresent(dirMeta.queries)) {
|
if (isPresent(dirMeta.queries)) {
|
||||||
queries = this.getQueriesMetadata(dirMeta.queries, false, directiveType);
|
queries = this._getQueriesMetadata(dirMeta.queries, false, directiveType);
|
||||||
viewQueries = this.getQueriesMetadata(dirMeta.queries, true, directiveType);
|
viewQueries = this._getQueriesMetadata(dirMeta.queries, true, directiveType);
|
||||||
}
|
}
|
||||||
|
|
||||||
meta = cpl.CompileDirectiveMetadata.create({
|
const meta = cpl.CompileDirectiveMetadata.create({
|
||||||
selector: selector,
|
selector: selector,
|
||||||
exportAs: dirMeta.exportAs,
|
exportAs: dirMeta.exportAs,
|
||||||
isComponent: !!templateMeta,
|
isComponent: !!templateMeta,
|
||||||
type: this.getTypeMetadata(directiveType, moduleUrl),
|
type: this._getTypeMetadata(directiveType, moduleUrl),
|
||||||
template: templateMeta,
|
template: templateMeta,
|
||||||
changeDetection: changeDetectionStrategy,
|
changeDetection: changeDetectionStrategy,
|
||||||
inputs: dirMeta.inputs,
|
inputs: dirMeta.inputs,
|
||||||
|
@ -204,27 +205,110 @@ export class CompileMetadataResolver {
|
||||||
entryComponents: entryComponentMetadata
|
entryComponents: entryComponentMetadata
|
||||||
});
|
});
|
||||||
this._directiveCache.set(directiveType, meta);
|
this._directiveCache.set(directiveType, meta);
|
||||||
}
|
|
||||||
return meta;
|
return meta;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (dirMeta instanceof Component) {
|
||||||
|
// component
|
||||||
|
moduleUrl = componentModuleUrl(this._reflector, directiveType, dirMeta);
|
||||||
|
assertArrayOfStrings('styles', dirMeta.styles);
|
||||||
|
assertArrayOfStrings('styleUrls', dirMeta.styleUrls);
|
||||||
|
assertInterpolationSymbols('interpolation', dirMeta.interpolation);
|
||||||
|
|
||||||
|
const animations = dirMeta.animations ?
|
||||||
|
dirMeta.animations.map(e => this.getAnimationEntryMetadata(e)) :
|
||||||
|
null;
|
||||||
|
|
||||||
|
const templateMeta = this._directiveNormalizer.normalizeTemplate({
|
||||||
|
componentType: directiveType,
|
||||||
|
moduleUrl: moduleUrl,
|
||||||
|
encapsulation: dirMeta.encapsulation,
|
||||||
|
template: dirMeta.template,
|
||||||
|
templateUrl: dirMeta.templateUrl,
|
||||||
|
styles: dirMeta.styles,
|
||||||
|
styleUrls: dirMeta.styleUrls,
|
||||||
|
animations: animations,
|
||||||
|
interpolation: dirMeta.interpolation
|
||||||
|
});
|
||||||
|
if (templateMeta.syncResult) {
|
||||||
|
createDirectiveMetadata(templateMeta.syncResult);
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
if (isSync) {
|
||||||
|
throw new ComponentStillLoadingError(directiveType);
|
||||||
|
}
|
||||||
|
return templateMeta.asyncResult.then(createDirectiveMetadata);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// directive
|
||||||
|
createDirectiveMetadata(null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getNgModuleMetadata(moduleType: any, throwIfNotFound = true): cpl.CompileNgModuleMetadata {
|
/**
|
||||||
|
* Gets the metadata for the given directive.
|
||||||
|
* This assumes `loadNgModuleMetadata` has been called first.
|
||||||
|
*/
|
||||||
|
getDirectiveMetadata(directiveType: any): cpl.CompileDirectiveMetadata {
|
||||||
|
const dirMeta = this._directiveCache.get(directiveType);
|
||||||
|
if (!dirMeta) {
|
||||||
|
throw new Error(
|
||||||
|
`Illegal state: getDirectiveMetadata can only be called after loadNgModuleMetadata for a module that declares it. Directive ${stringify(directiveType)}.`);
|
||||||
|
}
|
||||||
|
return dirMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
isDirective(type: any) { return this._directiveResolver.isDirective(type); }
|
||||||
|
|
||||||
|
isPipe(type: any) { return this._pipeResolver.isPipe(type); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the metadata for the given module.
|
||||||
|
* This assumes `loadNgModuleMetadata` has been called first.
|
||||||
|
*/
|
||||||
|
getNgModuleMetadata(moduleType: any): cpl.CompileNgModuleMetadata {
|
||||||
|
const modMeta = this._ngModuleCache.get(moduleType);
|
||||||
|
if (!modMeta) {
|
||||||
|
throw new Error(
|
||||||
|
`Illegal state: getNgModuleMetadata can only be called after loadNgModuleMetadata. Module ${stringify(moduleType)}.`);
|
||||||
|
}
|
||||||
|
return modMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads an NgModule and all of its directives. This includes loading the exported directives of
|
||||||
|
* imported modules,
|
||||||
|
* but not private directives of imported modules.
|
||||||
|
*/
|
||||||
|
loadNgModuleMetadata(moduleType: any, isSync: boolean, throwIfNotFound = true):
|
||||||
|
{ngModule: cpl.CompileNgModuleMetadata, loading: Promise<any>} {
|
||||||
|
const ngModule = this._loadNgModuleMetadata(moduleType, isSync, throwIfNotFound);
|
||||||
|
const loading =
|
||||||
|
ngModule ? Promise.all(ngModule.transitiveModule.loadingPromises) : Promise.resolve(null);
|
||||||
|
return {ngModule, loading};
|
||||||
|
}
|
||||||
|
|
||||||
|
private _loadNgModuleMetadata(moduleType: any, isSync: boolean, throwIfNotFound = true):
|
||||||
|
cpl.CompileNgModuleMetadata {
|
||||||
moduleType = resolveForwardRef(moduleType);
|
moduleType = resolveForwardRef(moduleType);
|
||||||
let compileMeta = this._ngModuleCache.get(moduleType);
|
let compileMeta = this._ngModuleCache.get(moduleType);
|
||||||
if (!compileMeta) {
|
if (compileMeta) {
|
||||||
|
return compileMeta;
|
||||||
|
}
|
||||||
const meta = this._ngModuleResolver.resolve(moduleType, throwIfNotFound);
|
const meta = this._ngModuleResolver.resolve(moduleType, throwIfNotFound);
|
||||||
if (!meta) {
|
if (!meta) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const declaredDirectives: cpl.CompileDirectiveMetadata[] = [];
|
const declaredDirectives: cpl.CompileIdentifierMetadata[] = [];
|
||||||
const exportedDirectives: cpl.CompileDirectiveMetadata[] = [];
|
const exportedDirectives: cpl.CompileIdentifierMetadata[] = [];
|
||||||
const declaredPipes: cpl.CompilePipeMetadata[] = [];
|
const declaredPipes: cpl.CompileIdentifierMetadata[] = [];
|
||||||
const exportedPipes: cpl.CompilePipeMetadata[] = [];
|
const exportedPipes: cpl.CompileIdentifierMetadata[] = [];
|
||||||
const importedModules: cpl.CompileNgModuleMetadata[] = [];
|
const importedModules: cpl.CompileNgModuleMetadata[] = [];
|
||||||
const exportedModules: cpl.CompileNgModuleMetadata[] = [];
|
const exportedModules: cpl.CompileNgModuleMetadata[] = [];
|
||||||
const providers: any[] = [];
|
const providers: any[] = [];
|
||||||
const entryComponents: cpl.CompileTypeMetadata[] = [];
|
const entryComponents: cpl.CompileIdentifierMetadata[] = [];
|
||||||
const bootstrapComponents: cpl.CompileTypeMetadata[] = [];
|
const bootstrapComponents: cpl.CompileIdentifierMetadata[] = [];
|
||||||
const schemas: SchemaMetadata[] = [];
|
const schemas: SchemaMetadata[] = [];
|
||||||
|
|
||||||
if (meta.imports) {
|
if (meta.imports) {
|
||||||
|
@ -236,14 +320,14 @@ export class CompileMetadataResolver {
|
||||||
const moduleWithProviders: ModuleWithProviders = importedType;
|
const moduleWithProviders: ModuleWithProviders = importedType;
|
||||||
importedModuleType = moduleWithProviders.ngModule;
|
importedModuleType = moduleWithProviders.ngModule;
|
||||||
if (moduleWithProviders.providers) {
|
if (moduleWithProviders.providers) {
|
||||||
providers.push(...this.getProvidersMetadata(
|
providers.push(...this._getProvidersMetadata(
|
||||||
moduleWithProviders.providers, entryComponents,
|
moduleWithProviders.providers, entryComponents,
|
||||||
`provider for the NgModule '${stringify(importedModuleType)}'`));
|
`provider for the NgModule '${stringify(importedModuleType)}'`));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (importedModuleType) {
|
if (importedModuleType) {
|
||||||
const importedMeta = this.getNgModuleMetadata(importedModuleType, false);
|
const importedMeta = this._loadNgModuleMetadata(importedModuleType, isSync, false);
|
||||||
if (importedMeta === null) {
|
if (importedMeta === null) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unexpected ${this._getTypeDescriptor(importedType)} '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`);
|
`Unexpected ${this._getTypeDescriptor(importedType)} '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`);
|
||||||
|
@ -262,14 +346,14 @@ export class CompileMetadataResolver {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unexpected value '${stringify(exportedType)}' exported by the module '${stringify(moduleType)}'`);
|
`Unexpected value '${stringify(exportedType)}' exported by the module '${stringify(moduleType)}'`);
|
||||||
}
|
}
|
||||||
let exportedDirMeta: cpl.CompileDirectiveMetadata;
|
let identifier =
|
||||||
let exportedPipeMeta: cpl.CompilePipeMetadata;
|
this._getIdentifierMetadata(exportedType, staticTypeModuleUrl(exportedType));
|
||||||
let exportedModuleMeta: cpl.CompileNgModuleMetadata;
|
let exportedModuleMeta: cpl.CompileNgModuleMetadata;
|
||||||
if (exportedDirMeta = this.getDirectiveMetadata(exportedType, false)) {
|
if (this._directiveResolver.isDirective(exportedType)) {
|
||||||
exportedDirectives.push(exportedDirMeta);
|
exportedDirectives.push(identifier);
|
||||||
} else if (exportedPipeMeta = this.getPipeMetadata(exportedType, false)) {
|
} else if (this._pipeResolver.isPipe(exportedType)) {
|
||||||
exportedPipes.push(exportedPipeMeta);
|
exportedPipes.push(identifier);
|
||||||
} else if (exportedModuleMeta = this.getNgModuleMetadata(exportedType, false)) {
|
} else if (exportedModuleMeta = this._loadNgModuleMetadata(exportedType, isSync, false)) {
|
||||||
exportedModules.push(exportedModuleMeta);
|
exportedModules.push(exportedModuleMeta);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -280,22 +364,30 @@ export class CompileMetadataResolver {
|
||||||
|
|
||||||
// Note: This will be modified later, so we rely on
|
// Note: This will be modified later, so we rely on
|
||||||
// getting a new instance every time!
|
// getting a new instance every time!
|
||||||
const transitiveModule =
|
const transitiveModule = this._getTransitiveNgModuleMetadata(importedModules, exportedModules);
|
||||||
this._getTransitiveNgModuleMetadata(importedModules, exportedModules);
|
|
||||||
if (meta.declarations) {
|
if (meta.declarations) {
|
||||||
flattenAndDedupeArray(meta.declarations).forEach((declaredType) => {
|
flattenAndDedupeArray(meta.declarations).forEach((declaredType) => {
|
||||||
if (!isValidType(declaredType)) {
|
if (!isValidType(declaredType)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unexpected value '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`);
|
`Unexpected value '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`);
|
||||||
}
|
}
|
||||||
let declaredDirMeta: cpl.CompileDirectiveMetadata;
|
const declaredIdentifier =
|
||||||
let declaredPipeMeta: cpl.CompilePipeMetadata;
|
this._getIdentifierMetadata(declaredType, staticTypeModuleUrl(declaredType));
|
||||||
if (declaredDirMeta = this.getDirectiveMetadata(declaredType, false)) {
|
if (this._directiveResolver.isDirective(declaredType)) {
|
||||||
this._addDirectiveToModule(
|
transitiveModule.directivesSet.add(declaredType);
|
||||||
declaredDirMeta, moduleType, transitiveModule, declaredDirectives, true);
|
transitiveModule.directives.push(declaredIdentifier);
|
||||||
} else if (declaredPipeMeta = this.getPipeMetadata(declaredType, false)) {
|
declaredDirectives.push(declaredIdentifier);
|
||||||
this._addPipeToModule(
|
this._addTypeToModule(declaredType, moduleType);
|
||||||
declaredPipeMeta, moduleType, transitiveModule, declaredPipes, true);
|
const loadingPromise = this._loadDirectiveMetadata(declaredType, isSync);
|
||||||
|
if (loadingPromise) {
|
||||||
|
transitiveModule.loadingPromises.push(loadingPromise);
|
||||||
|
}
|
||||||
|
} else if (this._pipeResolver.isPipe(declaredType)) {
|
||||||
|
transitiveModule.pipesSet.add(declaredType);
|
||||||
|
transitiveModule.pipes.push(declaredIdentifier);
|
||||||
|
declaredPipes.push(declaredIdentifier);
|
||||||
|
this._addTypeToModule(declaredType, moduleType);
|
||||||
|
this._loadPipeMetadata(declaredType);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unexpected ${this._getTypeDescriptor(declaredType)} '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`);
|
`Unexpected ${this._getTypeDescriptor(declaredType)} '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`);
|
||||||
|
@ -306,15 +398,14 @@ export class CompileMetadataResolver {
|
||||||
// The providers of the module have to go last
|
// The providers of the module have to go last
|
||||||
// so that they overwrite any other provider we already added.
|
// so that they overwrite any other provider we already added.
|
||||||
if (meta.providers) {
|
if (meta.providers) {
|
||||||
providers.push(...this.getProvidersMetadata(
|
providers.push(...this._getProvidersMetadata(
|
||||||
meta.providers, entryComponents,
|
meta.providers, entryComponents, `provider for the NgModule '${stringify(moduleType)}'`));
|
||||||
`provider for the NgModule '${stringify(moduleType)}'`));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.entryComponents) {
|
if (meta.entryComponents) {
|
||||||
entryComponents.push(
|
entryComponents.push(
|
||||||
...flattenAndDedupeArray(meta.entryComponents)
|
...flattenAndDedupeArray(meta.entryComponents)
|
||||||
.map(type => this.getTypeMetadata(type, staticTypeModuleUrl(type))));
|
.map(type => this._getTypeMetadata(type, staticTypeModuleUrl(type))));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.bootstrap) {
|
if (meta.bootstrap) {
|
||||||
|
@ -323,7 +414,7 @@ export class CompileMetadataResolver {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unexpected value '${stringify(type)}' used in the bootstrap property of module '${stringify(moduleType)}'`);
|
`Unexpected value '${stringify(type)}' used in the bootstrap property of module '${stringify(moduleType)}'`);
|
||||||
}
|
}
|
||||||
return this.getTypeMetadata(type, staticTypeModuleUrl(type));
|
return this._getTypeMetadata(type, staticTypeModuleUrl(type));
|
||||||
});
|
});
|
||||||
bootstrapComponents.push(...typeMetadata);
|
bootstrapComponents.push(...typeMetadata);
|
||||||
}
|
}
|
||||||
|
@ -338,7 +429,7 @@ export class CompileMetadataResolver {
|
||||||
transitiveModule.providers.push(...providers);
|
transitiveModule.providers.push(...providers);
|
||||||
|
|
||||||
compileMeta = new cpl.CompileNgModuleMetadata({
|
compileMeta = new cpl.CompileNgModuleMetadata({
|
||||||
type: this.getTypeMetadata(moduleType, staticTypeModuleUrl(moduleType)),
|
type: this._getTypeMetadata(moduleType, staticTypeModuleUrl(moduleType)),
|
||||||
providers,
|
providers,
|
||||||
entryComponents,
|
entryComponents,
|
||||||
bootstrapComponents,
|
bootstrapComponents,
|
||||||
|
@ -356,37 +447,36 @@ export class CompileMetadataResolver {
|
||||||
transitiveModule.modules.push(compileMeta);
|
transitiveModule.modules.push(compileMeta);
|
||||||
this._verifyModule(compileMeta);
|
this._verifyModule(compileMeta);
|
||||||
this._ngModuleCache.set(moduleType, compileMeta);
|
this._ngModuleCache.set(moduleType, compileMeta);
|
||||||
}
|
|
||||||
return compileMeta;
|
return compileMeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private _verifyModule(moduleMeta: cpl.CompileNgModuleMetadata) {
|
private _verifyModule(moduleMeta: cpl.CompileNgModuleMetadata) {
|
||||||
moduleMeta.exportedDirectives.forEach((dirMeta) => {
|
moduleMeta.exportedDirectives.forEach((dirIdentifier) => {
|
||||||
if (!moduleMeta.transitiveModule.directivesSet.has(dirMeta.type.reference)) {
|
if (!moduleMeta.transitiveModule.directivesSet.has(dirIdentifier.reference)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Can't export directive ${stringify(dirMeta.type.reference)} from ${stringify(moduleMeta.type.reference)} as it was neither declared nor imported!`);
|
`Can't export directive ${stringify(dirIdentifier.reference)} from ${stringify(moduleMeta.type.reference)} as it was neither declared nor imported!`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
moduleMeta.exportedPipes.forEach((pipeMeta) => {
|
moduleMeta.exportedPipes.forEach((pipeIdentifier) => {
|
||||||
if (!moduleMeta.transitiveModule.pipesSet.has(pipeMeta.type.reference)) {
|
if (!moduleMeta.transitiveModule.pipesSet.has(pipeIdentifier.reference)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Can't export pipe ${stringify(pipeMeta.type.reference)} from ${stringify(moduleMeta.type.reference)} as it was neither declared nor imported!`);
|
`Can't export pipe ${stringify(pipeIdentifier.reference)} from ${stringify(moduleMeta.type.reference)} as it was neither declared nor imported!`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getTypeDescriptor(type: Type<any>): string {
|
private _getTypeDescriptor(type: Type<any>): string {
|
||||||
if (this._directiveResolver.resolve(type, false)) {
|
if (this._directiveResolver.isDirective(type)) {
|
||||||
return 'directive';
|
return 'directive';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._pipeResolver.resolve(type, false)) {
|
if (this._pipeResolver.isPipe(type)) {
|
||||||
return 'pipe';
|
return 'pipe';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._ngModuleResolver.resolve(type, false)) {
|
if (this._ngModuleResolver.isNgModule(type)) {
|
||||||
return 'module';
|
return 'module';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,6 +487,7 @@ export class CompileMetadataResolver {
|
||||||
return 'value';
|
return 'value';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private _addTypeToModule(type: Type<any>, moduleType: Type<any>) {
|
private _addTypeToModule(type: Type<any>, moduleType: Type<any>) {
|
||||||
const oldModule = this._ngModuleOfTypes.get(type);
|
const oldModule = this._ngModuleOfTypes.get(type);
|
||||||
if (oldModule && oldModule !== moduleType) {
|
if (oldModule && oldModule !== moduleType) {
|
||||||
|
@ -421,81 +512,72 @@ export class CompileMetadataResolver {
|
||||||
const directives =
|
const directives =
|
||||||
flattenArray(transitiveExportedModules.map((ngModule) => ngModule.exportedDirectives));
|
flattenArray(transitiveExportedModules.map((ngModule) => ngModule.exportedDirectives));
|
||||||
const pipes = flattenArray(transitiveExportedModules.map((ngModule) => ngModule.exportedPipes));
|
const pipes = flattenArray(transitiveExportedModules.map((ngModule) => ngModule.exportedPipes));
|
||||||
|
const loadingPromises = ListWrapper.flatten(
|
||||||
|
transitiveExportedModules.map(ngModule => ngModule.transitiveModule.loadingPromises));
|
||||||
return new cpl.TransitiveCompileNgModuleMetadata(
|
return new cpl.TransitiveCompileNgModuleMetadata(
|
||||||
transitiveModules, providers, entryComponents, directives, pipes);
|
transitiveModules, providers, entryComponents, directives, pipes, loadingPromises);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addDirectiveToModule(
|
private _getIdentifierMetadata(type: Type<any>, moduleUrl: string):
|
||||||
dirMeta: cpl.CompileDirectiveMetadata, moduleType: any,
|
cpl.CompileIdentifierMetadata {
|
||||||
transitiveModule: cpl.TransitiveCompileNgModuleMetadata,
|
|
||||||
declaredDirectives: cpl.CompileDirectiveMetadata[], force: boolean = false): boolean {
|
|
||||||
if (force || !transitiveModule.directivesSet.has(dirMeta.type.reference)) {
|
|
||||||
transitiveModule.directivesSet.add(dirMeta.type.reference);
|
|
||||||
transitiveModule.directives.push(dirMeta);
|
|
||||||
declaredDirectives.push(dirMeta);
|
|
||||||
this._addTypeToModule(dirMeta.type.reference, moduleType);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _addPipeToModule(
|
|
||||||
pipeMeta: cpl.CompilePipeMetadata, moduleType: any,
|
|
||||||
transitiveModule: cpl.TransitiveCompileNgModuleMetadata,
|
|
||||||
declaredPipes: cpl.CompilePipeMetadata[], force: boolean = false): boolean {
|
|
||||||
if (force || !transitiveModule.pipesSet.has(pipeMeta.type.reference)) {
|
|
||||||
transitiveModule.pipesSet.add(pipeMeta.type.reference);
|
|
||||||
transitiveModule.pipes.push(pipeMeta);
|
|
||||||
declaredPipes.push(pipeMeta);
|
|
||||||
this._addTypeToModule(pipeMeta.type.reference, moduleType);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
getTypeMetadata(type: Type<any>, moduleUrl: string, dependencies: any[] = null):
|
|
||||||
cpl.CompileTypeMetadata {
|
|
||||||
type = resolveForwardRef(type);
|
type = resolveForwardRef(type);
|
||||||
|
return new cpl.CompileIdentifierMetadata(
|
||||||
|
{name: this.sanitizeTokenName(type), moduleUrl, reference: type});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getTypeMetadata(type: Type<any>, moduleUrl: string, dependencies: any[] = null):
|
||||||
|
cpl.CompileTypeMetadata {
|
||||||
|
const identifier = this._getIdentifierMetadata(type, moduleUrl);
|
||||||
return new cpl.CompileTypeMetadata({
|
return new cpl.CompileTypeMetadata({
|
||||||
name: this.sanitizeTokenName(type),
|
name: identifier.name,
|
||||||
moduleUrl,
|
moduleUrl: identifier.moduleUrl,
|
||||||
reference: type,
|
reference: identifier.reference,
|
||||||
diDeps: this.getDependenciesMetadata(type, dependencies),
|
diDeps: this._getDependenciesMetadata(identifier.reference, dependencies),
|
||||||
lifecycleHooks: LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, type)),
|
lifecycleHooks:
|
||||||
|
LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, identifier.reference)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getFactoryMetadata(factory: Function, moduleUrl: string, dependencies: any[] = null):
|
private _getFactoryMetadata(factory: Function, moduleUrl: string, dependencies: any[] = null):
|
||||||
cpl.CompileFactoryMetadata {
|
cpl.CompileFactoryMetadata {
|
||||||
factory = resolveForwardRef(factory);
|
factory = resolveForwardRef(factory);
|
||||||
return new cpl.CompileFactoryMetadata({
|
return new cpl.CompileFactoryMetadata({
|
||||||
name: this.sanitizeTokenName(factory),
|
name: this.sanitizeTokenName(factory),
|
||||||
moduleUrl,
|
moduleUrl,
|
||||||
reference: factory,
|
reference: factory,
|
||||||
diDeps: this.getDependenciesMetadata(factory, dependencies)
|
diDeps: this._getDependenciesMetadata(factory, dependencies)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getPipeMetadata(pipeType: Type<any>, throwIfNotFound = true): cpl.CompilePipeMetadata {
|
/**
|
||||||
|
* Gets the metadata for the given pipe.
|
||||||
|
* This assumes `loadNgModuleMetadata` has been called first.
|
||||||
|
*/
|
||||||
|
getPipeMetadata(pipeType: any): cpl.CompilePipeMetadata {
|
||||||
|
const pipeMeta = this._pipeCache.get(pipeType);
|
||||||
|
if (!pipeMeta) {
|
||||||
|
throw new Error(
|
||||||
|
`Illegal state: getPipeMetadata can only be called after loadNgModuleMetadata for a module that declares it. Pipe ${stringify(pipeType)}.`);
|
||||||
|
}
|
||||||
|
return pipeMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _loadPipeMetadata(pipeType: Type<any>): void {
|
||||||
pipeType = resolveForwardRef(pipeType);
|
pipeType = resolveForwardRef(pipeType);
|
||||||
let meta = this._pipeCache.get(pipeType);
|
const pipeMeta = this._pipeResolver.resolve(pipeType);
|
||||||
if (!meta) {
|
|
||||||
const pipeMeta = this._pipeResolver.resolve(pipeType, throwIfNotFound);
|
|
||||||
if (!pipeMeta) {
|
if (!pipeMeta) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
meta = new cpl.CompilePipeMetadata({
|
const meta = new cpl.CompilePipeMetadata({
|
||||||
type: this.getTypeMetadata(pipeType, staticTypeModuleUrl(pipeType)),
|
type: this._getTypeMetadata(pipeType, staticTypeModuleUrl(pipeType)),
|
||||||
name: pipeMeta.name,
|
name: pipeMeta.name,
|
||||||
pure: pipeMeta.pure
|
pure: pipeMeta.pure
|
||||||
});
|
});
|
||||||
this._pipeCache.set(pipeType, meta);
|
this._pipeCache.set(pipeType, meta);
|
||||||
}
|
}
|
||||||
return meta;
|
|
||||||
}
|
|
||||||
|
|
||||||
getDependenciesMetadata(typeOrFunc: Type<any>|Function, dependencies: any[]):
|
private _getDependenciesMetadata(typeOrFunc: Type<any>|Function, dependencies: any[]):
|
||||||
cpl.CompileDiDependencyMetadata[] {
|
cpl.CompileDiDependencyMetadata[] {
|
||||||
let hasUnknownDeps = false;
|
let hasUnknownDeps = false;
|
||||||
let params = dependencies || this._reflector.parameters(typeOrFunc) || [];
|
let params = dependencies || this._reflector.parameters(typeOrFunc) || [];
|
||||||
|
@ -540,7 +622,7 @@ export class CompileMetadataResolver {
|
||||||
isSelf,
|
isSelf,
|
||||||
isSkipSelf,
|
isSkipSelf,
|
||||||
isOptional,
|
isOptional,
|
||||||
token: this.getTokenMetadata(token)
|
token: this._getTokenMetadata(token)
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -555,7 +637,7 @@ export class CompileMetadataResolver {
|
||||||
return dependenciesMetadata;
|
return dependenciesMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
getTokenMetadata(token: any): cpl.CompileTokenMetadata {
|
private _getTokenMetadata(token: any): cpl.CompileTokenMetadata {
|
||||||
token = resolveForwardRef(token);
|
token = resolveForwardRef(token);
|
||||||
let compileToken: cpl.CompileTokenMetadata;
|
let compileToken: cpl.CompileTokenMetadata;
|
||||||
if (typeof token === 'string') {
|
if (typeof token === 'string') {
|
||||||
|
@ -572,8 +654,8 @@ export class CompileMetadataResolver {
|
||||||
return compileToken;
|
return compileToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
getProvidersMetadata(
|
private _getProvidersMetadata(
|
||||||
providers: Provider[], targetEntryComponents: cpl.CompileTypeMetadata[],
|
providers: Provider[], targetEntryComponents: cpl.CompileIdentifierMetadata[],
|
||||||
debugInfo?: string): Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> {
|
debugInfo?: string): Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> {
|
||||||
const compileProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
|
const compileProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
|
||||||
providers.forEach((provider: any, providerIdx: number) => {
|
providers.forEach((provider: any, providerIdx: number) => {
|
||||||
|
@ -583,9 +665,9 @@ export class CompileMetadataResolver {
|
||||||
}
|
}
|
||||||
let compileProvider: cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[];
|
let compileProvider: cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[];
|
||||||
if (Array.isArray(provider)) {
|
if (Array.isArray(provider)) {
|
||||||
compileProvider = this.getProvidersMetadata(provider, targetEntryComponents, debugInfo);
|
compileProvider = this._getProvidersMetadata(provider, targetEntryComponents, debugInfo);
|
||||||
} else if (provider instanceof cpl.ProviderMeta) {
|
} else if (provider instanceof cpl.ProviderMeta) {
|
||||||
let tokenMeta = this.getTokenMetadata(provider.token);
|
let tokenMeta = this._getTokenMetadata(provider.token);
|
||||||
if (tokenMeta.reference ===
|
if (tokenMeta.reference ===
|
||||||
resolveIdentifierToken(Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS).reference) {
|
resolveIdentifierToken(Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS).reference) {
|
||||||
targetEntryComponents.push(...this._getEntryComponentsFromProvider(provider));
|
targetEntryComponents.push(...this._getEntryComponentsFromProvider(provider));
|
||||||
|
@ -593,7 +675,7 @@ export class CompileMetadataResolver {
|
||||||
compileProvider = this.getProviderMetadata(provider);
|
compileProvider = this.getProviderMetadata(provider);
|
||||||
}
|
}
|
||||||
} else if (isValidType(provider)) {
|
} else if (isValidType(provider)) {
|
||||||
compileProvider = this.getTypeMetadata(provider, staticTypeModuleUrl(provider));
|
compileProvider = this._getTypeMetadata(provider, staticTypeModuleUrl(provider));
|
||||||
} else {
|
} else {
|
||||||
const providersInfo =
|
const providersInfo =
|
||||||
(<string[]>providers.reduce(
|
(<string[]>providers.reduce(
|
||||||
|
@ -620,8 +702,9 @@ export class CompileMetadataResolver {
|
||||||
return compileProviders;
|
return compileProviders;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta): cpl.CompileTypeMetadata[] {
|
private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta):
|
||||||
const components: cpl.CompileTypeMetadata[] = [];
|
cpl.CompileIdentifierMetadata[] {
|
||||||
|
const components: cpl.CompileIdentifierMetadata[] = [];
|
||||||
const collectedIdentifiers: cpl.CompileIdentifierMetadata[] = [];
|
const collectedIdentifiers: cpl.CompileIdentifierMetadata[] = [];
|
||||||
|
|
||||||
if (provider.useFactory || provider.useExisting || provider.useClass) {
|
if (provider.useFactory || provider.useExisting || provider.useClass) {
|
||||||
|
@ -634,9 +717,8 @@ export class CompileMetadataResolver {
|
||||||
|
|
||||||
convertToCompileValue(provider.useValue, collectedIdentifiers);
|
convertToCompileValue(provider.useValue, collectedIdentifiers);
|
||||||
collectedIdentifiers.forEach((identifier) => {
|
collectedIdentifiers.forEach((identifier) => {
|
||||||
const dirMeta = this.getDirectiveMetadata(identifier.reference, false);
|
if (this._directiveResolver.isDirective(identifier.reference)) {
|
||||||
if (dirMeta) {
|
components.push(identifier);
|
||||||
components.push(dirMeta.type);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return components;
|
return components;
|
||||||
|
@ -648,27 +730,27 @@ export class CompileMetadataResolver {
|
||||||
let compileFactoryMetadata: cpl.CompileFactoryMetadata = null;
|
let compileFactoryMetadata: cpl.CompileFactoryMetadata = null;
|
||||||
|
|
||||||
if (provider.useClass) {
|
if (provider.useClass) {
|
||||||
compileTypeMetadata = this.getTypeMetadata(
|
compileTypeMetadata = this._getTypeMetadata(
|
||||||
provider.useClass, staticTypeModuleUrl(provider.useClass), provider.dependencies);
|
provider.useClass, staticTypeModuleUrl(provider.useClass), provider.dependencies);
|
||||||
compileDeps = compileTypeMetadata.diDeps;
|
compileDeps = compileTypeMetadata.diDeps;
|
||||||
} else if (provider.useFactory) {
|
} else if (provider.useFactory) {
|
||||||
compileFactoryMetadata = this.getFactoryMetadata(
|
compileFactoryMetadata = this._getFactoryMetadata(
|
||||||
provider.useFactory, staticTypeModuleUrl(provider.useFactory), provider.dependencies);
|
provider.useFactory, staticTypeModuleUrl(provider.useFactory), provider.dependencies);
|
||||||
compileDeps = compileFactoryMetadata.diDeps;
|
compileDeps = compileFactoryMetadata.diDeps;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new cpl.CompileProviderMetadata({
|
return new cpl.CompileProviderMetadata({
|
||||||
token: this.getTokenMetadata(provider.token),
|
token: this._getTokenMetadata(provider.token),
|
||||||
useClass: compileTypeMetadata,
|
useClass: compileTypeMetadata,
|
||||||
useValue: convertToCompileValue(provider.useValue, []),
|
useValue: convertToCompileValue(provider.useValue, []),
|
||||||
useFactory: compileFactoryMetadata,
|
useFactory: compileFactoryMetadata,
|
||||||
useExisting: provider.useExisting ? this.getTokenMetadata(provider.useExisting) : null,
|
useExisting: provider.useExisting ? this._getTokenMetadata(provider.useExisting) : null,
|
||||||
deps: compileDeps,
|
deps: compileDeps,
|
||||||
multi: provider.multi
|
multi: provider.multi
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getQueriesMetadata(
|
private _getQueriesMetadata(
|
||||||
queries: {[key: string]: Query}, isViewQuery: boolean,
|
queries: {[key: string]: Query}, isViewQuery: boolean,
|
||||||
directiveType: Type<any>): cpl.CompileQueryMetadata[] {
|
directiveType: Type<any>): cpl.CompileQueryMetadata[] {
|
||||||
const res: cpl.CompileQueryMetadata[] = [];
|
const res: cpl.CompileQueryMetadata[] = [];
|
||||||
|
@ -676,7 +758,7 @@ export class CompileMetadataResolver {
|
||||||
Object.keys(queries).forEach((propertyName: string) => {
|
Object.keys(queries).forEach((propertyName: string) => {
|
||||||
const query = queries[propertyName];
|
const query = queries[propertyName];
|
||||||
if (query.isViewQuery === isViewQuery) {
|
if (query.isViewQuery === isViewQuery) {
|
||||||
res.push(this.getQueryMetadata(query, propertyName, directiveType));
|
res.push(this._getQueryMetadata(query, propertyName, directiveType));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -685,24 +767,25 @@ export class CompileMetadataResolver {
|
||||||
|
|
||||||
private _queryVarBindings(selector: any): string[] { return selector.split(/\s*,\s*/); }
|
private _queryVarBindings(selector: any): string[] { return selector.split(/\s*,\s*/); }
|
||||||
|
|
||||||
getQueryMetadata(q: Query, propertyName: string, typeOrFunc: Type<any>|Function):
|
private _getQueryMetadata(q: Query, propertyName: string, typeOrFunc: Type<any>|Function):
|
||||||
cpl.CompileQueryMetadata {
|
cpl.CompileQueryMetadata {
|
||||||
var selectors: cpl.CompileTokenMetadata[];
|
var selectors: cpl.CompileTokenMetadata[];
|
||||||
if (typeof q.selector === 'string') {
|
if (typeof q.selector === 'string') {
|
||||||
selectors = this._queryVarBindings(q.selector).map(varName => this.getTokenMetadata(varName));
|
selectors =
|
||||||
|
this._queryVarBindings(q.selector).map(varName => this._getTokenMetadata(varName));
|
||||||
} else {
|
} else {
|
||||||
if (!q.selector) {
|
if (!q.selector) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Can't construct a query for the property "${propertyName}" of "${stringify(typeOrFunc)}" since the query selector wasn't defined.`);
|
`Can't construct a query for the property "${propertyName}" of "${stringify(typeOrFunc)}" since the query selector wasn't defined.`);
|
||||||
}
|
}
|
||||||
selectors = [this.getTokenMetadata(q.selector)];
|
selectors = [this._getTokenMetadata(q.selector)];
|
||||||
}
|
}
|
||||||
|
|
||||||
return new cpl.CompileQueryMetadata({
|
return new cpl.CompileQueryMetadata({
|
||||||
selectors,
|
selectors,
|
||||||
first: q.first,
|
first: q.first,
|
||||||
descendants: q.descendants, propertyName,
|
descendants: q.descendants, propertyName,
|
||||||
read: q.read ? this.getTokenMetadata(q.read) : null
|
read: q.read ? this._getTokenMetadata(q.read) : null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ function _isNgModuleMetadata(obj: any): obj is NgModule {
|
||||||
export class NgModuleResolver {
|
export class NgModuleResolver {
|
||||||
constructor(private _reflector: ReflectorReader = reflector) {}
|
constructor(private _reflector: ReflectorReader = reflector) {}
|
||||||
|
|
||||||
|
isNgModule(type: any) { return this._reflector.annotations(type).some(_isNgModuleMetadata); }
|
||||||
|
|
||||||
resolve(type: Type<any>, throwIfNotFound = true): NgModule {
|
resolve(type: Type<any>, throwIfNotFound = true): NgModule {
|
||||||
const ngModuleMeta: NgModule = this._reflector.annotations(type).find(_isNgModuleMetadata);
|
const ngModuleMeta: NgModule = this._reflector.annotations(type).find(_isNgModuleMetadata);
|
||||||
|
|
||||||
|
|
|
@ -30,28 +30,16 @@ export class SourceModule {
|
||||||
// Returns all the source files and a mapping from modules to directives
|
// Returns all the source files and a mapping from modules to directives
|
||||||
export function analyzeNgModules(
|
export function analyzeNgModules(
|
||||||
programStaticSymbols: StaticSymbol[], options: {transitiveModules: boolean},
|
programStaticSymbols: StaticSymbol[], options: {transitiveModules: boolean},
|
||||||
metadataResolver: CompileMetadataResolver): {
|
metadataResolver: CompileMetadataResolver): Promise<{
|
||||||
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
||||||
files: Array<{srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}>
|
files: Array<{srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}>
|
||||||
} {
|
}> {
|
||||||
const {
|
return _loadNgModules(programStaticSymbols, options, metadataResolver).then(_analyzeNgModules);
|
||||||
ngModules: programNgModules,
|
|
||||||
pipesAndDirectives: programPipesOrDirectives,
|
|
||||||
} = _extractModulesAndPipesOrDirectives(programStaticSymbols, metadataResolver);
|
|
||||||
|
|
||||||
const moduleMetasByRef = new Map<any, CompileNgModuleMetadata>();
|
|
||||||
|
|
||||||
programNgModules.forEach(modMeta => {
|
|
||||||
if (options.transitiveModules) {
|
|
||||||
// For every input modules add the list of transitively included modules
|
|
||||||
modMeta.transitiveModule.modules.forEach(
|
|
||||||
modMeta => { moduleMetasByRef.set(modMeta.type.reference, modMeta); });
|
|
||||||
} else {
|
|
||||||
moduleMetasByRef.set(modMeta.type.reference, modMeta);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
const ngModuleMetas = Array.from(moduleMetasByRef.values());
|
function _analyzeNgModules(ngModuleMetas: CompileNgModuleMetadata[]) {
|
||||||
|
const moduleMetasByRef = new Map<any, CompileNgModuleMetadata>();
|
||||||
|
ngModuleMetas.forEach((ngModule) => moduleMetasByRef.set(ngModule.type.reference, ngModule));
|
||||||
const ngModuleByPipeOrDirective = new Map<StaticSymbol, CompileNgModuleMetadata>();
|
const ngModuleByPipeOrDirective = new Map<StaticSymbol, CompileNgModuleMetadata>();
|
||||||
const ngModulesByFile = new Map<string, StaticSymbol[]>();
|
const ngModulesByFile = new Map<string, StaticSymbol[]>();
|
||||||
const ngDirectivesByFile = new Map<string, StaticSymbol[]>();
|
const ngDirectivesByFile = new Map<string, StaticSymbol[]>();
|
||||||
|
@ -67,31 +55,20 @@ export function analyzeNgModules(
|
||||||
ngModulesByFile.set(
|
ngModulesByFile.set(
|
||||||
srcFileUrl, (ngModulesByFile.get(srcFileUrl) || []).concat(ngModuleMeta.type.reference));
|
srcFileUrl, (ngModulesByFile.get(srcFileUrl) || []).concat(ngModuleMeta.type.reference));
|
||||||
|
|
||||||
ngModuleMeta.declaredDirectives.forEach((dirMeta: CompileDirectiveMetadata) => {
|
ngModuleMeta.declaredDirectives.forEach((dirIdentifier) => {
|
||||||
const fileUrl = dirMeta.type.reference.filePath;
|
const fileUrl = dirIdentifier.reference.filePath;
|
||||||
filePaths.add(fileUrl);
|
filePaths.add(fileUrl);
|
||||||
ngDirectivesByFile.set(
|
ngDirectivesByFile.set(
|
||||||
fileUrl, (ngDirectivesByFile.get(fileUrl) || []).concat(dirMeta.type.reference));
|
fileUrl, (ngDirectivesByFile.get(fileUrl) || []).concat(dirIdentifier.reference));
|
||||||
ngModuleByPipeOrDirective.set(dirMeta.type.reference, ngModuleMeta);
|
ngModuleByPipeOrDirective.set(dirIdentifier.reference, ngModuleMeta);
|
||||||
});
|
});
|
||||||
|
ngModuleMeta.declaredPipes.forEach((pipeIdentifier) => {
|
||||||
ngModuleMeta.declaredPipes.forEach((pipeMeta: CompilePipeMetadata) => {
|
const fileUrl = pipeIdentifier.reference.filePath;
|
||||||
const fileUrl = pipeMeta.type.reference.filePath;
|
|
||||||
filePaths.add(fileUrl);
|
filePaths.add(fileUrl);
|
||||||
ngModuleByPipeOrDirective.set(pipeMeta.type.reference, ngModuleMeta);
|
ngModuleByPipeOrDirective.set(pipeIdentifier.reference, ngModuleMeta);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Throw an error if any of the program pipe or directives is not declared by a module
|
|
||||||
const symbolsMissingModule =
|
|
||||||
programPipesOrDirectives.filter(s => !ngModuleByPipeOrDirective.has(s));
|
|
||||||
|
|
||||||
if (symbolsMissingModule.length) {
|
|
||||||
const messages = symbolsMissingModule.map(
|
|
||||||
s => `Cannot determine the module for class ${s.name} in ${s.filePath}!`);
|
|
||||||
throw new Error(messages.join('\n'));
|
|
||||||
}
|
|
||||||
|
|
||||||
const files: {srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}[] = [];
|
const files: {srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}[] = [];
|
||||||
|
|
||||||
filePaths.forEach((srcUrl) => {
|
filePaths.forEach((srcUrl) => {
|
||||||
|
@ -112,35 +89,29 @@ export class OfflineCompiler {
|
||||||
private _animationCompiler = new AnimationCompiler();
|
private _animationCompiler = new AnimationCompiler();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _metadataResolver: CompileMetadataResolver,
|
private _metadataResolver: CompileMetadataResolver, private _templateParser: TemplateParser,
|
||||||
private _directiveNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
|
|
||||||
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
||||||
private _dirWrapperCompiler: DirectiveWrapperCompiler,
|
private _dirWrapperCompiler: DirectiveWrapperCompiler,
|
||||||
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
|
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
|
||||||
private _localeId: string, private _translationFormat: string,
|
private _localeId: string, private _translationFormat: string,
|
||||||
private _animationParser: AnimationParser) {}
|
private _animationParser: AnimationParser) {}
|
||||||
|
|
||||||
clearCache() {
|
clearCache() { this._metadataResolver.clearCache(); }
|
||||||
this._directiveNormalizer.clearCache();
|
|
||||||
this._metadataResolver.clearCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
compileModules(staticSymbols: StaticSymbol[], options: {transitiveModules: boolean}):
|
compileModules(staticSymbols: StaticSymbol[], options: {transitiveModules: boolean}):
|
||||||
Promise<SourceModule[]> {
|
Promise<SourceModule[]> {
|
||||||
const {ngModuleByPipeOrDirective, files} =
|
return analyzeNgModules(staticSymbols, options, this._metadataResolver)
|
||||||
analyzeNgModules(staticSymbols, options, this._metadataResolver);
|
.then(({ngModuleByPipeOrDirective, files}) => {
|
||||||
|
|
||||||
const sourceModules = files.map(
|
const sourceModules = files.map(
|
||||||
file => this._compileSrcFile(
|
file => this._compileSrcFile(
|
||||||
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.ngModules));
|
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.ngModules));
|
||||||
|
return ListWrapper.flatten(sourceModules);
|
||||||
return Promise.all(sourceModules)
|
});
|
||||||
.then((modules: SourceModule[][]) => ListWrapper.flatten(modules));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compileSrcFile(
|
private _compileSrcFile(
|
||||||
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
||||||
directives: StaticSymbol[], ngModules: StaticSymbol[]): Promise<SourceModule[]> {
|
directives: StaticSymbol[], ngModules: StaticSymbol[]): SourceModule[] {
|
||||||
const fileSuffix = _splitTypescriptSuffix(srcFileUrl)[1];
|
const fileSuffix = _splitTypescriptSuffix(srcFileUrl)[1];
|
||||||
const statements: o.Statement[] = [];
|
const statements: o.Statement[] = [];
|
||||||
const exportedVars: string[] = [];
|
const exportedVars: string[] = [];
|
||||||
|
@ -155,8 +126,7 @@ export class OfflineCompiler {
|
||||||
(directiveType) => this._compileDirectiveWrapper(directiveType, statements)));
|
(directiveType) => this._compileDirectiveWrapper(directiveType, statements)));
|
||||||
|
|
||||||
// compile components
|
// compile components
|
||||||
return Promise
|
directives.forEach((dirType) => {
|
||||||
.all(directives.map((dirType) => {
|
|
||||||
const compMeta = this._metadataResolver.getDirectiveMetadata(<any>dirType);
|
const compMeta = this._metadataResolver.getDirectiveMetadata(<any>dirType);
|
||||||
if (!compMeta.isComponent) {
|
if (!compMeta.isComponent) {
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
|
@ -167,36 +137,27 @@ export class OfflineCompiler {
|
||||||
`Internal Error: cannot determine the module for component ${compMeta.type.name}!`);
|
`Internal Error: cannot determine the module for component ${compMeta.type.name}!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise
|
|
||||||
.all([compMeta, ...ngModule.transitiveModule.directives].map(
|
|
||||||
dirMeta => this._directiveNormalizer.normalizeDirective(dirMeta).asyncResult))
|
|
||||||
.then((normalizedCompWithDirectives) => {
|
|
||||||
const [compMeta, ...dirMetas] = normalizedCompWithDirectives;
|
|
||||||
_assertComponent(compMeta);
|
_assertComponent(compMeta);
|
||||||
|
|
||||||
// compile styles
|
// compile styles
|
||||||
const stylesCompileResults = this._styleCompiler.compileComponent(compMeta);
|
const stylesCompileResults = this._styleCompiler.compileComponent(compMeta);
|
||||||
stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => {
|
stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => {
|
||||||
outputSourceModules.push(
|
outputSourceModules.push(this._codgenStyles(srcFileUrl, compiledStyleSheet, fileSuffix));
|
||||||
this._codgenStyles(srcFileUrl, compiledStyleSheet, fileSuffix));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// compile components
|
// compile components
|
||||||
exportedVars.push(
|
exportedVars.push(
|
||||||
this._compileComponentFactory(compMeta, fileSuffix, statements),
|
this._compileComponentFactory(compMeta, ngModule, fileSuffix, statements),
|
||||||
this._compileComponent(
|
this._compileComponent(
|
||||||
compMeta, dirMetas, ngModule.transitiveModule.pipes, ngModule.schemas,
|
compMeta, ngModule, ngModule.transitiveModule.directives,
|
||||||
stylesCompileResults.componentStylesheet, fileSuffix, statements));
|
stylesCompileResults.componentStylesheet, fileSuffix, statements));
|
||||||
});
|
});
|
||||||
}))
|
|
||||||
.then(() => {
|
|
||||||
if (statements.length > 0) {
|
if (statements.length > 0) {
|
||||||
const srcModule = this._codegenSourceModule(
|
const srcModule = this._codegenSourceModule(
|
||||||
srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars);
|
srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars);
|
||||||
outputSourceModules.unshift(srcModule);
|
outputSourceModules.unshift(srcModule);
|
||||||
}
|
}
|
||||||
return outputSourceModules;
|
return outputSourceModules;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
|
private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
|
||||||
|
@ -238,11 +199,11 @@ export class OfflineCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compileComponentFactory(
|
private _compileComponentFactory(
|
||||||
compMeta: CompileDirectiveMetadata, fileSuffix: string,
|
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: string,
|
||||||
targetStatements: o.Statement[]): string {
|
targetStatements: o.Statement[]): string {
|
||||||
const hostMeta = createHostComponentMeta(compMeta);
|
const hostMeta = createHostComponentMeta(compMeta);
|
||||||
const hostViewFactoryVar =
|
const hostViewFactoryVar = this._compileComponent(
|
||||||
this._compileComponent(hostMeta, [compMeta], [], [], null, fileSuffix, targetStatements);
|
hostMeta, ngModule, [compMeta.type], null, fileSuffix, targetStatements);
|
||||||
const compFactoryVar = _componentFactoryName(compMeta.type);
|
const compFactoryVar = _componentFactoryName(compMeta.type);
|
||||||
targetStatements.push(
|
targetStatements.push(
|
||||||
o.variable(compFactoryVar)
|
o.variable(compFactoryVar)
|
||||||
|
@ -262,12 +223,18 @@ export class OfflineCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compileComponent(
|
private _compileComponent(
|
||||||
compMeta: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[],
|
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata,
|
||||||
pipes: CompilePipeMetadata[], schemas: SchemaMetadata[], componentStyles: CompiledStylesheet,
|
directiveIdentifiers: CompileIdentifierMetadata[], componentStyles: CompiledStylesheet,
|
||||||
fileSuffix: string, targetStatements: o.Statement[]): string {
|
fileSuffix: string, targetStatements: o.Statement[]): string {
|
||||||
const parsedAnimations = this._animationParser.parseComponent(compMeta);
|
const parsedAnimations = this._animationParser.parseComponent(compMeta);
|
||||||
|
const directives =
|
||||||
|
directiveIdentifiers.map(dir => this._metadataResolver.getDirectiveMetadata(dir.reference));
|
||||||
|
const pipes = ngModule.transitiveModule.pipes.map(
|
||||||
|
pipe => this._metadataResolver.getPipeMetadata(pipe.reference));
|
||||||
|
|
||||||
const parsedTemplate = this._templateParser.parse(
|
const parsedTemplate = this._templateParser.parse(
|
||||||
compMeta, compMeta.template.template, directives, pipes, schemas, compMeta.type.name);
|
compMeta, compMeta.template.template, directives, pipes, ngModule.schemas,
|
||||||
|
compMeta.type.name);
|
||||||
const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
|
const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
|
||||||
const compiledAnimations =
|
const compiledAnimations =
|
||||||
this._animationCompiler.compile(compMeta.type.name, parsedAnimations);
|
this._animationCompiler.compile(compMeta.type.name, parsedAnimations);
|
||||||
|
@ -358,27 +325,50 @@ function _splitTypescriptSuffix(path: string): string[] {
|
||||||
return [path, ''];
|
return [path, ''];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group the symbols by types:
|
// Load the NgModules and check
|
||||||
// - NgModules,
|
// that all directives / pipes that are present in the program
|
||||||
// - Pipes and Directives.
|
// are also declared by a module.
|
||||||
function _extractModulesAndPipesOrDirectives(
|
function _loadNgModules(
|
||||||
programStaticSymbols: StaticSymbol[], metadataResolver: CompileMetadataResolver) {
|
programStaticSymbols: StaticSymbol[], options: {transitiveModules: boolean},
|
||||||
const ngModules: CompileNgModuleMetadata[] = [];
|
metadataResolver: CompileMetadataResolver): Promise<CompileNgModuleMetadata[]> {
|
||||||
const pipesAndDirectives: StaticSymbol[] = [];
|
const ngModules = new Map<any, CompileNgModuleMetadata>();
|
||||||
|
const programPipesAndDirectives: StaticSymbol[] = [];
|
||||||
programStaticSymbols.forEach(staticSymbol => {
|
const ngModulePipesAndDirective = new Set<StaticSymbol>();
|
||||||
const ngModule = metadataResolver.getNgModuleMetadata(staticSymbol, false);
|
const loadingPromises: Promise<any>[] = [];
|
||||||
const directive = metadataResolver.getDirectiveMetadata(staticSymbol, false);
|
|
||||||
const pipe = metadataResolver.getPipeMetadata(<any>staticSymbol, false);
|
|
||||||
|
|
||||||
|
const addNgModule = (staticSymbol: any) => {
|
||||||
|
if (ngModules.has(staticSymbol)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const {ngModule, loading} = metadataResolver.loadNgModuleMetadata(staticSymbol, false, false);
|
||||||
if (ngModule) {
|
if (ngModule) {
|
||||||
ngModules.push(ngModule);
|
ngModules.set(ngModule.type.reference, ngModule);
|
||||||
} else if (directive) {
|
loadingPromises.push(loading);
|
||||||
pipesAndDirectives.push(staticSymbol);
|
ngModule.declaredDirectives.forEach((dir) => ngModulePipesAndDirective.add(dir.reference));
|
||||||
} else if (pipe) {
|
ngModule.declaredPipes.forEach((pipe) => ngModulePipesAndDirective.add(pipe.reference));
|
||||||
pipesAndDirectives.push(staticSymbol);
|
if (options.transitiveModules) {
|
||||||
|
// For every input modules add the list of transitively included modules
|
||||||
|
ngModule.transitiveModule.modules.forEach(modMeta => addNgModule(modMeta.type.reference));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !!ngModule;
|
||||||
|
};
|
||||||
|
programStaticSymbols.forEach((staticSymbol) => {
|
||||||
|
if (!addNgModule(staticSymbol) &&
|
||||||
|
(metadataResolver.isDirective(staticSymbol) || metadataResolver.isPipe(staticSymbol))) {
|
||||||
|
programPipesAndDirectives.push(staticSymbol);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {ngModules, pipesAndDirectives};
|
// Throw an error if any of the program pipe or directives is not declared by a module
|
||||||
|
const symbolsMissingModule =
|
||||||
|
programPipesAndDirectives.filter(s => !ngModulePipesAndDirective.has(s));
|
||||||
|
|
||||||
|
if (symbolsMissingModule.length) {
|
||||||
|
const messages = symbolsMissingModule.map(
|
||||||
|
s => `Cannot determine the module for class ${s.name} in ${s.filePath}!`);
|
||||||
|
throw new Error(messages.join('\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(loadingPromises).then(() => Array.from(ngModules.values()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,11 @@ function _isPipeMetadata(type: any): boolean {
|
||||||
export class PipeResolver {
|
export class PipeResolver {
|
||||||
constructor(private _reflector: ReflectorReader = reflector) {}
|
constructor(private _reflector: ReflectorReader = reflector) {}
|
||||||
|
|
||||||
|
isPipe(type: Type<any>) {
|
||||||
|
const typeMetadata = this._reflector.annotations(resolveForwardRef(type));
|
||||||
|
return typeMetadata && typeMetadata.some(_isPipeMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return {@link Pipe} for a given `Type`.
|
* Return {@link Pipe} for a given `Type`.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -20,7 +20,6 @@ import {NgModuleCompiler} from './ng_module_compiler';
|
||||||
import * as ir from './output/output_ast';
|
import * as ir from './output/output_ast';
|
||||||
import {interpretStatements} from './output/output_interpreter';
|
import {interpretStatements} from './output/output_interpreter';
|
||||||
import {jitStatements} from './output/output_jit';
|
import {jitStatements} from './output/output_jit';
|
||||||
import {ComponentStillLoadingError} from './private_import_core';
|
|
||||||
import {CompiledStylesheet, StyleCompiler} from './style_compiler';
|
import {CompiledStylesheet, StyleCompiler} from './style_compiler';
|
||||||
import {TemplateParser} from './template_parser/template_parser';
|
import {TemplateParser} from './template_parser/template_parser';
|
||||||
import {SyncAsyncResult} from './util';
|
import {SyncAsyncResult} from './util';
|
||||||
|
@ -47,9 +46,8 @@ export class RuntimeCompiler implements Compiler {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _injector: Injector, private _metadataResolver: CompileMetadataResolver,
|
private _injector: Injector, private _metadataResolver: CompileMetadataResolver,
|
||||||
private _templateNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
|
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
|
||||||
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
private _viewCompiler: ViewCompiler, private _ngModuleCompiler: NgModuleCompiler,
|
||||||
private _ngModuleCompiler: NgModuleCompiler,
|
|
||||||
private _directiveWrapperCompiler: DirectiveWrapperCompiler,
|
private _directiveWrapperCompiler: DirectiveWrapperCompiler,
|
||||||
private _compilerConfig: CompilerConfig, private _animationParser: AnimationParser) {}
|
private _compilerConfig: CompilerConfig, private _animationParser: AnimationParser) {}
|
||||||
|
|
||||||
|
@ -74,38 +72,47 @@ export class RuntimeCompiler implements Compiler {
|
||||||
|
|
||||||
private _compileModuleAndComponents<T>(moduleType: Type<T>, isSync: boolean):
|
private _compileModuleAndComponents<T>(moduleType: Type<T>, isSync: boolean):
|
||||||
SyncAsyncResult<NgModuleFactory<T>> {
|
SyncAsyncResult<NgModuleFactory<T>> {
|
||||||
const componentPromise = this._compileComponents(moduleType, isSync);
|
const loadingPromise = this._loadModules(moduleType, isSync);
|
||||||
const ngModuleFactory = this._compileModule(moduleType);
|
const createResult = () => {
|
||||||
return new SyncAsyncResult(ngModuleFactory, componentPromise.then(() => ngModuleFactory));
|
this._compileComponents(moduleType, null);
|
||||||
|
return this._compileModule(moduleType);
|
||||||
|
};
|
||||||
|
if (isSync) {
|
||||||
|
return new SyncAsyncResult(createResult());
|
||||||
|
} else {
|
||||||
|
return new SyncAsyncResult(null, loadingPromise.then(createResult));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compileModuleAndAllComponents<T>(moduleType: Type<T>, isSync: boolean):
|
private _compileModuleAndAllComponents<T>(moduleType: Type<T>, isSync: boolean):
|
||||||
SyncAsyncResult<ModuleWithComponentFactories<T>> {
|
SyncAsyncResult<ModuleWithComponentFactories<T>> {
|
||||||
const componentPromise = this._compileComponents(moduleType, isSync);
|
const loadingPromise = this._loadModules(moduleType, isSync);
|
||||||
const ngModuleFactory = this._compileModule(moduleType);
|
const createResult = () => {
|
||||||
const moduleMeta = this._metadataResolver.getNgModuleMetadata(moduleType);
|
|
||||||
const componentFactories: ComponentFactory<any>[] = [];
|
const componentFactories: ComponentFactory<any>[] = [];
|
||||||
const templates = new Set<CompiledTemplate>();
|
this._compileComponents(moduleType, componentFactories);
|
||||||
moduleMeta.transitiveModule.modules.forEach((localModuleMeta) => {
|
return new ModuleWithComponentFactories(this._compileModule(moduleType), componentFactories);
|
||||||
localModuleMeta.declaredDirectives.forEach((dirMeta) => {
|
|
||||||
if (dirMeta.isComponent) {
|
|
||||||
const template =
|
|
||||||
this._createCompiledHostTemplate(dirMeta.type.reference, localModuleMeta);
|
|
||||||
templates.add(template);
|
|
||||||
componentFactories.push(template.proxyComponentFactory);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
const syncResult = new ModuleWithComponentFactories(ngModuleFactory, componentFactories);
|
|
||||||
// Note: host components themselves can always be compiled synchronously as they have an
|
|
||||||
// inline template. However, we still need to wait for the components that they
|
|
||||||
// reference to be loaded / compiled.
|
|
||||||
const compile = () => {
|
|
||||||
templates.forEach((template) => { this._compileTemplate(template); });
|
|
||||||
return syncResult;
|
|
||||||
};
|
};
|
||||||
const asyncResult = isSync ? Promise.resolve(compile()) : componentPromise.then(compile);
|
if (isSync) {
|
||||||
return new SyncAsyncResult(syncResult, asyncResult);
|
return new SyncAsyncResult(createResult());
|
||||||
|
} else {
|
||||||
|
return new SyncAsyncResult(null, loadingPromise.then(createResult));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _loadModules(mainModule: any, isSync: boolean): Promise<any> {
|
||||||
|
var loadingPromises: Promise<any>[] = [];
|
||||||
|
const {ngModule, loading} = this._metadataResolver.loadNgModuleMetadata(mainModule, isSync);
|
||||||
|
loadingPromises.push(loading);
|
||||||
|
// Note: the loadingPromise for a module only includes the loading of the exported directives
|
||||||
|
// of imported modules.
|
||||||
|
// However, for runtime compilation, we want to transitively compile all modules,
|
||||||
|
// so we also need to call loadNgModuleMetadata for all nested modules.
|
||||||
|
ngModule.transitiveModule.modules.forEach((localModuleMeta) => {
|
||||||
|
loadingPromises.push(
|
||||||
|
this._metadataResolver.loadNgModuleMetadata(localModuleMeta.type.reference, isSync)
|
||||||
|
.loading);
|
||||||
|
});
|
||||||
|
return Promise.all(loadingPromises);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compileModule<T>(moduleType: Type<T>): NgModuleFactory<T> {
|
private _compileModule<T>(moduleType: Type<T>): NgModuleFactory<T> {
|
||||||
|
@ -137,23 +144,30 @@ export class RuntimeCompiler implements Compiler {
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
_compileComponents(mainModule: Type<any>, isSync: boolean): Promise<any> {
|
_compileComponents(mainModule: Type<any>, allComponentFactories: ComponentFactory<any>[]) {
|
||||||
const templates = new Set<CompiledTemplate>();
|
|
||||||
var loadingPromises: Promise<any>[] = [];
|
|
||||||
|
|
||||||
const ngModule = this._metadataResolver.getNgModuleMetadata(mainModule);
|
const ngModule = this._metadataResolver.getNgModuleMetadata(mainModule);
|
||||||
const moduleByDirective = new Map<any, CompileNgModuleMetadata>();
|
const moduleByDirective = new Map<any, CompileNgModuleMetadata>();
|
||||||
|
const templates = new Set<CompiledTemplate>();
|
||||||
|
|
||||||
ngModule.transitiveModule.modules.forEach((localModuleMeta) => {
|
ngModule.transitiveModule.modules.forEach((localModuleMeta) => {
|
||||||
localModuleMeta.declaredDirectives.forEach((dirMeta) => {
|
localModuleMeta.declaredDirectives.forEach((dirIdentifier) => {
|
||||||
moduleByDirective.set(dirMeta.type.reference, localModuleMeta);
|
moduleByDirective.set(dirIdentifier.reference, localModuleMeta);
|
||||||
|
const dirMeta = this._metadataResolver.getDirectiveMetadata(dirIdentifier.reference);
|
||||||
this._compileDirectiveWrapper(dirMeta, localModuleMeta);
|
this._compileDirectiveWrapper(dirMeta, localModuleMeta);
|
||||||
if (dirMeta.isComponent) {
|
if (dirMeta.isComponent) {
|
||||||
templates.add(this._createCompiledTemplate(dirMeta, localModuleMeta));
|
templates.add(this._createCompiledTemplate(dirMeta, localModuleMeta));
|
||||||
|
if (allComponentFactories) {
|
||||||
|
const template =
|
||||||
|
this._createCompiledHostTemplate(dirMeta.type.reference, localModuleMeta);
|
||||||
|
templates.add(template);
|
||||||
|
allComponentFactories.push(template.proxyComponentFactory);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ngModule.transitiveModule.modules.forEach((localModuleMeta) => {
|
ngModule.transitiveModule.modules.forEach((localModuleMeta) => {
|
||||||
localModuleMeta.declaredDirectives.forEach((dirMeta) => {
|
localModuleMeta.declaredDirectives.forEach((dirIdentifier) => {
|
||||||
|
const dirMeta = this._metadataResolver.getDirectiveMetadata(dirIdentifier.reference);
|
||||||
if (dirMeta.isComponent) {
|
if (dirMeta.isComponent) {
|
||||||
dirMeta.entryComponents.forEach((entryComponentType) => {
|
dirMeta.entryComponents.forEach((entryComponentType) => {
|
||||||
const moduleMeta = moduleByDirective.get(entryComponentType.reference);
|
const moduleMeta = moduleByDirective.get(entryComponentType.reference);
|
||||||
|
@ -167,24 +181,7 @@ export class RuntimeCompiler implements Compiler {
|
||||||
templates.add(this._createCompiledHostTemplate(entryComponentType.reference, moduleMeta));
|
templates.add(this._createCompiledHostTemplate(entryComponentType.reference, moduleMeta));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
templates.forEach((template) => this._compileTemplate(template));
|
||||||
templates.forEach((template) => {
|
|
||||||
if (template.loading) {
|
|
||||||
if (isSync) {
|
|
||||||
throw new ComponentStillLoadingError(template.compType.reference);
|
|
||||||
} else {
|
|
||||||
loadingPromises.push(template.loading);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const compile =
|
|
||||||
() => { templates.forEach((template) => { this._compileTemplate(template); }); };
|
|
||||||
if (isSync) {
|
|
||||||
compile();
|
|
||||||
return Promise.resolve(null);
|
|
||||||
} else {
|
|
||||||
return Promise.all(loadingPromises).then(compile);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCacheFor(type: Type<any>) {
|
clearCacheFor(type: Type<any>) {
|
||||||
|
@ -193,7 +190,6 @@ export class RuntimeCompiler implements Compiler {
|
||||||
this._compiledHostTemplateCache.delete(type);
|
this._compiledHostTemplateCache.delete(type);
|
||||||
var compiledTemplate = this._compiledTemplateCache.get(type);
|
var compiledTemplate = this._compiledTemplateCache.get(type);
|
||||||
if (compiledTemplate) {
|
if (compiledTemplate) {
|
||||||
this._templateNormalizer.clearCacheFor(compiledTemplate.normalizedCompMeta);
|
|
||||||
this._compiledTemplateCache.delete(type);
|
this._compiledTemplateCache.delete(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,7 +198,6 @@ export class RuntimeCompiler implements Compiler {
|
||||||
this._metadataResolver.clearCache();
|
this._metadataResolver.clearCache();
|
||||||
this._compiledTemplateCache.clear();
|
this._compiledTemplateCache.clear();
|
||||||
this._compiledHostTemplateCache.clear();
|
this._compiledHostTemplateCache.clear();
|
||||||
this._templateNormalizer.clearCache();
|
|
||||||
this._compiledNgModuleCache.clear();
|
this._compiledNgModuleCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,8 +213,7 @@ export class RuntimeCompiler implements Compiler {
|
||||||
assertComponent(compMeta);
|
assertComponent(compMeta);
|
||||||
var hostMeta = createHostComponentMeta(compMeta);
|
var hostMeta = createHostComponentMeta(compMeta);
|
||||||
compiledTemplate = new CompiledTemplate(
|
compiledTemplate = new CompiledTemplate(
|
||||||
true, compMeta.selector, compMeta.type, ngModule, [compMeta],
|
true, compMeta.selector, compMeta.type, hostMeta, ngModule, [compMeta.type]);
|
||||||
this._templateNormalizer.normalizeDirective(hostMeta));
|
|
||||||
this._compiledHostTemplateCache.set(compType, compiledTemplate);
|
this._compiledHostTemplateCache.set(compType, compiledTemplate);
|
||||||
}
|
}
|
||||||
return compiledTemplate;
|
return compiledTemplate;
|
||||||
|
@ -231,8 +225,8 @@ export class RuntimeCompiler implements Compiler {
|
||||||
if (!compiledTemplate) {
|
if (!compiledTemplate) {
|
||||||
assertComponent(compMeta);
|
assertComponent(compMeta);
|
||||||
compiledTemplate = new CompiledTemplate(
|
compiledTemplate = new CompiledTemplate(
|
||||||
false, compMeta.selector, compMeta.type, ngModule, ngModule.transitiveModule.directives,
|
false, compMeta.selector, compMeta.type, compMeta, ngModule,
|
||||||
this._templateNormalizer.normalizeDirective(compMeta));
|
ngModule.transitiveModule.directives);
|
||||||
this._compiledTemplateCache.set(compMeta.type.reference, compiledTemplate);
|
this._compiledTemplateCache.set(compMeta.type.reference, compiledTemplate);
|
||||||
}
|
}
|
||||||
return compiledTemplate;
|
return compiledTemplate;
|
||||||
|
@ -243,16 +237,7 @@ export class RuntimeCompiler implements Compiler {
|
||||||
this._compiledTemplateCache.get(compType);
|
this._compiledTemplateCache.get(compType);
|
||||||
if (!compiledTemplate) {
|
if (!compiledTemplate) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Illegal state: Compiled view for component ${stringify(compType)} does not exist!`);
|
`Illegal state: Compiled view for component ${stringify(compType)} (host: ${isHost}) does not exist!`);
|
||||||
}
|
|
||||||
return compiledTemplate;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _assertComponentLoaded(compType: any, isHost: boolean): CompiledTemplate {
|
|
||||||
const compiledTemplate = this._assertComponentKnown(compType, isHost);
|
|
||||||
if (compiledTemplate.loading) {
|
|
||||||
throw new Error(
|
|
||||||
`Illegal state: CompiledTemplate for ${stringify(compType)} (isHost: ${isHost}) is still loading!`);
|
|
||||||
}
|
}
|
||||||
return compiledTemplate;
|
return compiledTemplate;
|
||||||
}
|
}
|
||||||
|
@ -285,34 +270,36 @@ export class RuntimeCompiler implements Compiler {
|
||||||
if (template.isCompiled) {
|
if (template.isCompiled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const compMeta = template.normalizedCompMeta;
|
const compMeta = template.compMeta;
|
||||||
const externalStylesheetsByModuleUrl = new Map<string, CompiledStylesheet>();
|
const externalStylesheetsByModuleUrl = new Map<string, CompiledStylesheet>();
|
||||||
const stylesCompileResult = this._styleCompiler.compileComponent(compMeta);
|
const stylesCompileResult = this._styleCompiler.compileComponent(compMeta);
|
||||||
stylesCompileResult.externalStylesheets.forEach(
|
stylesCompileResult.externalStylesheets.forEach(
|
||||||
(r) => { externalStylesheetsByModuleUrl.set(r.meta.moduleUrl, r); });
|
(r) => { externalStylesheetsByModuleUrl.set(r.meta.moduleUrl, r); });
|
||||||
this._resolveStylesCompileResult(
|
this._resolveStylesCompileResult(
|
||||||
stylesCompileResult.componentStylesheet, externalStylesheetsByModuleUrl);
|
stylesCompileResult.componentStylesheet, externalStylesheetsByModuleUrl);
|
||||||
const viewCompMetas = template.viewComponentTypes.map(
|
|
||||||
(compType) => this._assertComponentLoaded(compType, false).normalizedCompMeta);
|
|
||||||
const parsedAnimations = this._animationParser.parseComponent(compMeta);
|
const parsedAnimations = this._animationParser.parseComponent(compMeta);
|
||||||
|
const directives =
|
||||||
|
template.directives.map(dir => this._metadataResolver.getDirectiveMetadata(dir.reference));
|
||||||
|
const pipes = template.ngModule.transitiveModule.pipes.map(
|
||||||
|
pipe => this._metadataResolver.getPipeMetadata(pipe.reference));
|
||||||
const parsedTemplate = this._templateParser.parse(
|
const parsedTemplate = this._templateParser.parse(
|
||||||
compMeta, compMeta.template.template, template.viewDirectives.concat(viewCompMetas),
|
compMeta, compMeta.template.template, directives, pipes, template.ngModule.schemas,
|
||||||
template.viewPipes, template.schemas, compMeta.type.name);
|
compMeta.type.name);
|
||||||
const compiledAnimations =
|
const compiledAnimations =
|
||||||
this._animationCompiler.compile(compMeta.type.name, parsedAnimations);
|
this._animationCompiler.compile(compMeta.type.name, parsedAnimations);
|
||||||
const compileResult = this._viewCompiler.compileComponent(
|
const compileResult = this._viewCompiler.compileComponent(
|
||||||
compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar),
|
compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar),
|
||||||
template.viewPipes, compiledAnimations);
|
pipes, compiledAnimations);
|
||||||
compileResult.dependencies.forEach((dep) => {
|
compileResult.dependencies.forEach((dep) => {
|
||||||
let depTemplate: CompiledTemplate;
|
let depTemplate: CompiledTemplate;
|
||||||
if (dep instanceof ViewClassDependency) {
|
if (dep instanceof ViewClassDependency) {
|
||||||
let vfd = <ViewClassDependency>dep;
|
let vfd = <ViewClassDependency>dep;
|
||||||
depTemplate = this._assertComponentLoaded(vfd.comp.reference, false);
|
depTemplate = this._assertComponentKnown(vfd.comp.reference, false);
|
||||||
vfd.placeholder.reference = depTemplate.proxyViewClass;
|
vfd.placeholder.reference = depTemplate.proxyViewClass;
|
||||||
vfd.placeholder.name = `View_${vfd.comp.name}`;
|
vfd.placeholder.name = `View_${vfd.comp.name}`;
|
||||||
} else if (dep instanceof ComponentFactoryDependency) {
|
} else if (dep instanceof ComponentFactoryDependency) {
|
||||||
let cfd = <ComponentFactoryDependency>dep;
|
let cfd = <ComponentFactoryDependency>dep;
|
||||||
depTemplate = this._assertComponentLoaded(cfd.comp.reference, true);
|
depTemplate = this._assertComponentKnown(cfd.comp.reference, true);
|
||||||
cfd.placeholder.reference = depTemplate.proxyComponentFactory;
|
cfd.placeholder.reference = depTemplate.proxyComponentFactory;
|
||||||
cfd.placeholder.name = `compFactory_${cfd.comp.name}`;
|
cfd.placeholder.name = `compFactory_${cfd.comp.name}`;
|
||||||
} else if (dep instanceof DirectiveWrapperDependency) {
|
} else if (dep instanceof DirectiveWrapperDependency) {
|
||||||
|
@ -361,29 +348,12 @@ class CompiledTemplate {
|
||||||
private _viewClass: Function = null;
|
private _viewClass: Function = null;
|
||||||
proxyViewClass: Type<any>;
|
proxyViewClass: Type<any>;
|
||||||
proxyComponentFactory: ComponentFactory<any>;
|
proxyComponentFactory: ComponentFactory<any>;
|
||||||
loading: Promise<any> = null;
|
|
||||||
private _normalizedCompMeta: CompileDirectiveMetadata = null;
|
|
||||||
isCompiled = false;
|
isCompiled = false;
|
||||||
isCompiledWithDeps = false;
|
|
||||||
viewComponentTypes: Type<any>[] = [];
|
|
||||||
viewDirectives: CompileDirectiveMetadata[] = [];
|
|
||||||
viewPipes: CompilePipeMetadata[];
|
|
||||||
schemas: SchemaMetadata[];
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public isHost: boolean, selector: string, public compType: CompileIdentifierMetadata,
|
public isHost: boolean, selector: string, public compType: CompileIdentifierMetadata,
|
||||||
public ngModule: CompileNgModuleMetadata,
|
public compMeta: CompileDirectiveMetadata, public ngModule: CompileNgModuleMetadata,
|
||||||
viewDirectiveAndComponents: CompileDirectiveMetadata[],
|
public directives: CompileIdentifierMetadata[]) {
|
||||||
_normalizeResult: SyncAsyncResult<CompileDirectiveMetadata>) {
|
|
||||||
this.viewPipes = ngModule.transitiveModule.pipes;
|
|
||||||
this.schemas = ngModule.schemas;
|
|
||||||
viewDirectiveAndComponents.forEach((dirMeta) => {
|
|
||||||
if (dirMeta.isComponent) {
|
|
||||||
this.viewComponentTypes.push(dirMeta.type.reference);
|
|
||||||
} else {
|
|
||||||
this.viewDirectives.push(dirMeta);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const self = this;
|
const self = this;
|
||||||
this.proxyViewClass = <any>function() {
|
this.proxyViewClass = <any>function() {
|
||||||
if (!self._viewClass) {
|
if (!self._viewClass) {
|
||||||
|
@ -395,21 +365,6 @@ class CompiledTemplate {
|
||||||
this.proxyComponentFactory = isHost ?
|
this.proxyComponentFactory = isHost ?
|
||||||
new ComponentFactory<any>(selector, this.proxyViewClass, compType.reference) :
|
new ComponentFactory<any>(selector, this.proxyViewClass, compType.reference) :
|
||||||
null;
|
null;
|
||||||
if (_normalizeResult.syncResult) {
|
|
||||||
this._normalizedCompMeta = _normalizeResult.syncResult;
|
|
||||||
} else {
|
|
||||||
this.loading = _normalizeResult.asyncResult.then((normalizedCompMeta) => {
|
|
||||||
this._normalizedCompMeta = normalizedCompMeta;
|
|
||||||
this.loading = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get normalizedCompMeta(): CompileDirectiveMetadata {
|
|
||||||
if (this.loading) {
|
|
||||||
throw new Error(`Template is still loading for ${this.compType.name}!`);
|
|
||||||
}
|
|
||||||
return this._normalizedCompMeta;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compiled(viewClass: Function) {
|
compiled(viewClass: Function) {
|
||||||
|
@ -417,8 +372,6 @@ class CompiledTemplate {
|
||||||
this.proxyViewClass.prototype = viewClass.prototype;
|
this.proxyViewClass.prototype = viewClass.prototype;
|
||||||
this.isCompiled = true;
|
this.isCompiled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
depsCompiled() { this.isCompiledWithDeps = true; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertComponent(meta: CompileDirectiveMetadata) {
|
function assertComponent(meta: CompileDirectiveMetadata) {
|
||||||
|
|
|
@ -18,79 +18,78 @@ import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@ang
|
||||||
|
|
||||||
import {SpyResourceLoader} from './spies';
|
import {SpyResourceLoader} from './spies';
|
||||||
|
|
||||||
|
const SOME_MODULE_URL = 'package:some/module/a.js';
|
||||||
|
const SOME_HTTP_MODULE_URL = 'http://some/module/a.js';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('DirectiveNormalizer', () => {
|
describe('DirectiveNormalizer', () => {
|
||||||
var dirType: CompileTypeMetadata;
|
|
||||||
var dirTypeWithHttpUrl: CompileTypeMetadata;
|
|
||||||
|
|
||||||
beforeEach(() => { TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); });
|
beforeEach(() => { TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); });
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
dirType = new CompileTypeMetadata({moduleUrl: 'package:some/module/a.js', name: 'SomeComp'});
|
|
||||||
dirTypeWithHttpUrl =
|
|
||||||
new CompileTypeMetadata({moduleUrl: 'http://some/module/a.js', name: 'SomeComp'});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('normalizeDirective', () => {
|
describe('normalizeDirective', () => {
|
||||||
it('should throw if no template was specified',
|
it('should throw if no template was specified',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
expect(() => normalizer.normalizeDirective(new CompileDirectiveMetadata({
|
expect(() => normalizer.normalizeTemplate({
|
||||||
type: dirType,
|
componentType: SomeComp,
|
||||||
isComponent: true,
|
moduleUrl: SOME_MODULE_URL,
|
||||||
template:
|
})).toThrowError('No template specified for component SomeComp');
|
||||||
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []})
|
|
||||||
}))).toThrowError('No template specified for component SomeComp');
|
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('normalizeTemplateSync', () => {
|
describe('normalizeTemplateSync', () => {
|
||||||
it('should store the template',
|
it('should store the template',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
let template = normalizer.normalizeTemplateSync(dirType, new CompileTemplateMetadata({
|
let template = normalizer.normalizeTemplateSync({
|
||||||
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
encapsulation: null,
|
encapsulation: null,
|
||||||
template: 'a',
|
template: 'a',
|
||||||
templateUrl: null,
|
templateUrl: null,
|
||||||
styles: [],
|
styles: [],
|
||||||
styleUrls: []
|
styleUrls: []
|
||||||
}));
|
});
|
||||||
expect(template.template).toEqual('a');
|
expect(template.template).toEqual('a');
|
||||||
expect(template.templateUrl).toEqual('package:some/module/a.js');
|
expect(template.templateUrl).toEqual('package:some/module/a.js');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should resolve styles on the annotation against the moduleUrl',
|
it('should resolve styles on the annotation against the moduleUrl',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
let template = normalizer.normalizeTemplateSync(dirType, new CompileTemplateMetadata({
|
let template = normalizer.normalizeTemplateSync({
|
||||||
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
encapsulation: null,
|
encapsulation: null,
|
||||||
template: '',
|
template: '',
|
||||||
templateUrl: null,
|
templateUrl: null,
|
||||||
styles: [],
|
styles: [],
|
||||||
styleUrls: ['test.css']
|
styleUrls: ['test.css']
|
||||||
}));
|
});
|
||||||
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
|
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should resolve styles in the template against the moduleUrl',
|
it('should resolve styles in the template against the moduleUrl',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
let template =
|
let template = normalizer.normalizeTemplateSync({
|
||||||
normalizer.normalizeTemplateSync(dirType, new CompileTemplateMetadata({
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
encapsulation: null,
|
encapsulation: null,
|
||||||
template: '<style>@import test.css</style>',
|
template: '<style>@import test.css</style>',
|
||||||
templateUrl: null,
|
templateUrl: null,
|
||||||
styles: [],
|
styles: [],
|
||||||
styleUrls: []
|
styleUrls: []
|
||||||
}));
|
});
|
||||||
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
|
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should use ViewEncapsulation.Emulated by default',
|
it('should use ViewEncapsulation.Emulated by default',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
let template = normalizer.normalizeTemplateSync(dirType, new CompileTemplateMetadata({
|
let template = normalizer.normalizeTemplateSync({
|
||||||
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
encapsulation: null,
|
encapsulation: null,
|
||||||
template: '',
|
template: '',
|
||||||
templateUrl: null,
|
templateUrl: null,
|
||||||
styles: [],
|
styles: [],
|
||||||
styleUrls: ['test.css']
|
styleUrls: ['test.css']
|
||||||
}));
|
});
|
||||||
expect(template.encapsulation).toEqual(ViewEncapsulation.Emulated);
|
expect(template.encapsulation).toEqual(ViewEncapsulation.Emulated);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -99,14 +98,15 @@ export function main() {
|
||||||
[CompilerConfig, DirectiveNormalizer],
|
[CompilerConfig, DirectiveNormalizer],
|
||||||
(config: CompilerConfig, normalizer: DirectiveNormalizer) => {
|
(config: CompilerConfig, normalizer: DirectiveNormalizer) => {
|
||||||
config.defaultEncapsulation = ViewEncapsulation.None;
|
config.defaultEncapsulation = ViewEncapsulation.None;
|
||||||
let template =
|
let template = normalizer.normalizeTemplateSync({
|
||||||
normalizer.normalizeTemplateSync(dirType, new CompileTemplateMetadata({
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
encapsulation: null,
|
encapsulation: null,
|
||||||
template: '',
|
template: '',
|
||||||
templateUrl: null,
|
templateUrl: null,
|
||||||
styles: [],
|
styles: [],
|
||||||
styleUrls: ['test.css']
|
styleUrls: ['test.css']
|
||||||
}));
|
});
|
||||||
expect(template.encapsulation).toEqual(ViewEncapsulation.None);
|
expect(template.encapsulation).toEqual(ViewEncapsulation.None);
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
@ -120,13 +120,15 @@ export function main() {
|
||||||
resourceLoader: MockResourceLoader) => {
|
resourceLoader: MockResourceLoader) => {
|
||||||
resourceLoader.expect('package:some/module/sometplurl.html', 'a');
|
resourceLoader.expect('package:some/module/sometplurl.html', 'a');
|
||||||
normalizer
|
normalizer
|
||||||
.normalizeTemplateAsync(dirType, new CompileTemplateMetadata({
|
.normalizeTemplateAsync({
|
||||||
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
encapsulation: null,
|
encapsulation: null,
|
||||||
template: null,
|
template: null,
|
||||||
templateUrl: 'sometplurl.html',
|
templateUrl: 'sometplurl.html',
|
||||||
styles: [],
|
styles: [],
|
||||||
styleUrls: ['test.css']
|
styleUrls: ['test.css']
|
||||||
}))
|
})
|
||||||
.then((template: CompileTemplateMetadata) => {
|
.then((template: CompileTemplateMetadata) => {
|
||||||
expect(template.template).toEqual('a');
|
expect(template.template).toEqual('a');
|
||||||
expect(template.templateUrl).toEqual('package:some/module/sometplurl.html');
|
expect(template.templateUrl).toEqual('package:some/module/sometplurl.html');
|
||||||
|
@ -142,13 +144,15 @@ export function main() {
|
||||||
resourceLoader: MockResourceLoader) => {
|
resourceLoader: MockResourceLoader) => {
|
||||||
resourceLoader.expect('package:some/module/tpl/sometplurl.html', '');
|
resourceLoader.expect('package:some/module/tpl/sometplurl.html', '');
|
||||||
normalizer
|
normalizer
|
||||||
.normalizeTemplateAsync(dirType, new CompileTemplateMetadata({
|
.normalizeTemplateAsync({
|
||||||
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
encapsulation: null,
|
encapsulation: null,
|
||||||
template: null,
|
template: null,
|
||||||
templateUrl: 'tpl/sometplurl.html',
|
templateUrl: 'tpl/sometplurl.html',
|
||||||
styles: [],
|
styles: [],
|
||||||
styleUrls: ['test.css']
|
styleUrls: ['test.css']
|
||||||
}))
|
})
|
||||||
.then((template: CompileTemplateMetadata) => {
|
.then((template: CompileTemplateMetadata) => {
|
||||||
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
|
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
|
||||||
async.done();
|
async.done();
|
||||||
|
@ -164,13 +168,15 @@ export function main() {
|
||||||
resourceLoader.expect(
|
resourceLoader.expect(
|
||||||
'package:some/module/tpl/sometplurl.html', '<style>@import test.css</style>');
|
'package:some/module/tpl/sometplurl.html', '<style>@import test.css</style>');
|
||||||
normalizer
|
normalizer
|
||||||
.normalizeTemplateAsync(dirType, new CompileTemplateMetadata({
|
.normalizeTemplateAsync({
|
||||||
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
encapsulation: null,
|
encapsulation: null,
|
||||||
template: null,
|
template: null,
|
||||||
templateUrl: 'tpl/sometplurl.html',
|
templateUrl: 'tpl/sometplurl.html',
|
||||||
styles: [],
|
styles: [],
|
||||||
styleUrls: []
|
styleUrls: []
|
||||||
}))
|
})
|
||||||
.then((template: CompileTemplateMetadata) => {
|
.then((template: CompileTemplateMetadata) => {
|
||||||
expect(template.styleUrls).toEqual(['package:some/module/tpl/test.css']);
|
expect(template.styleUrls).toEqual(['package:some/module/tpl/test.css']);
|
||||||
async.done();
|
async.done();
|
||||||
|
@ -249,13 +255,15 @@ export function main() {
|
||||||
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer,
|
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer,
|
||||||
resourceLoader: MockResourceLoader) => {
|
resourceLoader: MockResourceLoader) => {
|
||||||
resourceLoader.expect('package:some/module/cmp.html', 'a');
|
resourceLoader.expect('package:some/module/cmp.html', 'a');
|
||||||
var templateMeta = new CompileTemplateMetadata({
|
var prenormMeta = {
|
||||||
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
templateUrl: 'cmp.html',
|
templateUrl: 'cmp.html',
|
||||||
});
|
};
|
||||||
Promise
|
Promise
|
||||||
.all([
|
.all([
|
||||||
normalizer.normalizeTemplateAsync(dirType, templateMeta),
|
normalizer.normalizeTemplateAsync(prenormMeta),
|
||||||
normalizer.normalizeTemplateAsync(dirType, templateMeta)
|
normalizer.normalizeTemplateAsync(prenormMeta)
|
||||||
])
|
])
|
||||||
.then((templates: CompileTemplateMetadata[]) => {
|
.then((templates: CompileTemplateMetadata[]) => {
|
||||||
expect(templates[0].template).toEqual('a');
|
expect(templates[0].template).toEqual('a');
|
||||||
|
@ -273,8 +281,13 @@ export function main() {
|
||||||
|
|
||||||
var viewEncapsulation = ViewEncapsulation.Native;
|
var viewEncapsulation = ViewEncapsulation.Native;
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType, new CompileTemplateMetadata(
|
{
|
||||||
{encapsulation: viewEncapsulation, styles: [], styleUrls: []}),
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
encapsulation: viewEncapsulation,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: []
|
||||||
|
},
|
||||||
'', 'package:some/module/');
|
'', 'package:some/module/');
|
||||||
expect(template.encapsulation).toBe(viewEncapsulation);
|
expect(template.encapsulation).toBe(viewEncapsulation);
|
||||||
}));
|
}));
|
||||||
|
@ -282,17 +295,27 @@ export function main() {
|
||||||
it('should keep the template as html',
|
it('should keep the template as html',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType,
|
{
|
||||||
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), 'a',
|
componentType: SomeComp,
|
||||||
'package:some/module/');
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
encapsulation: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: []
|
||||||
|
},
|
||||||
|
'a', 'package:some/module/');
|
||||||
expect(template.template).toEqual('a');
|
expect(template.template).toEqual('a');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should collect ngContent',
|
it('should collect ngContent',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType,
|
{
|
||||||
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
encapsulation: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: []
|
||||||
|
},
|
||||||
'<ng-content select="a"></ng-content>', 'package:some/module/');
|
'<ng-content select="a"></ng-content>', 'package:some/module/');
|
||||||
expect(template.ngContentSelectors).toEqual(['a']);
|
expect(template.ngContentSelectors).toEqual(['a']);
|
||||||
}));
|
}));
|
||||||
|
@ -300,8 +323,13 @@ export function main() {
|
||||||
it('should normalize ngContent wildcard selector',
|
it('should normalize ngContent wildcard selector',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType,
|
{
|
||||||
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
encapsulation: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: []
|
||||||
|
},
|
||||||
'<ng-content></ng-content><ng-content select></ng-content><ng-content select="*"></ng-content>',
|
'<ng-content></ng-content><ng-content select></ng-content><ng-content select="*"></ng-content>',
|
||||||
'package:some/module/');
|
'package:some/module/');
|
||||||
expect(template.ngContentSelectors).toEqual(['*', '*', '*']);
|
expect(template.ngContentSelectors).toEqual(['*', '*', '*']);
|
||||||
|
@ -310,8 +338,13 @@ export function main() {
|
||||||
it('should collect top level styles in the template',
|
it('should collect top level styles in the template',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType,
|
{
|
||||||
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
encapsulation: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: []
|
||||||
|
},
|
||||||
'<style>a</style>', 'package:some/module/');
|
'<style>a</style>', 'package:some/module/');
|
||||||
expect(template.styles).toEqual(['a']);
|
expect(template.styles).toEqual(['a']);
|
||||||
}));
|
}));
|
||||||
|
@ -319,8 +352,13 @@ export function main() {
|
||||||
it('should collect styles inside in elements',
|
it('should collect styles inside in elements',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType,
|
{
|
||||||
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
encapsulation: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: []
|
||||||
|
},
|
||||||
'<div><style>a</style></div>', 'package:some/module/');
|
'<div><style>a</style></div>', 'package:some/module/');
|
||||||
expect(template.styles).toEqual(['a']);
|
expect(template.styles).toEqual(['a']);
|
||||||
}));
|
}));
|
||||||
|
@ -328,8 +366,13 @@ export function main() {
|
||||||
it('should collect styleUrls in the template',
|
it('should collect styleUrls in the template',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType,
|
{
|
||||||
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
encapsulation: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: []
|
||||||
|
},
|
||||||
'<link rel="stylesheet" href="aUrl">', 'package:some/module/');
|
'<link rel="stylesheet" href="aUrl">', 'package:some/module/');
|
||||||
expect(template.styleUrls).toEqual(['package:some/module/aUrl']);
|
expect(template.styleUrls).toEqual(['package:some/module/aUrl']);
|
||||||
}));
|
}));
|
||||||
|
@ -337,8 +380,13 @@ export function main() {
|
||||||
it('should collect styleUrls in elements',
|
it('should collect styleUrls in elements',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType,
|
{
|
||||||
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
encapsulation: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: []
|
||||||
|
},
|
||||||
'<div><link rel="stylesheet" href="aUrl"></div>', 'package:some/module/');
|
'<div><link rel="stylesheet" href="aUrl"></div>', 'package:some/module/');
|
||||||
expect(template.styleUrls).toEqual(['package:some/module/aUrl']);
|
expect(template.styleUrls).toEqual(['package:some/module/aUrl']);
|
||||||
}));
|
}));
|
||||||
|
@ -346,8 +394,13 @@ export function main() {
|
||||||
it('should ignore link elements with non stylesheet rel attribute',
|
it('should ignore link elements with non stylesheet rel attribute',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType,
|
{
|
||||||
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
encapsulation: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: []
|
||||||
|
},
|
||||||
'<link href="b" rel="a">', 'package:some/module/');
|
'<link href="b" rel="a">', 'package:some/module/');
|
||||||
expect(template.styleUrls).toEqual([]);
|
expect(template.styleUrls).toEqual([]);
|
||||||
}));
|
}));
|
||||||
|
@ -355,8 +408,13 @@ export function main() {
|
||||||
it('should ignore link elements with absolute urls but non package: scheme',
|
it('should ignore link elements with absolute urls but non package: scheme',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType,
|
{
|
||||||
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
encapsulation: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: []
|
||||||
|
},
|
||||||
'<link href="http://some/external.css" rel="stylesheet">', 'package:some/module/');
|
'<link href="http://some/external.css" rel="stylesheet">', 'package:some/module/');
|
||||||
expect(template.styleUrls).toEqual([]);
|
expect(template.styleUrls).toEqual([]);
|
||||||
}));
|
}));
|
||||||
|
@ -364,8 +422,13 @@ export function main() {
|
||||||
it('should extract @import style urls into styleAbsUrl',
|
it('should extract @import style urls into styleAbsUrl',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType, new CompileTemplateMetadata(
|
{
|
||||||
{encapsulation: null, styles: ['@import "test.css";'], styleUrls: []}),
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
encapsulation: null,
|
||||||
|
styles: ['@import "test.css";'],
|
||||||
|
styleUrls: []
|
||||||
|
},
|
||||||
'', 'package:some/module/id');
|
'', 'package:some/module/id');
|
||||||
expect(template.styles).toEqual(['']);
|
expect(template.styles).toEqual(['']);
|
||||||
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
|
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
|
||||||
|
@ -374,11 +437,13 @@ export function main() {
|
||||||
it('should not resolve relative urls in inline styles',
|
it('should not resolve relative urls in inline styles',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType, new CompileTemplateMetadata({
|
{
|
||||||
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
encapsulation: null,
|
encapsulation: null,
|
||||||
styles: ['.foo{background-image: url(\'double.jpg\');'],
|
styles: ['.foo{background-image: url(\'double.jpg\');'],
|
||||||
styleUrls: []
|
styleUrls: []
|
||||||
}),
|
},
|
||||||
'', 'package:some/module/id');
|
'', 'package:some/module/id');
|
||||||
expect(template.styles).toEqual(['.foo{background-image: url(\'double.jpg\');']);
|
expect(template.styles).toEqual(['.foo{background-image: url(\'double.jpg\');']);
|
||||||
}));
|
}));
|
||||||
|
@ -386,8 +451,13 @@ export function main() {
|
||||||
it('should resolve relative style urls in styleUrls',
|
it('should resolve relative style urls in styleUrls',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType, new CompileTemplateMetadata(
|
{
|
||||||
{encapsulation: null, styles: [], styleUrls: ['test.css']}),
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
encapsulation: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: ['test.css']
|
||||||
|
},
|
||||||
'', 'package:some/module/id');
|
'', 'package:some/module/id');
|
||||||
expect(template.styles).toEqual([]);
|
expect(template.styles).toEqual([]);
|
||||||
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
|
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
|
||||||
|
@ -396,8 +466,13 @@ export function main() {
|
||||||
it('should resolve relative style urls in styleUrls with http directive url',
|
it('should resolve relative style urls in styleUrls with http directive url',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirTypeWithHttpUrl, new CompileTemplateMetadata(
|
{
|
||||||
{encapsulation: null, styles: [], styleUrls: ['test.css']}),
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_HTTP_MODULE_URL,
|
||||||
|
encapsulation: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: ['test.css']
|
||||||
|
},
|
||||||
'', 'http://some/module/id');
|
'', 'http://some/module/id');
|
||||||
expect(template.styles).toEqual([]);
|
expect(template.styles).toEqual([]);
|
||||||
expect(template.styleUrls).toEqual(['http://some/module/test.css']);
|
expect(template.styleUrls).toEqual(['http://some/module/test.css']);
|
||||||
|
@ -406,8 +481,13 @@ export function main() {
|
||||||
it('should normalize ViewEncapsulation.Emulated to ViewEncapsulation.None if there are no styles nor stylesheets',
|
it('should normalize ViewEncapsulation.Emulated to ViewEncapsulation.None if there are no styles nor stylesheets',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType, new CompileTemplateMetadata(
|
{
|
||||||
{encapsulation: ViewEncapsulation.Emulated, styles: [], styleUrls: []}),
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
encapsulation: ViewEncapsulation.Emulated,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: []
|
||||||
|
},
|
||||||
'', 'package:some/module/id');
|
'', 'package:some/module/id');
|
||||||
expect(template.encapsulation).toEqual(ViewEncapsulation.None);
|
expect(template.encapsulation).toEqual(ViewEncapsulation.None);
|
||||||
}));
|
}));
|
||||||
|
@ -415,8 +495,13 @@ export function main() {
|
||||||
it('should ignore ng-content in elements with ngNonBindable',
|
it('should ignore ng-content in elements with ngNonBindable',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType,
|
{
|
||||||
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
encapsulation: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: []
|
||||||
|
},
|
||||||
'<div ngNonBindable><ng-content select="a"></ng-content></div>',
|
'<div ngNonBindable><ng-content select="a"></ng-content></div>',
|
||||||
'package:some/module/');
|
'package:some/module/');
|
||||||
expect(template.ngContentSelectors).toEqual([]);
|
expect(template.ngContentSelectors).toEqual([]);
|
||||||
|
@ -425,8 +510,13 @@ export function main() {
|
||||||
it('should still collect <style> in elements with ngNonBindable',
|
it('should still collect <style> in elements with ngNonBindable',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType,
|
{
|
||||||
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
componentType: SomeComp,
|
||||||
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
encapsulation: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: []
|
||||||
|
},
|
||||||
'<div ngNonBindable><style>div {color:red}</style></div>', 'package:some/module/');
|
'<div ngNonBindable><style>div {color:red}</style></div>', 'package:some/module/');
|
||||||
expect(template.styles).toEqual(['div {color:red}']);
|
expect(template.styles).toEqual(['div {color:red}']);
|
||||||
}));
|
}));
|
||||||
|
@ -444,3 +534,5 @@ function programResourceLoaderSpy(spy: SpyResourceLoader, results: {[key: string
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SomeComp {}
|
|
@ -9,10 +9,12 @@
|
||||||
import {TEST_COMPILER_PROVIDERS} from '@angular/compiler/testing/test_bindings';
|
import {TEST_COMPILER_PROVIDERS} from '@angular/compiler/testing/test_bindings';
|
||||||
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, Component, DoCheck, Injectable, NgModule, OnChanges, OnDestroy, OnInit, Pipe, SimpleChanges, ViewEncapsulation} from '@angular/core';
|
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, Component, DoCheck, Injectable, NgModule, OnChanges, OnDestroy, OnInit, Pipe, SimpleChanges, ViewEncapsulation} from '@angular/core';
|
||||||
import {LIFECYCLE_HOOKS_VALUES} from '@angular/core/src/metadata/lifecycle_hooks';
|
import {LIFECYCLE_HOOKS_VALUES} from '@angular/core/src/metadata/lifecycle_hooks';
|
||||||
import {TestBed, inject} from '@angular/core/testing';
|
import {TestBed, async, inject} from '@angular/core/testing';
|
||||||
|
|
||||||
import {stringify} from '../src/facade/lang';
|
import {stringify} from '../src/facade/lang';
|
||||||
import {CompileMetadataResolver} from '../src/metadata_resolver';
|
import {CompileMetadataResolver} from '../src/metadata_resolver';
|
||||||
|
import {ResourceLoader} from '../src/resource_loader';
|
||||||
|
import {MockResourceLoader} from '../testing/resource_loader_mock';
|
||||||
|
|
||||||
import {MalformedStylesComponent} from './metadata_resolver_fixture';
|
import {MalformedStylesComponent} from './metadata_resolver_fixture';
|
||||||
|
|
||||||
|
@ -20,15 +22,35 @@ export function main() {
|
||||||
describe('CompileMetadataResolver', () => {
|
describe('CompileMetadataResolver', () => {
|
||||||
beforeEach(() => { TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); });
|
beforeEach(() => { TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); });
|
||||||
|
|
||||||
describe('getDirectiveMetadata', () => {
|
it('should throw on the get... methods if the module has not been loaded yet',
|
||||||
it('should read metadata',
|
|
||||||
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||||
const meta = resolver.getDirectiveMetadata(ComponentWithEverything);
|
@NgModule({})
|
||||||
|
class SomeModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Pipe({name: 'pipe'})
|
||||||
|
class SomePipe {
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => resolver.getNgModuleMetadata(SomeModule)).toThrowError(/Illegal state/);
|
||||||
|
expect(() => resolver.getDirectiveMetadata(ComponentWithEverythingInline))
|
||||||
|
.toThrowError(/Illegal state/);
|
||||||
|
expect(() => resolver.getPipeMetadata(SomePipe)).toThrowError(/Illegal state/);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should read metadata in sync for components with inline resources',
|
||||||
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||||
|
@NgModule({declarations: [ComponentWithEverythingInline]})
|
||||||
|
class SomeModule {
|
||||||
|
}
|
||||||
|
resolver.loadNgModuleMetadata(SomeModule, true);
|
||||||
|
|
||||||
|
const meta = resolver.getDirectiveMetadata(ComponentWithEverythingInline);
|
||||||
expect(meta.selector).toEqual('someSelector');
|
expect(meta.selector).toEqual('someSelector');
|
||||||
expect(meta.exportAs).toEqual('someExportAs');
|
expect(meta.exportAs).toEqual('someExportAs');
|
||||||
expect(meta.isComponent).toBe(true);
|
expect(meta.isComponent).toBe(true);
|
||||||
expect(meta.type.reference).toBe(ComponentWithEverything);
|
expect(meta.type.reference).toBe(ComponentWithEverythingInline);
|
||||||
expect(meta.type.name).toEqual(stringify(ComponentWithEverything));
|
expect(meta.type.name).toEqual(stringify(ComponentWithEverythingInline));
|
||||||
expect(meta.type.lifecycleHooks).toEqual(LIFECYCLE_HOOKS_VALUES);
|
expect(meta.type.lifecycleHooks).toEqual(LIFECYCLE_HOOKS_VALUES);
|
||||||
expect(meta.changeDetection).toBe(ChangeDetectionStrategy.Default);
|
expect(meta.changeDetection).toBe(ChangeDetectionStrategy.Default);
|
||||||
expect(meta.inputs).toEqual({'someProp': 'someProp'});
|
expect(meta.inputs).toEqual({'someProp': 'someProp'});
|
||||||
|
@ -38,14 +60,67 @@ export function main() {
|
||||||
expect(meta.hostAttributes).toEqual({'someHostAttr': 'someHostAttrValue'});
|
expect(meta.hostAttributes).toEqual({'someHostAttr': 'someHostAttrValue'});
|
||||||
expect(meta.template.encapsulation).toBe(ViewEncapsulation.Emulated);
|
expect(meta.template.encapsulation).toBe(ViewEncapsulation.Emulated);
|
||||||
expect(meta.template.styles).toEqual(['someStyle']);
|
expect(meta.template.styles).toEqual(['someStyle']);
|
||||||
expect(meta.template.styleUrls).toEqual(['someStyleUrl']);
|
|
||||||
expect(meta.template.template).toEqual('someTemplate');
|
expect(meta.template.template).toEqual('someTemplate');
|
||||||
expect(meta.template.templateUrl).toEqual('someTemplateUrl');
|
|
||||||
expect(meta.template.interpolation).toEqual(['{{', '}}']);
|
expect(meta.template.interpolation).toEqual(['{{', '}}']);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should throw when reading metadata for component with external resources when sync=true is passed',
|
||||||
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||||
|
@NgModule({declarations: [ComponentWithExternalResources]})
|
||||||
|
class SomeModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => resolver.loadNgModuleMetadata(SomeModule, true))
|
||||||
|
.toThrowError(
|
||||||
|
`Can't compile synchronously as ${stringify(ComponentWithExternalResources)} is still being loaded!`);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should read external metadata when sync=false',
|
||||||
|
async(inject(
|
||||||
|
[CompileMetadataResolver, ResourceLoader],
|
||||||
|
(resolver: CompileMetadataResolver, resourceLoader: MockResourceLoader) => {
|
||||||
|
@NgModule({declarations: [ComponentWithExternalResources]})
|
||||||
|
class SomeModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceLoader.when('someTemplateUrl', 'someTemplate');
|
||||||
|
resolver.loadNgModuleMetadata(SomeModule, false).loading.then(() => {
|
||||||
|
const meta = resolver.getDirectiveMetadata(ComponentWithExternalResources);
|
||||||
|
expect(meta.selector).toEqual('someSelector');
|
||||||
|
expect(meta.template.styleUrls).toEqual(['someStyleUrl']);
|
||||||
|
expect(meta.template.templateUrl).toEqual('someTemplateUrl');
|
||||||
|
expect(meta.template.template).toEqual('someTemplate');
|
||||||
|
});
|
||||||
|
resourceLoader.flush();
|
||||||
|
})));
|
||||||
|
|
||||||
|
it('should wait for external resources of imported modules',
|
||||||
|
async(inject(
|
||||||
|
[CompileMetadataResolver, ResourceLoader],
|
||||||
|
(resolver: CompileMetadataResolver, resourceLoader: MockResourceLoader) => {
|
||||||
|
@NgModule({declarations: [ComponentWithExternalResources]})
|
||||||
|
class SomeImportedModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({imports: [SomeImportedModule]})
|
||||||
|
class SomeModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceLoader.when('someTemplateUrl', 'someTemplate');
|
||||||
|
resolver.loadNgModuleMetadata(SomeModule, false).loading.then(() => {
|
||||||
|
const meta = resolver.getDirectiveMetadata(ComponentWithExternalResources);
|
||||||
|
expect(meta.selector).toEqual('someSelector');
|
||||||
|
});
|
||||||
|
resourceLoader.flush();
|
||||||
|
})));
|
||||||
|
|
||||||
it('should use the moduleUrl from the reflector if none is given',
|
it('should use the moduleUrl from the reflector if none is given',
|
||||||
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||||
|
@NgModule({declarations: [ComponentWithoutModuleId]})
|
||||||
|
class SomeModule {
|
||||||
|
}
|
||||||
|
resolver.loadNgModuleMetadata(SomeModule, true);
|
||||||
|
|
||||||
const value: string =
|
const value: string =
|
||||||
resolver.getDirectiveMetadata(ComponentWithoutModuleId).type.moduleUrl;
|
resolver.getDirectiveMetadata(ComponentWithoutModuleId).type.moduleUrl;
|
||||||
const expectedEndValue = './ComponentWithoutModuleId';
|
const expectedEndValue = './ComponentWithoutModuleId';
|
||||||
|
@ -54,7 +129,11 @@ export function main() {
|
||||||
|
|
||||||
it('should throw when the moduleId is not a string',
|
it('should throw when the moduleId is not a string',
|
||||||
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||||
expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidModuleId))
|
@NgModule({declarations: [ComponentWithInvalidModuleId]})
|
||||||
|
class SomeModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => resolver.loadNgModuleMetadata(SomeModule, true))
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
`moduleId should be a string in "ComponentWithInvalidModuleId". See` +
|
`moduleId should be a string in "ComponentWithInvalidModuleId". See` +
|
||||||
` https://goo.gl/wIDDiL for more information.\n` +
|
` https://goo.gl/wIDDiL for more information.\n` +
|
||||||
|
@ -65,13 +144,21 @@ export function main() {
|
||||||
|
|
||||||
it('should throw when metadata is incorrectly typed',
|
it('should throw when metadata is incorrectly typed',
|
||||||
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||||
expect(() => resolver.getDirectiveMetadata(MalformedStylesComponent))
|
@NgModule({declarations: [MalformedStylesComponent]})
|
||||||
|
class SomeModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => resolver.loadNgModuleMetadata(SomeModule, true))
|
||||||
.toThrowError(`Expected 'styles' to be an array of strings.`);
|
.toThrowError(`Expected 'styles' to be an array of strings.`);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should throw with descriptive error message when provider token can not be resolved',
|
it('should throw with descriptive error message when provider token can not be resolved',
|
||||||
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||||
expect(() => resolver.getDirectiveMetadata(MyBrokenComp1))
|
@NgModule({declarations: [MyBrokenComp1]})
|
||||||
|
class SomeModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => resolver.loadNgModuleMetadata(SomeModule, true))
|
||||||
.toThrowError(`Can't resolve all parameters for MyBrokenComp1: (?).`);
|
.toThrowError(`Can't resolve all parameters for MyBrokenComp1: (?).`);
|
||||||
}));
|
}));
|
||||||
it('should throw with descriptive error message when a directive is passed to imports',
|
it('should throw with descriptive error message when a directive is passed to imports',
|
||||||
|
@ -79,7 +166,7 @@ export function main() {
|
||||||
@NgModule({imports: [ComponentWithoutModuleId]})
|
@NgModule({imports: [ComponentWithoutModuleId]})
|
||||||
class ModuleWithImportedComponent {
|
class ModuleWithImportedComponent {
|
||||||
}
|
}
|
||||||
expect(() => resolver.getNgModuleMetadata(ModuleWithImportedComponent))
|
expect(() => resolver.loadNgModuleMetadata(ModuleWithImportedComponent, true))
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
`Unexpected directive 'ComponentWithoutModuleId' imported by the module 'ModuleWithImportedComponent'`);
|
`Unexpected directive 'ComponentWithoutModuleId' imported by the module 'ModuleWithImportedComponent'`);
|
||||||
}));
|
}));
|
||||||
|
@ -92,7 +179,7 @@ export function main() {
|
||||||
@NgModule({imports: [SomePipe]})
|
@NgModule({imports: [SomePipe]})
|
||||||
class ModuleWithImportedPipe {
|
class ModuleWithImportedPipe {
|
||||||
}
|
}
|
||||||
expect(() => resolver.getNgModuleMetadata(ModuleWithImportedPipe))
|
expect(() => resolver.loadNgModuleMetadata(ModuleWithImportedPipe, true))
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
`Unexpected pipe 'SomePipe' imported by the module 'ModuleWithImportedPipe'`);
|
`Unexpected pipe 'SomePipe' imported by the module 'ModuleWithImportedPipe'`);
|
||||||
}));
|
}));
|
||||||
|
@ -105,7 +192,7 @@ export function main() {
|
||||||
@NgModule({declarations: [SomeModule]})
|
@NgModule({declarations: [SomeModule]})
|
||||||
class ModuleWithDeclaredModule {
|
class ModuleWithDeclaredModule {
|
||||||
}
|
}
|
||||||
expect(() => resolver.getNgModuleMetadata(ModuleWithDeclaredModule))
|
expect(() => resolver.loadNgModuleMetadata(ModuleWithDeclaredModule, true))
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
`Unexpected module 'SomeModule' declared by the module 'ModuleWithDeclaredModule'`);
|
`Unexpected module 'SomeModule' declared by the module 'ModuleWithDeclaredModule'`);
|
||||||
}));
|
}));
|
||||||
|
@ -115,7 +202,7 @@ export function main() {
|
||||||
@NgModule({declarations: [null]})
|
@NgModule({declarations: [null]})
|
||||||
class ModuleWithNullDeclared {
|
class ModuleWithNullDeclared {
|
||||||
}
|
}
|
||||||
expect(() => resolver.getNgModuleMetadata(ModuleWithNullDeclared))
|
expect(() => resolver.loadNgModuleMetadata(ModuleWithNullDeclared, true))
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
`Unexpected value 'null' declared by the module 'ModuleWithNullDeclared'`);
|
`Unexpected value 'null' declared by the module 'ModuleWithNullDeclared'`);
|
||||||
}));
|
}));
|
||||||
|
@ -125,7 +212,7 @@ export function main() {
|
||||||
@NgModule({imports: [null]})
|
@NgModule({imports: [null]})
|
||||||
class ModuleWithNullImported {
|
class ModuleWithNullImported {
|
||||||
}
|
}
|
||||||
expect(() => resolver.getNgModuleMetadata(ModuleWithNullImported))
|
expect(() => resolver.loadNgModuleMetadata(ModuleWithNullImported, true))
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
`Unexpected value 'null' imported by the module 'ModuleWithNullImported'`);
|
`Unexpected value 'null' imported by the module 'ModuleWithNullImported'`);
|
||||||
}));
|
}));
|
||||||
|
@ -133,20 +220,32 @@ export function main() {
|
||||||
|
|
||||||
it('should throw with descriptive error message when a param token of a dependency is undefined',
|
it('should throw with descriptive error message when a param token of a dependency is undefined',
|
||||||
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||||
expect(() => resolver.getDirectiveMetadata(MyBrokenComp2))
|
@NgModule({declarations: [MyBrokenComp2]})
|
||||||
|
class SomeModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => resolver.loadNgModuleMetadata(SomeModule, true))
|
||||||
.toThrowError(`Can't resolve all parameters for NonAnnotatedService: (?).`);
|
.toThrowError(`Can't resolve all parameters for NonAnnotatedService: (?).`);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should throw with descriptive error message when one of providers is not present',
|
it('should throw with descriptive error message when one of providers is not present',
|
||||||
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||||
expect(() => resolver.getDirectiveMetadata(MyBrokenComp3))
|
@NgModule({declarations: [MyBrokenComp3]})
|
||||||
|
class SomeModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => resolver.loadNgModuleMetadata(SomeModule, true))
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
`Invalid providers for "MyBrokenComp3" - only instances of Provider and Type are allowed, got: [SimpleService, ?null?, ...]`);
|
`Invalid providers for "MyBrokenComp3" - only instances of Provider and Type are allowed, got: [SimpleService, ?null?, ...]`);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should throw with descriptive error message when one of viewProviders is not present',
|
it('should throw with descriptive error message when one of viewProviders is not present',
|
||||||
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||||
expect(() => resolver.getDirectiveMetadata(MyBrokenComp4))
|
@NgModule({declarations: [MyBrokenComp4]})
|
||||||
|
class SomeModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => resolver.loadNgModuleMetadata(SomeModule, true))
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
`Invalid viewProviders for "MyBrokenComp4" - only instances of Provider and Type are allowed, got: [?null?, ...]`);
|
`Invalid viewProviders for "MyBrokenComp4" - only instances of Provider and Type are allowed, got: [?null?, ...]`);
|
||||||
}));
|
}));
|
||||||
|
@ -160,25 +259,49 @@ export function main() {
|
||||||
class ModuleWithUndefinedBootstrap {
|
class ModuleWithUndefinedBootstrap {
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(() => resolver.getNgModuleMetadata(ModuleWithNullBootstrap))
|
expect(() => resolver.loadNgModuleMetadata(ModuleWithNullBootstrap, true))
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
`Unexpected value 'null' used in the bootstrap property of module 'ModuleWithNullBootstrap'`);
|
`Unexpected value 'null' used in the bootstrap property of module 'ModuleWithNullBootstrap'`);
|
||||||
expect(() => resolver.getNgModuleMetadata(ModuleWithUndefinedBootstrap))
|
expect(() => resolver.loadNgModuleMetadata(ModuleWithUndefinedBootstrap, true))
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
`Unexpected value 'undefined' used in the bootstrap property of module 'ModuleWithUndefinedBootstrap'`);
|
`Unexpected value 'undefined' used in the bootstrap property of module 'ModuleWithUndefinedBootstrap'`);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should throw an error when the interpolation config has invalid symbols',
|
it('should throw an error when the interpolation config has invalid symbols',
|
||||||
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||||
expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidInterpolation1))
|
@NgModule({declarations: [ComponentWithInvalidInterpolation1]})
|
||||||
|
class Module1 {
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => resolver.loadNgModuleMetadata(Module1, true))
|
||||||
.toThrowError(`[' ', ' '] contains unusable interpolation symbol.`);
|
.toThrowError(`[' ', ' '] contains unusable interpolation symbol.`);
|
||||||
expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidInterpolation2))
|
|
||||||
|
@NgModule({declarations: [ComponentWithInvalidInterpolation2]})
|
||||||
|
class Module2 {
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => resolver.loadNgModuleMetadata(Module2, true))
|
||||||
.toThrowError(`['{', '}'] contains unusable interpolation symbol.`);
|
.toThrowError(`['{', '}'] contains unusable interpolation symbol.`);
|
||||||
expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidInterpolation3))
|
|
||||||
|
@NgModule({declarations: [ComponentWithInvalidInterpolation3]})
|
||||||
|
class Module3 {
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => resolver.loadNgModuleMetadata(Module3, true))
|
||||||
.toThrowError(`['<%', '%>'] contains unusable interpolation symbol.`);
|
.toThrowError(`['<%', '%>'] contains unusable interpolation symbol.`);
|
||||||
expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidInterpolation4))
|
|
||||||
|
@NgModule({declarations: [ComponentWithInvalidInterpolation4]})
|
||||||
|
class Module4 {
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => resolver.loadNgModuleMetadata(Module4, true))
|
||||||
.toThrowError(`['&#', '}}'] contains unusable interpolation symbol.`);
|
.toThrowError(`['&#', '}}'] contains unusable interpolation symbol.`);
|
||||||
expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidInterpolation5))
|
|
||||||
|
@NgModule({declarations: [ComponentWithInvalidInterpolation5]})
|
||||||
|
class Module5 {
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => resolver.loadNgModuleMetadata(Module5, true))
|
||||||
.toThrowError(`['{', '}}'] contains unusable interpolation symbol.`);
|
.toThrowError(`['{', '}}'] contains unusable interpolation symbol.`);
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
@ -194,12 +317,10 @@ export function main() {
|
||||||
class MyModule {
|
class MyModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
const modMeta = resolver.getNgModuleMetadata(MyModule);
|
const modMeta = resolver.loadNgModuleMetadata(MyModule, true).ngModule;
|
||||||
expect(modMeta.declaredDirectives.length).toBe(1);
|
expect(modMeta.declaredDirectives.length).toBe(1);
|
||||||
expect(modMeta.declaredDirectives[0].type.reference).toBe(MyComp);
|
expect(modMeta.declaredDirectives[0].reference).toBe(MyComp);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({selector: 'someComponent', template: ''})
|
@Component({selector: 'someComponent', template: ''})
|
||||||
|
@ -210,6 +331,14 @@ class ComponentWithoutModuleId {
|
||||||
class ComponentWithInvalidModuleId {
|
class ComponentWithInvalidModuleId {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'someSelector',
|
||||||
|
templateUrl: 'someTemplateUrl',
|
||||||
|
styleUrls: ['someStyleUrl'],
|
||||||
|
})
|
||||||
|
class ComponentWithExternalResources {
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'someSelector',
|
selector: 'someSelector',
|
||||||
inputs: ['someProp'],
|
inputs: ['someProp'],
|
||||||
|
@ -223,13 +352,11 @@ class ComponentWithInvalidModuleId {
|
||||||
moduleId: 'someModuleId',
|
moduleId: 'someModuleId',
|
||||||
changeDetection: ChangeDetectionStrategy.Default,
|
changeDetection: ChangeDetectionStrategy.Default,
|
||||||
template: 'someTemplate',
|
template: 'someTemplate',
|
||||||
templateUrl: 'someTemplateUrl',
|
|
||||||
encapsulation: ViewEncapsulation.Emulated,
|
encapsulation: ViewEncapsulation.Emulated,
|
||||||
styles: ['someStyle'],
|
styles: ['someStyle'],
|
||||||
styleUrls: ['someStyleUrl'],
|
|
||||||
interpolation: ['{{', '}}']
|
interpolation: ['{{', '}}']
|
||||||
})
|
})
|
||||||
class ComponentWithEverything implements OnChanges,
|
class ComponentWithEverythingInline implements OnChanges,
|
||||||
OnInit, DoCheck, OnDestroy, AfterContentInit, AfterContentChecked, AfterViewInit,
|
OnInit, DoCheck, OnDestroy, AfterContentInit, AfterContentChecked, AfterViewInit,
|
||||||
AfterViewChecked {
|
AfterViewChecked {
|
||||||
ngOnChanges(changes: SimpleChanges): void {}
|
ngOnChanges(changes: SimpleChanges): void {}
|
||||||
|
|
|
@ -8,8 +8,7 @@
|
||||||
|
|
||||||
import {DirectiveResolver, ResourceLoader} from '@angular/compiler';
|
import {DirectiveResolver, ResourceLoader} from '@angular/compiler';
|
||||||
import {Compiler, Component, Injector, NgModule, NgModuleFactory} from '@angular/core';
|
import {Compiler, Component, Injector, NgModule, NgModuleFactory} from '@angular/core';
|
||||||
import {TestBed, async, fakeAsync, tick} from '@angular/core/testing';
|
import {TestBed, async, fakeAsync, inject, tick} from '@angular/core/testing';
|
||||||
import {beforeEach, describe, inject, it} from '@angular/core/testing/testing_internal';
|
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
|
|
||||||
import {stringify} from '../src/facade/lang';
|
import {stringify} from '../src/facade/lang';
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {ApplicationRef, destroyPlatform} from '@angular/core/src/application_ref
|
||||||
import {Console} from '@angular/core/src/console';
|
import {Console} from '@angular/core/src/console';
|
||||||
import {ComponentRef} from '@angular/core/src/linker/component_factory';
|
import {ComponentRef} from '@angular/core/src/linker/component_factory';
|
||||||
import {Testability, TestabilityRegistry} from '@angular/core/src/testability/testability';
|
import {Testability, TestabilityRegistry} from '@angular/core/src/testability/testability';
|
||||||
import {AsyncTestCompleter, Log, afterEach, beforeEach, beforeEachProviders, describe, inject, it} from '@angular/core/testing/testing_internal';
|
import {AsyncTestCompleter, Log, afterEach, beforeEach, beforeEachProviders, describe, iit, inject, it} from '@angular/core/testing/testing_internal';
|
||||||
import {BrowserModule} from '@angular/platform-browser';
|
import {BrowserModule} from '@angular/platform-browser';
|
||||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
@ -149,11 +149,19 @@ export function main() {
|
||||||
|
|
||||||
afterEach(destroyPlatform);
|
afterEach(destroyPlatform);
|
||||||
|
|
||||||
it('should throw if bootstrapped Directive is not a Component', () => {
|
it('should throw if bootstrapped Directive is not a Component',
|
||||||
expect(() => bootstrap(HelloRootDirectiveIsNotCmp))
|
inject([AsyncTestCompleter], (done: AsyncTestCompleter) => {
|
||||||
.toThrowError(
|
var logger = new MockConsole();
|
||||||
|
var errorHandler = new ErrorHandler(false);
|
||||||
|
errorHandler._console = logger as any;
|
||||||
|
bootstrap(HelloRootDirectiveIsNotCmp, [
|
||||||
|
{provide: ErrorHandler, useValue: errorHandler}
|
||||||
|
]).catch((e) => {
|
||||||
|
expect(e.message).toBe(
|
||||||
`Could not compile '${stringify(HelloRootDirectiveIsNotCmp)}' because it is not a component.`);
|
`Could not compile '${stringify(HelloRootDirectiveIsNotCmp)}' because it is not a component.`);
|
||||||
|
done.done();
|
||||||
});
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should throw if no element is found',
|
it('should throw if no element is found',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
|
|
Loading…
Reference in New Issue