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 => { |  | ||||||
|                 const html = compMeta.template.template; |  | ||||||
|                 const interpolationConfig = |  | ||||||
|                     compiler.InterpolationConfig.fromArray(compMeta.template.interpolation); |  | ||||||
|                 errors.push(...this.messageBundle.updateFromTemplate( |  | ||||||
|                     html, file.srcUrl, interpolationConfig)); |  | ||||||
|               }); |  | ||||||
|             }); |             }); | ||||||
|  |             compMetas.forEach(compMeta => { | ||||||
|  |               const html = compMeta.template.template; | ||||||
|  |               const interpolationConfig = | ||||||
|  |                   compiler.InterpolationConfig.fromArray(compMeta.template.interpolation); | ||||||
|  |               errors.push( | ||||||
|  |                   ...this.messageBundle.updateFromTemplate(html, file.srcUrl, interpolationConfig)); | ||||||
|  |             }); | ||||||
|  |           }); | ||||||
| 
 | 
 | ||||||
|         filePromises.push(done); |           if (errors.length) { | ||||||
|       } |             throw new Error(errors.map(e => e.toString()).join('\n')); | ||||||
|     }); |           } | ||||||
| 
 | 
 | ||||||
| 
 |           return this.messageBundle; | ||||||
|     if (errors.length) { |         }); | ||||||
|       throw new Error(errors.map(e => e.toString()).join('\n')); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return Promise.all(filePromises).then(_ => 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) { |     if (!dirMeta) { | ||||||
|       const dirMeta = this._directiveResolver.resolve(directiveType, throwIfNotFound); |       return null; | ||||||
|       if (!dirMeta) { |     } | ||||||
|         return null; |     let moduleUrl = staticTypeModuleUrl(directiveType); | ||||||
|       } | 
 | ||||||
|       let templateMeta: cpl.CompileTemplateMetadata = null; |     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,189 +205,278 @@ export class CompileMetadataResolver { | |||||||
|         entryComponents: entryComponentMetadata |         entryComponents: entryComponentMetadata | ||||||
|       }); |       }); | ||||||
|       this._directiveCache.set(directiveType, meta); |       this._directiveCache.set(directiveType, 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; | ||||||
|     } |     } | ||||||
|     return meta; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   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) { | ||||||
|       const meta = this._ngModuleResolver.resolve(moduleType, throwIfNotFound); |       return compileMeta; | ||||||
|       if (!meta) { |  | ||||||
|         return null; |  | ||||||
|       } |  | ||||||
|       const declaredDirectives: cpl.CompileDirectiveMetadata[] = []; |  | ||||||
|       const exportedDirectives: cpl.CompileDirectiveMetadata[] = []; |  | ||||||
|       const declaredPipes: cpl.CompilePipeMetadata[] = []; |  | ||||||
|       const exportedPipes: cpl.CompilePipeMetadata[] = []; |  | ||||||
|       const importedModules: cpl.CompileNgModuleMetadata[] = []; |  | ||||||
|       const exportedModules: cpl.CompileNgModuleMetadata[] = []; |  | ||||||
|       const providers: any[] = []; |  | ||||||
|       const entryComponents: cpl.CompileTypeMetadata[] = []; |  | ||||||
|       const bootstrapComponents: cpl.CompileTypeMetadata[] = []; |  | ||||||
|       const schemas: SchemaMetadata[] = []; |  | ||||||
| 
 |  | ||||||
|       if (meta.imports) { |  | ||||||
|         flattenAndDedupeArray(meta.imports).forEach((importedType) => { |  | ||||||
|           let importedModuleType: Type<any>; |  | ||||||
|           if (isValidType(importedType)) { |  | ||||||
|             importedModuleType = importedType; |  | ||||||
|           } else if (importedType && importedType.ngModule) { |  | ||||||
|             const moduleWithProviders: ModuleWithProviders = importedType; |  | ||||||
|             importedModuleType = moduleWithProviders.ngModule; |  | ||||||
|             if (moduleWithProviders.providers) { |  | ||||||
|               providers.push(...this.getProvidersMetadata( |  | ||||||
|                   moduleWithProviders.providers, entryComponents, |  | ||||||
|                   `provider for the NgModule '${stringify(importedModuleType)}'`)); |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
| 
 |  | ||||||
|           if (importedModuleType) { |  | ||||||
|             const importedMeta = this.getNgModuleMetadata(importedModuleType, false); |  | ||||||
|             if (importedMeta === null) { |  | ||||||
|               throw new Error( |  | ||||||
|                   `Unexpected ${this._getTypeDescriptor(importedType)} '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`); |  | ||||||
|             } |  | ||||||
|             importedModules.push(importedMeta); |  | ||||||
|           } else { |  | ||||||
|             throw new Error( |  | ||||||
|                 `Unexpected value '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`); |  | ||||||
|           } |  | ||||||
|         }); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       if (meta.exports) { |  | ||||||
|         flattenAndDedupeArray(meta.exports).forEach((exportedType) => { |  | ||||||
|           if (!isValidType(exportedType)) { |  | ||||||
|             throw new Error( |  | ||||||
|                 `Unexpected value '${stringify(exportedType)}' exported by the module '${stringify(moduleType)}'`); |  | ||||||
|           } |  | ||||||
|           let exportedDirMeta: cpl.CompileDirectiveMetadata; |  | ||||||
|           let exportedPipeMeta: cpl.CompilePipeMetadata; |  | ||||||
|           let exportedModuleMeta: cpl.CompileNgModuleMetadata; |  | ||||||
|           if (exportedDirMeta = this.getDirectiveMetadata(exportedType, false)) { |  | ||||||
|             exportedDirectives.push(exportedDirMeta); |  | ||||||
|           } else if (exportedPipeMeta = this.getPipeMetadata(exportedType, false)) { |  | ||||||
|             exportedPipes.push(exportedPipeMeta); |  | ||||||
|           } else if (exportedModuleMeta = this.getNgModuleMetadata(exportedType, false)) { |  | ||||||
|             exportedModules.push(exportedModuleMeta); |  | ||||||
|           } else { |  | ||||||
|             throw new Error( |  | ||||||
|                 `Unexpected ${this._getTypeDescriptor(exportedType)} '${stringify(exportedType)}' exported by the module '${stringify(moduleType)}'`); |  | ||||||
|           } |  | ||||||
|         }); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       // Note: This will be modified later, so we rely on
 |  | ||||||
|       // getting a new instance every time!
 |  | ||||||
|       const transitiveModule = |  | ||||||
|           this._getTransitiveNgModuleMetadata(importedModules, exportedModules); |  | ||||||
|       if (meta.declarations) { |  | ||||||
|         flattenAndDedupeArray(meta.declarations).forEach((declaredType) => { |  | ||||||
|           if (!isValidType(declaredType)) { |  | ||||||
|             throw new Error( |  | ||||||
|                 `Unexpected value '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`); |  | ||||||
|           } |  | ||||||
|           let declaredDirMeta: cpl.CompileDirectiveMetadata; |  | ||||||
|           let declaredPipeMeta: cpl.CompilePipeMetadata; |  | ||||||
|           if (declaredDirMeta = this.getDirectiveMetadata(declaredType, false)) { |  | ||||||
|             this._addDirectiveToModule( |  | ||||||
|                 declaredDirMeta, moduleType, transitiveModule, declaredDirectives, true); |  | ||||||
|           } else if (declaredPipeMeta = this.getPipeMetadata(declaredType, false)) { |  | ||||||
|             this._addPipeToModule( |  | ||||||
|                 declaredPipeMeta, moduleType, transitiveModule, declaredPipes, true); |  | ||||||
|           } else { |  | ||||||
|             throw new Error( |  | ||||||
|                 `Unexpected ${this._getTypeDescriptor(declaredType)} '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`); |  | ||||||
|           } |  | ||||||
|         }); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       // The providers of the module have to go last
 |  | ||||||
|       // so that they overwrite any other provider we already added.
 |  | ||||||
|       if (meta.providers) { |  | ||||||
|         providers.push(...this.getProvidersMetadata( |  | ||||||
|             meta.providers, entryComponents, |  | ||||||
|             `provider for the NgModule '${stringify(moduleType)}'`)); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       if (meta.entryComponents) { |  | ||||||
|         entryComponents.push( |  | ||||||
|             ...flattenAndDedupeArray(meta.entryComponents) |  | ||||||
|                 .map(type => this.getTypeMetadata(type, staticTypeModuleUrl(type)))); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       if (meta.bootstrap) { |  | ||||||
|         const typeMetadata = flattenAndDedupeArray(meta.bootstrap).map(type => { |  | ||||||
|           if (!isValidType(type)) { |  | ||||||
|             throw new Error( |  | ||||||
|                 `Unexpected value '${stringify(type)}' used in the bootstrap property of module '${stringify(moduleType)}'`); |  | ||||||
|           } |  | ||||||
|           return this.getTypeMetadata(type, staticTypeModuleUrl(type)); |  | ||||||
|         }); |  | ||||||
|         bootstrapComponents.push(...typeMetadata); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       entryComponents.push(...bootstrapComponents); |  | ||||||
| 
 |  | ||||||
|       if (meta.schemas) { |  | ||||||
|         schemas.push(...flattenAndDedupeArray(meta.schemas)); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       transitiveModule.entryComponents.push(...entryComponents); |  | ||||||
|       transitiveModule.providers.push(...providers); |  | ||||||
| 
 |  | ||||||
|       compileMeta = new cpl.CompileNgModuleMetadata({ |  | ||||||
|         type: this.getTypeMetadata(moduleType, staticTypeModuleUrl(moduleType)), |  | ||||||
|         providers, |  | ||||||
|         entryComponents, |  | ||||||
|         bootstrapComponents, |  | ||||||
|         schemas, |  | ||||||
|         declaredDirectives, |  | ||||||
|         exportedDirectives, |  | ||||||
|         declaredPipes, |  | ||||||
|         exportedPipes, |  | ||||||
|         importedModules, |  | ||||||
|         exportedModules, |  | ||||||
|         transitiveModule, |  | ||||||
|         id: meta.id, |  | ||||||
|       }); |  | ||||||
| 
 |  | ||||||
|       transitiveModule.modules.push(compileMeta); |  | ||||||
|       this._verifyModule(compileMeta); |  | ||||||
|       this._ngModuleCache.set(moduleType, compileMeta); |  | ||||||
|     } |     } | ||||||
|  |     const meta = this._ngModuleResolver.resolve(moduleType, throwIfNotFound); | ||||||
|  |     if (!meta) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     const declaredDirectives: cpl.CompileIdentifierMetadata[] = []; | ||||||
|  |     const exportedDirectives: cpl.CompileIdentifierMetadata[] = []; | ||||||
|  |     const declaredPipes: cpl.CompileIdentifierMetadata[] = []; | ||||||
|  |     const exportedPipes: cpl.CompileIdentifierMetadata[] = []; | ||||||
|  |     const importedModules: cpl.CompileNgModuleMetadata[] = []; | ||||||
|  |     const exportedModules: cpl.CompileNgModuleMetadata[] = []; | ||||||
|  |     const providers: any[] = []; | ||||||
|  |     const entryComponents: cpl.CompileIdentifierMetadata[] = []; | ||||||
|  |     const bootstrapComponents: cpl.CompileIdentifierMetadata[] = []; | ||||||
|  |     const schemas: SchemaMetadata[] = []; | ||||||
|  | 
 | ||||||
|  |     if (meta.imports) { | ||||||
|  |       flattenAndDedupeArray(meta.imports).forEach((importedType) => { | ||||||
|  |         let importedModuleType: Type<any>; | ||||||
|  |         if (isValidType(importedType)) { | ||||||
|  |           importedModuleType = importedType; | ||||||
|  |         } else if (importedType && importedType.ngModule) { | ||||||
|  |           const moduleWithProviders: ModuleWithProviders = importedType; | ||||||
|  |           importedModuleType = moduleWithProviders.ngModule; | ||||||
|  |           if (moduleWithProviders.providers) { | ||||||
|  |             providers.push(...this._getProvidersMetadata( | ||||||
|  |                 moduleWithProviders.providers, entryComponents, | ||||||
|  |                 `provider for the NgModule '${stringify(importedModuleType)}'`)); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (importedModuleType) { | ||||||
|  |           const importedMeta = this._loadNgModuleMetadata(importedModuleType, isSync, false); | ||||||
|  |           if (importedMeta === null) { | ||||||
|  |             throw new Error( | ||||||
|  |                 `Unexpected ${this._getTypeDescriptor(importedType)} '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`); | ||||||
|  |           } | ||||||
|  |           importedModules.push(importedMeta); | ||||||
|  |         } else { | ||||||
|  |           throw new Error( | ||||||
|  |               `Unexpected value '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (meta.exports) { | ||||||
|  |       flattenAndDedupeArray(meta.exports).forEach((exportedType) => { | ||||||
|  |         if (!isValidType(exportedType)) { | ||||||
|  |           throw new Error( | ||||||
|  |               `Unexpected value '${stringify(exportedType)}' exported by the module '${stringify(moduleType)}'`); | ||||||
|  |         } | ||||||
|  |         let identifier = | ||||||
|  |             this._getIdentifierMetadata(exportedType, staticTypeModuleUrl(exportedType)); | ||||||
|  |         let exportedModuleMeta: cpl.CompileNgModuleMetadata; | ||||||
|  |         if (this._directiveResolver.isDirective(exportedType)) { | ||||||
|  |           exportedDirectives.push(identifier); | ||||||
|  |         } else if (this._pipeResolver.isPipe(exportedType)) { | ||||||
|  |           exportedPipes.push(identifier); | ||||||
|  |         } else if (exportedModuleMeta = this._loadNgModuleMetadata(exportedType, isSync, false)) { | ||||||
|  |           exportedModules.push(exportedModuleMeta); | ||||||
|  |         } else { | ||||||
|  |           throw new Error( | ||||||
|  |               `Unexpected ${this._getTypeDescriptor(exportedType)} '${stringify(exportedType)}' exported by the module '${stringify(moduleType)}'`); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Note: This will be modified later, so we rely on
 | ||||||
|  |     // getting a new instance every time!
 | ||||||
|  |     const transitiveModule = this._getTransitiveNgModuleMetadata(importedModules, exportedModules); | ||||||
|  |     if (meta.declarations) { | ||||||
|  |       flattenAndDedupeArray(meta.declarations).forEach((declaredType) => { | ||||||
|  |         if (!isValidType(declaredType)) { | ||||||
|  |           throw new Error( | ||||||
|  |               `Unexpected value '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`); | ||||||
|  |         } | ||||||
|  |         const declaredIdentifier = | ||||||
|  |             this._getIdentifierMetadata(declaredType, staticTypeModuleUrl(declaredType)); | ||||||
|  |         if (this._directiveResolver.isDirective(declaredType)) { | ||||||
|  |           transitiveModule.directivesSet.add(declaredType); | ||||||
|  |           transitiveModule.directives.push(declaredIdentifier); | ||||||
|  |           declaredDirectives.push(declaredIdentifier); | ||||||
|  |           this._addTypeToModule(declaredType, moduleType); | ||||||
|  |           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 { | ||||||
|  |           throw new Error( | ||||||
|  |               `Unexpected ${this._getTypeDescriptor(declaredType)} '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // The providers of the module have to go last
 | ||||||
|  |     // so that they overwrite any other provider we already added.
 | ||||||
|  |     if (meta.providers) { | ||||||
|  |       providers.push(...this._getProvidersMetadata( | ||||||
|  |           meta.providers, entryComponents, `provider for the NgModule '${stringify(moduleType)}'`)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (meta.entryComponents) { | ||||||
|  |       entryComponents.push( | ||||||
|  |           ...flattenAndDedupeArray(meta.entryComponents) | ||||||
|  |               .map(type => this._getTypeMetadata(type, staticTypeModuleUrl(type)))); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (meta.bootstrap) { | ||||||
|  |       const typeMetadata = flattenAndDedupeArray(meta.bootstrap).map(type => { | ||||||
|  |         if (!isValidType(type)) { | ||||||
|  |           throw new Error( | ||||||
|  |               `Unexpected value '${stringify(type)}' used in the bootstrap property of module '${stringify(moduleType)}'`); | ||||||
|  |         } | ||||||
|  |         return this._getTypeMetadata(type, staticTypeModuleUrl(type)); | ||||||
|  |       }); | ||||||
|  |       bootstrapComponents.push(...typeMetadata); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     entryComponents.push(...bootstrapComponents); | ||||||
|  | 
 | ||||||
|  |     if (meta.schemas) { | ||||||
|  |       schemas.push(...flattenAndDedupeArray(meta.schemas)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     transitiveModule.entryComponents.push(...entryComponents); | ||||||
|  |     transitiveModule.providers.push(...providers); | ||||||
|  | 
 | ||||||
|  |     compileMeta = new cpl.CompileNgModuleMetadata({ | ||||||
|  |       type: this._getTypeMetadata(moduleType, staticTypeModuleUrl(moduleType)), | ||||||
|  |       providers, | ||||||
|  |       entryComponents, | ||||||
|  |       bootstrapComponents, | ||||||
|  |       schemas, | ||||||
|  |       declaredDirectives, | ||||||
|  |       exportedDirectives, | ||||||
|  |       declaredPipes, | ||||||
|  |       exportedPipes, | ||||||
|  |       importedModules, | ||||||
|  |       exportedModules, | ||||||
|  |       transitiveModule, | ||||||
|  |       id: meta.id, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     transitiveModule.modules.push(compileMeta); | ||||||
|  |     this._verifyModule(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 { |   /** | ||||||
|     pipeType = resolveForwardRef(pipeType); |    * Gets the metadata for the given pipe. | ||||||
|     let meta = this._pipeCache.get(pipeType); |    * This assumes `loadNgModuleMetadata` has been called first. | ||||||
|     if (!meta) { |    */ | ||||||
|       const pipeMeta = this._pipeResolver.resolve(pipeType, throwIfNotFound); |   getPipeMetadata(pipeType: any): cpl.CompilePipeMetadata { | ||||||
|       if (!pipeMeta) { |     const pipeMeta = this._pipeCache.get(pipeType); | ||||||
|         return null; |     if (!pipeMeta) { | ||||||
|       } |       throw new Error( | ||||||
| 
 |           `Illegal state: getPipeMetadata can only be called after loadNgModuleMetadata for a module that declares it. Pipe ${stringify(pipeType)}.`); | ||||||
|       meta = new cpl.CompilePipeMetadata({ |  | ||||||
|         type: this.getTypeMetadata(pipeType, staticTypeModuleUrl(pipeType)), |  | ||||||
|         name: pipeMeta.name, |  | ||||||
|         pure: pipeMeta.pure |  | ||||||
|       }); |  | ||||||
|       this._pipeCache.set(pipeType, meta); |  | ||||||
|     } |     } | ||||||
|     return meta; |     return pipeMeta; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getDependenciesMetadata(typeOrFunc: Type<any>|Function, dependencies: any[]): |   private _loadPipeMetadata(pipeType: Type<any>): void { | ||||||
|  |     pipeType = resolveForwardRef(pipeType); | ||||||
|  |     const pipeMeta = this._pipeResolver.resolve(pipeType); | ||||||
|  |     if (!pipeMeta) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const meta = new cpl.CompilePipeMetadata({ | ||||||
|  |       type: this._getTypeMetadata(pipeType, staticTypeModuleUrl(pipeType)), | ||||||
|  |       name: pipeMeta.name, | ||||||
|  |       pure: pipeMeta.pure | ||||||
|  |     }); | ||||||
|  |     this._pipeCache.set(pipeType, meta); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   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); |  | ||||||
| 
 | 
 | ||||||
|  | function _analyzeNgModules(ngModuleMetas: CompileNgModuleMetadata[]) { | ||||||
|   const moduleMetasByRef = new Map<any, CompileNgModuleMetadata>(); |   const moduleMetasByRef = new Map<any, CompileNgModuleMetadata>(); | ||||||
| 
 |   ngModuleMetas.forEach((ngModule) => moduleMetasByRef.set(ngModule.type.reference, ngModule)); | ||||||
|   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()); |  | ||||||
|   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,48 +126,38 @@ 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); |       } | ||||||
|           } |       const ngModule = ngModuleByPipeOrDirective.get(dirType); | ||||||
|           const ngModule = ngModuleByPipeOrDirective.get(dirType); |       if (!ngModule) { | ||||||
|           if (!ngModule) { |         throw new Error( | ||||||
|             throw new Error( |             `Internal Error: cannot determine the module for component ${compMeta.type.name}!`); | ||||||
|                 `Internal Error: cannot determine the module for component ${compMeta.type.name}!`); |       } | ||||||
|           } |  | ||||||
| 
 | 
 | ||||||
|           return Promise |       _assertComponent(compMeta); | ||||||
|               .all([compMeta, ...ngModule.transitiveModule.directives].map( |  | ||||||
|                   dirMeta => this._directiveNormalizer.normalizeDirective(dirMeta).asyncResult)) |  | ||||||
|               .then((normalizedCompWithDirectives) => { |  | ||||||
|                 const [compMeta, ...dirMetas] = normalizedCompWithDirectives; |  | ||||||
|                 _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)); | ||||||
|               }); |     }); | ||||||
|         })) |     if (statements.length > 0) { | ||||||
|         .then(() => { |       const srcModule = this._codegenSourceModule( | ||||||
|           if (statements.length > 0) { |           srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars); | ||||||
|             const srcModule = this._codegenSourceModule( |       outputSourceModules.unshift(srcModule); | ||||||
|                 srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars); |     } | ||||||
|             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>[] = []; |       this._compileComponents(moduleType, componentFactories); | ||||||
|     const templates = new Set<CompiledTemplate>(); |       return new ModuleWithComponentFactories(this._compileModule(moduleType), componentFactories); | ||||||
|     moduleMeta.transitiveModule.modules.forEach((localModuleMeta) => { |  | ||||||
|       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({ | ||||||
|                                                              encapsulation: null, |              componentType: SomeComp, | ||||||
|                                                              template: 'a', |              moduleUrl: SOME_MODULE_URL, | ||||||
|                                                              templateUrl: null, |              encapsulation: null, | ||||||
|                                                              styles: [], |              template: 'a', | ||||||
|                                                              styleUrls: [] |              templateUrl: null, | ||||||
|                                                            })); |              styles: [], | ||||||
|  |              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({ | ||||||
|                                                              encapsulation: null, |              componentType: SomeComp, | ||||||
|                                                              template: '', |              moduleUrl: SOME_MODULE_URL, | ||||||
|                                                              templateUrl: null, |              encapsulation: null, | ||||||
|                                                              styles: [], |              template: '', | ||||||
|                                                              styleUrls: ['test.css'] |              templateUrl: null, | ||||||
|                                                            })); |              styles: [], | ||||||
|  |              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, | ||||||
|                                                   encapsulation: null, |              moduleUrl: SOME_MODULE_URL, | ||||||
|                                                   template: '<style>@import test.css</style>', |              encapsulation: null, | ||||||
|                                                   templateUrl: null, |              template: '<style>@import test.css</style>', | ||||||
|                                                   styles: [], |              templateUrl: null, | ||||||
|                                                   styleUrls: [] |              styles: [], | ||||||
|                                                 })); |              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({ | ||||||
|                                                              encapsulation: null, |              componentType: SomeComp, | ||||||
|                                                              template: '', |              moduleUrl: SOME_MODULE_URL, | ||||||
|                                                              templateUrl: null, |              encapsulation: null, | ||||||
|                                                              styles: [], |              template: '', | ||||||
|                                                              styleUrls: ['test.css'] |              templateUrl: null, | ||||||
|                                                            })); |              styles: [], | ||||||
|  |              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, | ||||||
|                                                       encapsulation: null, |                  moduleUrl: SOME_MODULE_URL, | ||||||
|                                                       template: '', |                  encapsulation: null, | ||||||
|                                                       templateUrl: null, |                  template: '', | ||||||
|                                                       styles: [], |                  templateUrl: null, | ||||||
|                                                       styleUrls: ['test.css'] |                  styles: [], | ||||||
|                                                     })); |                  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({ | ||||||
|                                              encapsulation: null, |                      componentType: SomeComp, | ||||||
|                                              template: null, |                      moduleUrl: SOME_MODULE_URL, | ||||||
|                                              templateUrl: 'sometplurl.html', |                      encapsulation: null, | ||||||
|                                              styles: [], |                      template: null, | ||||||
|                                              styleUrls: ['test.css'] |                      templateUrl: 'sometplurl.html', | ||||||
|                                            })) |                      styles: [], | ||||||
|  |                      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({ | ||||||
|                                              encapsulation: null, |                      componentType: SomeComp, | ||||||
|                                              template: null, |                      moduleUrl: SOME_MODULE_URL, | ||||||
|                                              templateUrl: 'tpl/sometplurl.html', |                      encapsulation: null, | ||||||
|                                              styles: [], |                      template: null, | ||||||
|                                              styleUrls: ['test.css'] |                      templateUrl: 'tpl/sometplurl.html', | ||||||
|                                            })) |                      styles: [], | ||||||
|  |                      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({ | ||||||
|                                              encapsulation: null, |                      componentType: SomeComp, | ||||||
|                                              template: null, |                      moduleUrl: SOME_MODULE_URL, | ||||||
|                                              templateUrl: 'tpl/sometplurl.html', |                      encapsulation: null, | ||||||
|                                              styles: [], |                      template: null, | ||||||
|                                              styleUrls: [] |                      templateUrl: 'tpl/sometplurl.html', | ||||||
|                                            })) |                      styles: [], | ||||||
|  |                      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,186 +22,305 @@ 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) => { |  | ||||||
|            const meta = resolver.getDirectiveMetadata(ComponentWithEverything); |  | ||||||
|            expect(meta.selector).toEqual('someSelector'); |  | ||||||
|            expect(meta.exportAs).toEqual('someExportAs'); |  | ||||||
|            expect(meta.isComponent).toBe(true); |  | ||||||
|            expect(meta.type.reference).toBe(ComponentWithEverything); |  | ||||||
|            expect(meta.type.name).toEqual(stringify(ComponentWithEverything)); |  | ||||||
|            expect(meta.type.lifecycleHooks).toEqual(LIFECYCLE_HOOKS_VALUES); |  | ||||||
|            expect(meta.changeDetection).toBe(ChangeDetectionStrategy.Default); |  | ||||||
|            expect(meta.inputs).toEqual({'someProp': 'someProp'}); |  | ||||||
|            expect(meta.outputs).toEqual({'someEvent': 'someEvent'}); |  | ||||||
|            expect(meta.hostListeners).toEqual({'someHostListener': 'someHostListenerExpr'}); |  | ||||||
|            expect(meta.hostProperties).toEqual({'someHostProp': 'someHostPropExpr'}); |  | ||||||
|            expect(meta.hostAttributes).toEqual({'someHostAttr': 'someHostAttrValue'}); |  | ||||||
|            expect(meta.template.encapsulation).toBe(ViewEncapsulation.Emulated); |  | ||||||
|            expect(meta.template.styles).toEqual(['someStyle']); |  | ||||||
|            expect(meta.template.styleUrls).toEqual(['someStyleUrl']); |  | ||||||
|            expect(meta.template.template).toEqual('someTemplate'); |  | ||||||
|            expect(meta.template.templateUrl).toEqual('someTemplateUrl'); |  | ||||||
|            expect(meta.template.interpolation).toEqual(['{{', '}}']); |  | ||||||
|          })); |  | ||||||
| 
 |  | ||||||
|       it('should use the moduleUrl from the reflector if none is given', |  | ||||||
|          inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { |  | ||||||
|            const value: string = |  | ||||||
|                resolver.getDirectiveMetadata(ComponentWithoutModuleId).type.moduleUrl; |  | ||||||
|            const expectedEndValue = './ComponentWithoutModuleId'; |  | ||||||
|            expect(value.endsWith(expectedEndValue)).toBe(true); |  | ||||||
|          })); |  | ||||||
| 
 |  | ||||||
|       it('should throw when the moduleId is not a string', |  | ||||||
|          inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { |  | ||||||
|            expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidModuleId)) |  | ||||||
|                .toThrowError( |  | ||||||
|                    `moduleId should be a string in "ComponentWithInvalidModuleId". See` + |  | ||||||
|                    ` https://goo.gl/wIDDiL for more information.\n` + |  | ||||||
|                    `If you're using Webpack you should inline the template and the styles, see` + |  | ||||||
|                    ` https://goo.gl/X2J8zc.`); |  | ||||||
|          })); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|       it('should throw when metadata is incorrectly typed', |  | ||||||
|          inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { |  | ||||||
|            expect(() => resolver.getDirectiveMetadata(MalformedStylesComponent)) |  | ||||||
|                .toThrowError(`Expected 'styles' to be an array of strings.`); |  | ||||||
|          })); |  | ||||||
| 
 |  | ||||||
|       it('should throw with descriptive error message when provider token can not be resolved', |  | ||||||
|          inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { |  | ||||||
|            expect(() => resolver.getDirectiveMetadata(MyBrokenComp1)) |  | ||||||
|                .toThrowError(`Can't resolve all parameters for MyBrokenComp1: (?).`); |  | ||||||
|          })); |  | ||||||
|       it('should throw with descriptive error message when a directive is passed to imports', |  | ||||||
|          inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { |  | ||||||
|            @NgModule({imports: [ComponentWithoutModuleId]}) |  | ||||||
|            class ModuleWithImportedComponent { |  | ||||||
|            } |  | ||||||
|            expect(() => resolver.getNgModuleMetadata(ModuleWithImportedComponent)) |  | ||||||
|                .toThrowError( |  | ||||||
|                    `Unexpected directive 'ComponentWithoutModuleId' imported by the module 'ModuleWithImportedComponent'`); |  | ||||||
|          })); |  | ||||||
| 
 |  | ||||||
|       it('should throw with descriptive error message when a pipe is passed to imports', |  | ||||||
|          inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { |  | ||||||
|            @Pipe({name: 'somePipe'}) |  | ||||||
|            class SomePipe { |  | ||||||
|            } |  | ||||||
|            @NgModule({imports: [SomePipe]}) |  | ||||||
|            class ModuleWithImportedPipe { |  | ||||||
|            } |  | ||||||
|            expect(() => resolver.getNgModuleMetadata(ModuleWithImportedPipe)) |  | ||||||
|                .toThrowError( |  | ||||||
|                    `Unexpected pipe 'SomePipe' imported by the module 'ModuleWithImportedPipe'`); |  | ||||||
|          })); |  | ||||||
| 
 |  | ||||||
|       it('should throw with descriptive error message when a module is passed to declarations', |  | ||||||
|          inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { |  | ||||||
|            @NgModule({}) |  | ||||||
|            class SomeModule { |  | ||||||
|            } |  | ||||||
|            @NgModule({declarations: [SomeModule]}) |  | ||||||
|            class ModuleWithDeclaredModule { |  | ||||||
|            } |  | ||||||
|            expect(() => resolver.getNgModuleMetadata(ModuleWithDeclaredModule)) |  | ||||||
|                .toThrowError( |  | ||||||
|                    `Unexpected module 'SomeModule' declared by the module 'ModuleWithDeclaredModule'`); |  | ||||||
|          })); |  | ||||||
| 
 |  | ||||||
|       it('should throw with descriptive error message when null is passed to declarations', |  | ||||||
|          inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { |  | ||||||
|            @NgModule({declarations: [null]}) |  | ||||||
|            class ModuleWithNullDeclared { |  | ||||||
|            } |  | ||||||
|            expect(() => resolver.getNgModuleMetadata(ModuleWithNullDeclared)) |  | ||||||
|                .toThrowError( |  | ||||||
|                    `Unexpected value 'null' declared by the module 'ModuleWithNullDeclared'`); |  | ||||||
|          })); |  | ||||||
| 
 |  | ||||||
|       it('should throw with descriptive error message when null is passed to imports', |  | ||||||
|          inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { |  | ||||||
|            @NgModule({imports: [null]}) |  | ||||||
|            class ModuleWithNullImported { |  | ||||||
|            } |  | ||||||
|            expect(() => resolver.getNgModuleMetadata(ModuleWithNullImported)) |  | ||||||
|                .toThrowError( |  | ||||||
|                    `Unexpected value 'null' imported by the module 'ModuleWithNullImported'`); |  | ||||||
|          })); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|       it('should throw with descriptive error message when a param token of a dependency is undefined', |  | ||||||
|          inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { |  | ||||||
|            expect(() => resolver.getDirectiveMetadata(MyBrokenComp2)) |  | ||||||
|                .toThrowError(`Can't resolve all parameters for NonAnnotatedService: (?).`); |  | ||||||
|          })); |  | ||||||
| 
 |  | ||||||
|       it('should throw with descriptive error message when one of providers is not present', |  | ||||||
|          inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { |  | ||||||
|            expect(() => resolver.getDirectiveMetadata(MyBrokenComp3)) |  | ||||||
|                .toThrowError( |  | ||||||
|                    `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', |  | ||||||
|          inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { |  | ||||||
|            expect(() => resolver.getDirectiveMetadata(MyBrokenComp4)) |  | ||||||
|                .toThrowError( |  | ||||||
|                    `Invalid viewProviders for "MyBrokenComp4" - only instances of Provider and Type are allowed, got: [?null?, ...]`); |  | ||||||
|          })); |  | ||||||
| 
 |  | ||||||
|       it('should throw with descriptive error message when null or undefined is passed to module bootstrap', |  | ||||||
|          inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { |  | ||||||
|            @NgModule({bootstrap: [null]}) |  | ||||||
|            class ModuleWithNullBootstrap { |  | ||||||
|            } |  | ||||||
|            @NgModule({bootstrap: [undefined]}) |  | ||||||
|            class ModuleWithUndefinedBootstrap { |  | ||||||
|            } |  | ||||||
| 
 |  | ||||||
|            expect(() => resolver.getNgModuleMetadata(ModuleWithNullBootstrap)) |  | ||||||
|                .toThrowError( |  | ||||||
|                    `Unexpected value 'null' used in the bootstrap property of module 'ModuleWithNullBootstrap'`); |  | ||||||
|            expect(() => resolver.getNgModuleMetadata(ModuleWithUndefinedBootstrap)) |  | ||||||
|                .toThrowError( |  | ||||||
|                    `Unexpected value 'undefined' used in the bootstrap property of module 'ModuleWithUndefinedBootstrap'`); |  | ||||||
|          })); |  | ||||||
| 
 |  | ||||||
|       it('should throw an error when the interpolation config has invalid symbols', |  | ||||||
|          inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { |  | ||||||
|            expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidInterpolation1)) |  | ||||||
|                .toThrowError(`[' ', ' '] contains unusable interpolation symbol.`); |  | ||||||
|            expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidInterpolation2)) |  | ||||||
|                .toThrowError(`['{', '}'] contains unusable interpolation symbol.`); |  | ||||||
|            expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidInterpolation3)) |  | ||||||
|                .toThrowError(`['<%', '%>'] contains unusable interpolation symbol.`); |  | ||||||
|            expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidInterpolation4)) |  | ||||||
|                .toThrowError(`['&#', '}}'] contains unusable interpolation symbol.`); |  | ||||||
|            expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidInterpolation5)) |  | ||||||
|                .toThrowError(`['{', '}}'] contains unusable interpolation symbol.`); |  | ||||||
|          })); |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     it('should dedupe declarations in NgModule', |  | ||||||
|        inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { |        inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { | ||||||
| 
 |          @NgModule({}) | ||||||
|          @Component({template: ''}) |          class SomeModule { | ||||||
|          class MyComp { |  | ||||||
|          } |          } | ||||||
| 
 | 
 | ||||||
|          @NgModule({declarations: [MyComp, MyComp]}) |          @Pipe({name: 'pipe'}) | ||||||
|          class MyModule { |          class SomePipe { | ||||||
|          } |          } | ||||||
| 
 | 
 | ||||||
|          const modMeta = resolver.getNgModuleMetadata(MyModule); |          expect(() => resolver.getNgModuleMetadata(SomeModule)).toThrowError(/Illegal state/); | ||||||
|          expect(modMeta.declaredDirectives.length).toBe(1); |          expect(() => resolver.getDirectiveMetadata(ComponentWithEverythingInline)) | ||||||
|          expect(modMeta.declaredDirectives[0].type.reference).toBe(MyComp); |              .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.exportAs).toEqual('someExportAs'); | ||||||
|  |          expect(meta.isComponent).toBe(true); | ||||||
|  |          expect(meta.type.reference).toBe(ComponentWithEverythingInline); | ||||||
|  |          expect(meta.type.name).toEqual(stringify(ComponentWithEverythingInline)); | ||||||
|  |          expect(meta.type.lifecycleHooks).toEqual(LIFECYCLE_HOOKS_VALUES); | ||||||
|  |          expect(meta.changeDetection).toBe(ChangeDetectionStrategy.Default); | ||||||
|  |          expect(meta.inputs).toEqual({'someProp': 'someProp'}); | ||||||
|  |          expect(meta.outputs).toEqual({'someEvent': 'someEvent'}); | ||||||
|  |          expect(meta.hostListeners).toEqual({'someHostListener': 'someHostListenerExpr'}); | ||||||
|  |          expect(meta.hostProperties).toEqual({'someHostProp': 'someHostPropExpr'}); | ||||||
|  |          expect(meta.hostAttributes).toEqual({'someHostAttr': 'someHostAttrValue'}); | ||||||
|  |          expect(meta.template.encapsulation).toBe(ViewEncapsulation.Emulated); | ||||||
|  |          expect(meta.template.styles).toEqual(['someStyle']); | ||||||
|  |          expect(meta.template.template).toEqual('someTemplate'); | ||||||
|  |          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', | ||||||
|  |        inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { | ||||||
|  |          @NgModule({declarations: [ComponentWithoutModuleId]}) | ||||||
|  |          class SomeModule { | ||||||
|  |          } | ||||||
|  |          resolver.loadNgModuleMetadata(SomeModule, true); | ||||||
|  | 
 | ||||||
|  |          const value: string = | ||||||
|  |              resolver.getDirectiveMetadata(ComponentWithoutModuleId).type.moduleUrl; | ||||||
|  |          const expectedEndValue = './ComponentWithoutModuleId'; | ||||||
|  |          expect(value.endsWith(expectedEndValue)).toBe(true); | ||||||
|  |        })); | ||||||
|  | 
 | ||||||
|  |     it('should throw when the moduleId is not a string', | ||||||
|  |        inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { | ||||||
|  |          @NgModule({declarations: [ComponentWithInvalidModuleId]}) | ||||||
|  |          class SomeModule { | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          expect(() => resolver.loadNgModuleMetadata(SomeModule, true)) | ||||||
|  |              .toThrowError( | ||||||
|  |                  `moduleId should be a string in "ComponentWithInvalidModuleId". See` + | ||||||
|  |                  ` https://goo.gl/wIDDiL for more information.\n` + | ||||||
|  |                  `If you're using Webpack you should inline the template and the styles, see` + | ||||||
|  |                  ` https://goo.gl/X2J8zc.`); | ||||||
|  |        })); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     it('should throw when metadata is incorrectly typed', | ||||||
|  |        inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { | ||||||
|  |          @NgModule({declarations: [MalformedStylesComponent]}) | ||||||
|  |          class SomeModule { | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          expect(() => resolver.loadNgModuleMetadata(SomeModule, true)) | ||||||
|  |              .toThrowError(`Expected 'styles' to be an array of strings.`); | ||||||
|  |        })); | ||||||
|  | 
 | ||||||
|  |     it('should throw with descriptive error message when provider token can not be resolved', | ||||||
|  |        inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { | ||||||
|  |          @NgModule({declarations: [MyBrokenComp1]}) | ||||||
|  |          class SomeModule { | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          expect(() => resolver.loadNgModuleMetadata(SomeModule, true)) | ||||||
|  |              .toThrowError(`Can't resolve all parameters for MyBrokenComp1: (?).`); | ||||||
|  |        })); | ||||||
|  |     it('should throw with descriptive error message when a directive is passed to imports', | ||||||
|  |        inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { | ||||||
|  |          @NgModule({imports: [ComponentWithoutModuleId]}) | ||||||
|  |          class ModuleWithImportedComponent { | ||||||
|  |          } | ||||||
|  |          expect(() => resolver.loadNgModuleMetadata(ModuleWithImportedComponent, true)) | ||||||
|  |              .toThrowError( | ||||||
|  |                  `Unexpected directive 'ComponentWithoutModuleId' imported by the module 'ModuleWithImportedComponent'`); | ||||||
|  |        })); | ||||||
|  | 
 | ||||||
|  |     it('should throw with descriptive error message when a pipe is passed to imports', | ||||||
|  |        inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { | ||||||
|  |          @Pipe({name: 'somePipe'}) | ||||||
|  |          class SomePipe { | ||||||
|  |          } | ||||||
|  |          @NgModule({imports: [SomePipe]}) | ||||||
|  |          class ModuleWithImportedPipe { | ||||||
|  |          } | ||||||
|  |          expect(() => resolver.loadNgModuleMetadata(ModuleWithImportedPipe, true)) | ||||||
|  |              .toThrowError( | ||||||
|  |                  `Unexpected pipe 'SomePipe' imported by the module 'ModuleWithImportedPipe'`); | ||||||
|  |        })); | ||||||
|  | 
 | ||||||
|  |     it('should throw with descriptive error message when a module is passed to declarations', | ||||||
|  |        inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { | ||||||
|  |          @NgModule({}) | ||||||
|  |          class SomeModule { | ||||||
|  |          } | ||||||
|  |          @NgModule({declarations: [SomeModule]}) | ||||||
|  |          class ModuleWithDeclaredModule { | ||||||
|  |          } | ||||||
|  |          expect(() => resolver.loadNgModuleMetadata(ModuleWithDeclaredModule, true)) | ||||||
|  |              .toThrowError( | ||||||
|  |                  `Unexpected module 'SomeModule' declared by the module 'ModuleWithDeclaredModule'`); | ||||||
|  |        })); | ||||||
|  | 
 | ||||||
|  |     it('should throw with descriptive error message when null is passed to declarations', | ||||||
|  |        inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { | ||||||
|  |          @NgModule({declarations: [null]}) | ||||||
|  |          class ModuleWithNullDeclared { | ||||||
|  |          } | ||||||
|  |          expect(() => resolver.loadNgModuleMetadata(ModuleWithNullDeclared, true)) | ||||||
|  |              .toThrowError( | ||||||
|  |                  `Unexpected value 'null' declared by the module 'ModuleWithNullDeclared'`); | ||||||
|  |        })); | ||||||
|  | 
 | ||||||
|  |     it('should throw with descriptive error message when null is passed to imports', | ||||||
|  |        inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { | ||||||
|  |          @NgModule({imports: [null]}) | ||||||
|  |          class ModuleWithNullImported { | ||||||
|  |          } | ||||||
|  |          expect(() => resolver.loadNgModuleMetadata(ModuleWithNullImported, true)) | ||||||
|  |              .toThrowError( | ||||||
|  |                  `Unexpected value 'null' imported by the module 'ModuleWithNullImported'`); | ||||||
|  |        })); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     it('should throw with descriptive error message when a param token of a dependency is undefined', | ||||||
|  |        inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { | ||||||
|  |          @NgModule({declarations: [MyBrokenComp2]}) | ||||||
|  |          class SomeModule { | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          expect(() => resolver.loadNgModuleMetadata(SomeModule, true)) | ||||||
|  |              .toThrowError(`Can't resolve all parameters for NonAnnotatedService: (?).`); | ||||||
|  |        })); | ||||||
|  | 
 | ||||||
|  |     it('should throw with descriptive error message when one of providers is not present', | ||||||
|  |        inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { | ||||||
|  |          @NgModule({declarations: [MyBrokenComp3]}) | ||||||
|  |          class SomeModule { | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          expect(() => resolver.loadNgModuleMetadata(SomeModule, true)) | ||||||
|  |              .toThrowError( | ||||||
|  |                  `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', | ||||||
|  |        inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { | ||||||
|  |          @NgModule({declarations: [MyBrokenComp4]}) | ||||||
|  |          class SomeModule { | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          expect(() => resolver.loadNgModuleMetadata(SomeModule, true)) | ||||||
|  |              .toThrowError( | ||||||
|  |                  `Invalid viewProviders for "MyBrokenComp4" - only instances of Provider and Type are allowed, got: [?null?, ...]`); | ||||||
|  |        })); | ||||||
|  | 
 | ||||||
|  |     it('should throw with descriptive error message when null or undefined is passed to module bootstrap', | ||||||
|  |        inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { | ||||||
|  |          @NgModule({bootstrap: [null]}) | ||||||
|  |          class ModuleWithNullBootstrap { | ||||||
|  |          } | ||||||
|  |          @NgModule({bootstrap: [undefined]}) | ||||||
|  |          class ModuleWithUndefinedBootstrap { | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          expect(() => resolver.loadNgModuleMetadata(ModuleWithNullBootstrap, true)) | ||||||
|  |              .toThrowError( | ||||||
|  |                  `Unexpected value 'null' used in the bootstrap property of module 'ModuleWithNullBootstrap'`); | ||||||
|  |          expect(() => resolver.loadNgModuleMetadata(ModuleWithUndefinedBootstrap, true)) | ||||||
|  |              .toThrowError( | ||||||
|  |                  `Unexpected value 'undefined' used in the bootstrap property of module 'ModuleWithUndefinedBootstrap'`); | ||||||
|  |        })); | ||||||
|  | 
 | ||||||
|  |     it('should throw an error when the interpolation config has invalid symbols', | ||||||
|  |        inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { | ||||||
|  |          @NgModule({declarations: [ComponentWithInvalidInterpolation1]}) | ||||||
|  |          class Module1 { | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          expect(() => resolver.loadNgModuleMetadata(Module1, true)) | ||||||
|  |              .toThrowError(`[' ', ' '] contains unusable interpolation symbol.`); | ||||||
|  | 
 | ||||||
|  |          @NgModule({declarations: [ComponentWithInvalidInterpolation2]}) | ||||||
|  |          class Module2 { | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          expect(() => resolver.loadNgModuleMetadata(Module2, true)) | ||||||
|  |              .toThrowError(`['{', '}'] contains unusable interpolation symbol.`); | ||||||
|  | 
 | ||||||
|  |          @NgModule({declarations: [ComponentWithInvalidInterpolation3]}) | ||||||
|  |          class Module3 { | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          expect(() => resolver.loadNgModuleMetadata(Module3, true)) | ||||||
|  |              .toThrowError(`['<%', '%>'] contains unusable interpolation symbol.`); | ||||||
|  | 
 | ||||||
|  |          @NgModule({declarations: [ComponentWithInvalidInterpolation4]}) | ||||||
|  |          class Module4 { | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          expect(() => resolver.loadNgModuleMetadata(Module4, true)) | ||||||
|  |              .toThrowError(`['&#', '}}'] contains unusable interpolation symbol.`); | ||||||
|  | 
 | ||||||
|  |          @NgModule({declarations: [ComponentWithInvalidInterpolation5]}) | ||||||
|  |          class Module5 { | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          expect(() => resolver.loadNgModuleMetadata(Module5, true)) | ||||||
|  |              .toThrowError(`['{', '}}'] contains unusable interpolation symbol.`); | ||||||
|  |        })); | ||||||
|   }); |   }); | ||||||
|  | 
 | ||||||
|  |   it('should dedupe declarations in NgModule', | ||||||
|  |      inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { | ||||||
|  | 
 | ||||||
|  |        @Component({template: ''}) | ||||||
|  |        class MyComp { | ||||||
|  |        } | ||||||
|  | 
 | ||||||
|  |        @NgModule({declarations: [MyComp, MyComp]}) | ||||||
|  |        class MyModule { | ||||||
|  |        } | ||||||
|  | 
 | ||||||
|  |        const modMeta = resolver.loadNgModuleMetadata(MyModule, true).ngModule; | ||||||
|  |        expect(modMeta.declaredDirectives.length).toBe(1); | ||||||
|  |        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(); | ||||||
|               `Could not compile '${stringify(HelloRootDirectiveIsNotCmp)}' because it is not a component.`); |          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.`); | ||||||
|  |            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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user