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( | ||||
|         new compiler.NgModuleResolver(staticReflector), | ||||
|         new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector), | ||||
|         elementSchemaRegistry, staticReflector); | ||||
|         elementSchemaRegistry, normalizer, staticReflector); | ||||
|     // TODO(vicb): do not pass cliOptions.i18nFormat here
 | ||||
|     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.DirectiveWrapperCompiler( | ||||
|             config, expressionParser, elementSchemaRegistry, console), | ||||
|  | ||||
| @ -28,50 +28,40 @@ export class Extractor { | ||||
|       private options: tsc.AngularCompilerOptions, private program: ts.Program, | ||||
|       public host: ts.CompilerHost, private staticReflector: StaticReflector, | ||||
|       private messageBundle: compiler.MessageBundle, private reflectorHost: ReflectorHost, | ||||
|       private metadataResolver: compiler.CompileMetadataResolver, | ||||
|       private directiveNormalizer: compiler.DirectiveNormalizer) {} | ||||
|       private metadataResolver: compiler.CompileMetadataResolver) {} | ||||
| 
 | ||||
|   extract(): Promise<compiler.MessageBundle> { | ||||
|     const programSymbols: StaticSymbol[] = | ||||
|         extractProgramSymbols(this.program, this.staticReflector, this.reflectorHost, this.options); | ||||
| 
 | ||||
|     const files = | ||||
|         compiler.analyzeNgModules(programSymbols, {transitiveModules: true}, this.metadataResolver) | ||||
|             .files; | ||||
|     const errors: compiler.ParseError[] = []; | ||||
|     const filePromises: Promise<any>[] = []; | ||||
|     return compiler | ||||
|         .analyzeNgModules(programSymbols, {transitiveModules: true}, this.metadataResolver) | ||||
|         .then(({files}) => { | ||||
|           const errors: compiler.ParseError[] = []; | ||||
| 
 | ||||
|     files.forEach(file => { | ||||
|       const cmpPromises: Promise<compiler.CompileDirectiveMetadata>[] = []; | ||||
|       file.directives.forEach(directiveType => { | ||||
|         const dirMeta = this.metadataResolver.getDirectiveMetadata(directiveType); | ||||
|         if (dirMeta.isComponent) { | ||||
|           cmpPromises.push(this.directiveNormalizer.normalizeDirective(dirMeta).asyncResult); | ||||
|         } | ||||
|       }); | ||||
| 
 | ||||
|       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)); | ||||
|               }); | ||||
|           files.forEach(file => { | ||||
|             const compMetas: compiler.CompileDirectiveMetadata[] = []; | ||||
|             file.directives.forEach(directiveType => { | ||||
|               const dirMeta = this.metadataResolver.getDirectiveMetadata(directiveType); | ||||
|               if (dirMeta && dirMeta.isComponent) { | ||||
|                 compMetas.push(dirMeta); | ||||
|               } | ||||
|             }); | ||||
|             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')); | ||||
|           } | ||||
| 
 | ||||
| 
 | ||||
|     if (errors.length) { | ||||
|       throw new Error(errors.map(e => e.toString()).join('\n')); | ||||
|     } | ||||
| 
 | ||||
|     return Promise.all(filePromises).then(_ => this.messageBundle); | ||||
|           return this.messageBundle; | ||||
|         }); | ||||
|   } | ||||
| 
 | ||||
|   static create( | ||||
| @ -98,13 +88,12 @@ export class Extractor { | ||||
|     const resolver = new compiler.CompileMetadataResolver( | ||||
|         new compiler.NgModuleResolver(staticReflector), | ||||
|         new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector), | ||||
|         elementSchemaRegistry, staticReflector); | ||||
|         elementSchemaRegistry, normalizer, staticReflector); | ||||
| 
 | ||||
|     // TODO(vicb): implicit tags & attributes
 | ||||
|     let messageBundle = new compiler.MessageBundle(htmlParser, [], {}); | ||||
| 
 | ||||
|     return new Extractor( | ||||
|         options, program, compilerHost, staticReflector, messageBundle, reflectorHost, resolver, | ||||
|         normalizer); | ||||
|         options, program, compilerHost, staticReflector, messageBundle, reflectorHost, resolver); | ||||
|   } | ||||
| } | ||||
| @ -326,9 +326,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier { | ||||
|             Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>, | ||||
|         queries?: CompileQueryMetadata[], | ||||
|         viewQueries?: CompileQueryMetadata[], | ||||
|         entryComponents?: CompileTypeMetadata[], | ||||
|         viewDirectives?: CompileTypeMetadata[], | ||||
|         viewPipes?: CompileTypeMetadata[], | ||||
|         entryComponents?: CompileIdentifierMetadata[], | ||||
|         template?: CompileTemplateMetadata | ||||
|       } = {}): CompileDirectiveMetadata { | ||||
|     var hostListeners: {[key: string]: string} = {}; | ||||
| @ -397,7 +395,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier { | ||||
|   queries: CompileQueryMetadata[]; | ||||
|   viewQueries: CompileQueryMetadata[]; | ||||
|   // Note: Need to keep types here to prevent cycles!
 | ||||
|   entryComponents: CompileTypeMetadata[]; | ||||
|   entryComponents: CompileIdentifierMetadata[]; | ||||
| 
 | ||||
|   template: CompileTemplateMetadata; | ||||
| 
 | ||||
| @ -421,9 +419,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier { | ||||
|             Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>, | ||||
|         queries?: CompileQueryMetadata[], | ||||
|         viewQueries?: CompileQueryMetadata[], | ||||
|         entryComponents?: CompileTypeMetadata[], | ||||
|         viewDirectives?: CompileTypeMetadata[], | ||||
|         viewPipes?: CompileTypeMetadata[], | ||||
|         entryComponents?: CompileIdentifierMetadata[], | ||||
|         template?: CompileTemplateMetadata, | ||||
|       } = {}) { | ||||
|     this.type = type; | ||||
| @ -506,13 +502,13 @@ export class CompilePipeMetadata implements CompileMetadataWithIdentifier { | ||||
|  */ | ||||
| export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier { | ||||
|   type: CompileTypeMetadata; | ||||
|   declaredDirectives: CompileDirectiveMetadata[]; | ||||
|   exportedDirectives: CompileDirectiveMetadata[]; | ||||
|   declaredPipes: CompilePipeMetadata[]; | ||||
|   exportedPipes: CompilePipeMetadata[]; | ||||
|   declaredDirectives: CompileIdentifierMetadata[]; | ||||
|   exportedDirectives: CompileIdentifierMetadata[]; | ||||
|   declaredPipes: CompileIdentifierMetadata[]; | ||||
|   exportedPipes: CompileIdentifierMetadata[]; | ||||
|   // Note: See CompileDirectiveMetadata.entryComponents why this has to be a type.
 | ||||
|   entryComponents: CompileTypeMetadata[]; | ||||
|   bootstrapComponents: CompileTypeMetadata[]; | ||||
|   entryComponents: CompileIdentifierMetadata[]; | ||||
|   bootstrapComponents: CompileIdentifierMetadata[]; | ||||
|   providers: CompileProviderMetadata[]; | ||||
| 
 | ||||
|   importedModules: CompileNgModuleMetadata[]; | ||||
| @ -529,12 +525,12 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier { | ||||
|         type?: CompileTypeMetadata, | ||||
|         providers?: | ||||
|             Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>, | ||||
|         declaredDirectives?: CompileDirectiveMetadata[], | ||||
|         exportedDirectives?: CompileDirectiveMetadata[], | ||||
|         declaredPipes?: CompilePipeMetadata[], | ||||
|         exportedPipes?: CompilePipeMetadata[], | ||||
|         entryComponents?: CompileTypeMetadata[], | ||||
|         bootstrapComponents?: CompileTypeMetadata[], | ||||
|         declaredDirectives?: CompileIdentifierMetadata[], | ||||
|         exportedDirectives?: CompileIdentifierMetadata[], | ||||
|         declaredPipes?: CompileIdentifierMetadata[], | ||||
|         exportedPipes?: CompileIdentifierMetadata[], | ||||
|         entryComponents?: CompileIdentifierMetadata[], | ||||
|         bootstrapComponents?: CompileIdentifierMetadata[], | ||||
|         importedModules?: CompileNgModuleMetadata[], | ||||
|         exportedModules?: CompileNgModuleMetadata[], | ||||
|         transitiveModule?: TransitiveCompileNgModuleMetadata, | ||||
| @ -560,15 +556,16 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier { | ||||
| } | ||||
| 
 | ||||
| export class TransitiveCompileNgModuleMetadata { | ||||
|   directivesSet = new Set<Type<any>>(); | ||||
|   pipesSet = new Set<Type<any>>(); | ||||
|   directivesSet = new Set<any>(); | ||||
|   pipesSet = new Set<any>(); | ||||
| 
 | ||||
|   constructor( | ||||
|       public modules: CompileNgModuleMetadata[], public providers: CompileProviderMetadata[], | ||||
|       public entryComponents: CompileTypeMetadata[], public directives: CompileDirectiveMetadata[], | ||||
|       public pipes: CompilePipeMetadata[]) { | ||||
|     directives.forEach(dir => this.directivesSet.add(dir.type.reference)); | ||||
|     pipes.forEach(pipe => this.pipesSet.add(pipe.type.reference)); | ||||
|       public entryComponents: CompileIdentifierMetadata[], | ||||
|       public directives: CompileIdentifierMetadata[], public pipes: CompileIdentifierMetadata[], | ||||
|       public loadingPromises: Promise<any>[]) { | ||||
|     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
 | ||||
|  */ | ||||
| 
 | ||||
| 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 {isBlank, isPresent} from './facade/lang'; | ||||
| import {isBlank, isPresent, stringify} from './facade/lang'; | ||||
| import * as html from './ml_parser/ast'; | ||||
| import {HtmlParser} from './ml_parser/html_parser'; | ||||
| import {InterpolationConfig} from './ml_parser/interpolation_config'; | ||||
| @ -20,6 +20,18 @@ import {PreparsedElementType, preparseElement} from './template_parser/template_ | ||||
| import {UrlResolver} from './url_resolver'; | ||||
| 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() | ||||
| export class DirectiveNormalizer { | ||||
|   private _resourceLoaderCache = new Map<string, Promise<string>>(); | ||||
| @ -48,65 +60,56 @@ export class DirectiveNormalizer { | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   normalizeDirective(directive: CompileDirectiveMetadata): | ||||
|       SyncAsyncResult<CompileDirectiveMetadata> { | ||||
|     if (!directive.isComponent) { | ||||
|       // For non components there is nothing to be normalized yet.
 | ||||
|       return new SyncAsyncResult(directive, Promise.resolve(directive)); | ||||
|     } | ||||
|   normalizeTemplate(prenormData: PrenormalizedTemplateMetadata): | ||||
|       SyncAsyncResult<CompileTemplateMetadata> { | ||||
|     let normalizedTemplateSync: CompileTemplateMetadata = null; | ||||
|     let normalizedTemplateAsync: Promise<CompileTemplateMetadata>; | ||||
|     if (isPresent(directive.template.template)) { | ||||
|       normalizedTemplateSync = this.normalizeTemplateSync(directive.type, directive.template); | ||||
|     if (isPresent(prenormData.template)) { | ||||
|       normalizedTemplateSync = this.normalizeTemplateSync(prenormData); | ||||
|       normalizedTemplateAsync = Promise.resolve(normalizedTemplateSync); | ||||
|     } else if (directive.template.templateUrl) { | ||||
|       normalizedTemplateAsync = this.normalizeTemplateAsync(directive.type, directive.template); | ||||
|     } else if (prenormData.templateUrl) { | ||||
|       normalizedTemplateAsync = this.normalizeTemplateAsync(prenormData); | ||||
|     } 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) { | ||||
|       // sync case
 | ||||
|       let normalizedDirective = _cloneDirectiveWithTemplate(directive, normalizedTemplateSync); | ||||
|       return new SyncAsyncResult(normalizedDirective, Promise.resolve(normalizedDirective)); | ||||
|       return new SyncAsyncResult(normalizedTemplateSync); | ||||
|     } else { | ||||
|       // async case
 | ||||
|       return new SyncAsyncResult( | ||||
|           null, | ||||
|           normalizedTemplateAsync | ||||
|               .then((normalizedTemplate) => this.normalizeExternalStylesheets(normalizedTemplate)) | ||||
|               .then( | ||||
|                   (normalizedTemplate) => | ||||
|                       _cloneDirectiveWithTemplate(directive, normalizedTemplate))); | ||||
|           null, normalizedTemplateAsync.then( | ||||
|                     (normalizedTemplate) => this.normalizeExternalStylesheets(normalizedTemplate))); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   normalizeTemplateSync(directiveType: CompileTypeMetadata, template: CompileTemplateMetadata): | ||||
|       CompileTemplateMetadata { | ||||
|     return this.normalizeLoadedTemplate( | ||||
|         directiveType, template, template.template, directiveType.moduleUrl); | ||||
|   normalizeTemplateSync(prenomData: PrenormalizedTemplateMetadata): CompileTemplateMetadata { | ||||
|     return this.normalizeLoadedTemplate(prenomData, prenomData.template, prenomData.moduleUrl); | ||||
|   } | ||||
| 
 | ||||
|   normalizeTemplateAsync(directiveType: CompileTypeMetadata, template: CompileTemplateMetadata): | ||||
|   normalizeTemplateAsync(prenomData: PrenormalizedTemplateMetadata): | ||||
|       Promise<CompileTemplateMetadata> { | ||||
|     let templateUrl = this._urlResolver.resolve(directiveType.moduleUrl, template.templateUrl); | ||||
|     let templateUrl = this._urlResolver.resolve(prenomData.moduleUrl, prenomData.templateUrl); | ||||
|     return this._fetch(templateUrl) | ||||
|         .then((value) => this.normalizeLoadedTemplate(directiveType, template, value, templateUrl)); | ||||
|         .then((value) => this.normalizeLoadedTemplate(prenomData, value, templateUrl)); | ||||
|   } | ||||
| 
 | ||||
|   normalizeLoadedTemplate( | ||||
|       directiveType: CompileTypeMetadata, templateMeta: CompileTemplateMetadata, template: string, | ||||
|       prenomData: PrenormalizedTemplateMetadata, template: string, | ||||
|       templateAbsUrl: string): CompileTemplateMetadata { | ||||
|     const interpolationConfig = InterpolationConfig.fromArray(templateMeta.interpolation); | ||||
|     const rootNodesAndErrors = | ||||
|         this._htmlParser.parse(template, directiveType.name, false, interpolationConfig); | ||||
|     const interpolationConfig = InterpolationConfig.fromArray(prenomData.interpolation); | ||||
|     const rootNodesAndErrors = this._htmlParser.parse( | ||||
|         template, stringify(prenomData.componentType), false, interpolationConfig); | ||||
|     if (rootNodesAndErrors.errors.length > 0) { | ||||
|       const errorString = rootNodesAndErrors.errors.join('\n'); | ||||
|       throw new Error(`Template parse errors:\n${errorString}`); | ||||
|     } | ||||
|     const templateMetadataStyles = this.normalizeStylesheet(new CompileStylesheetMetadata({ | ||||
|       styles: templateMeta.styles, | ||||
|       styleUrls: templateMeta.styleUrls, | ||||
|       moduleUrl: directiveType.moduleUrl | ||||
|       styles: prenomData.styles, | ||||
|       styleUrls: prenomData.styleUrls, | ||||
|       moduleUrl: prenomData.moduleUrl | ||||
|     })); | ||||
| 
 | ||||
|     const visitor = new TemplatePreparseVisitor(); | ||||
| @ -114,7 +117,7 @@ export class DirectiveNormalizer { | ||||
|     const templateStyles = this.normalizeStylesheet(new CompileStylesheetMetadata( | ||||
|         {styles: visitor.styles, styleUrls: visitor.styleUrls, moduleUrl: templateAbsUrl})); | ||||
| 
 | ||||
|     let encapsulation = templateMeta.encapsulation; | ||||
|     let encapsulation = prenomData.encapsulation; | ||||
|     if (isBlank(encapsulation)) { | ||||
|       encapsulation = this._config.defaultEncapsulation; | ||||
|     } | ||||
| @ -131,10 +134,9 @@ export class DirectiveNormalizer { | ||||
|       encapsulation, | ||||
|       template, | ||||
|       templateUrl: templateAbsUrl, styles, styleUrls, | ||||
|       externalStylesheets: templateMeta.externalStylesheets, | ||||
|       ngContentSelectors: visitor.ngContentSelectors, | ||||
|       animations: templateMeta.animations, | ||||
|       interpolation: templateMeta.interpolation, | ||||
|       animations: prenomData.animations, | ||||
|       interpolation: prenomData.interpolation, | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
| @ -231,25 +233,3 @@ class TemplatePreparseVisitor implements html.Visitor { | ||||
|   visitExpansion(ast: html.Expansion, 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 { | ||||
|   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`. | ||||
|    */ | ||||
|  | ||||
| @ -10,17 +10,27 @@ import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata | ||||
| 
 | ||||
| import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions'; | ||||
| import * as cpl from './compile_metadata'; | ||||
| import {DirectiveNormalizer} from './directive_normalizer'; | ||||
| import {DirectiveResolver} from './directive_resolver'; | ||||
| import {ListWrapper} from './facade/collection'; | ||||
| import {isBlank, isPresent, stringify} from './facade/lang'; | ||||
| import {Identifiers, resolveIdentifierToken} from './identifiers'; | ||||
| import {hasLifecycleHook} from './lifecycle_reflector'; | ||||
| import {NgModuleResolver} from './ng_module_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 {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() | ||||
| export class CompileMetadataResolver { | ||||
|   private _directiveCache = new Map<Type<any>, cpl.CompileDirectiveMetadata>(); | ||||
| @ -33,6 +43,7 @@ export class CompileMetadataResolver { | ||||
|   constructor( | ||||
|       private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver, | ||||
|       private _pipeResolver: PipeResolver, private _schemaRegistry: ElementSchemaRegistry, | ||||
|       private _directiveNormalizer: DirectiveNormalizer, | ||||
|       private _reflector: ReflectorReader = reflector) {} | ||||
| 
 | ||||
|   private sanitizeTokenName(token: any): string { | ||||
| @ -50,11 +61,15 @@ export class CompileMetadataResolver { | ||||
|   } | ||||
| 
 | ||||
|   clearCacheFor(type: Type<any>) { | ||||
|     const dirMeta = this._directiveCache.get(type); | ||||
|     this._directiveCache.delete(type); | ||||
|     this._pipeCache.delete(type); | ||||
|     this._ngModuleOfTypes.delete(type); | ||||
|     // Clear all of the NgModule as they contain transitive information!
 | ||||
|     this._ngModuleCache.clear(); | ||||
|     if (dirMeta) { | ||||
|       this._directiveNormalizer.clearCacheFor(dirMeta); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   clearCache() { | ||||
| @ -62,50 +77,53 @@ export class CompileMetadataResolver { | ||||
|     this._pipeCache.clear(); | ||||
|     this._ngModuleCache.clear(); | ||||
|     this._ngModuleOfTypes.clear(); | ||||
|     this._directiveNormalizer.clearCache(); | ||||
|   } | ||||
| 
 | ||||
|   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); | ||||
|   } | ||||
| 
 | ||||
|   getAnimationStateMetadata(value: AnimationStateMetadata): cpl.CompileAnimationStateMetadata { | ||||
|   private _getAnimationStateMetadata(value: AnimationStateMetadata): | ||||
|       cpl.CompileAnimationStateMetadata { | ||||
|     if (value instanceof AnimationStateDeclarationMetadata) { | ||||
|       const styles = this.getAnimationStyleMetadata(value.styles); | ||||
|       const styles = this._getAnimationStyleMetadata(value.styles); | ||||
|       return new cpl.CompileAnimationStateDeclarationMetadata(value.stateNameExpr, styles); | ||||
|     } | ||||
| 
 | ||||
|     if (value instanceof AnimationStateTransitionMetadata) { | ||||
|       return new cpl.CompileAnimationStateTransitionMetadata( | ||||
|           value.stateChangeExpr, this.getAnimationMetadata(value.steps)); | ||||
|           value.stateChangeExpr, this._getAnimationMetadata(value.steps)); | ||||
|     } | ||||
| 
 | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   getAnimationStyleMetadata(value: AnimationStyleMetadata): cpl.CompileAnimationStyleMetadata { | ||||
|   private _getAnimationStyleMetadata(value: AnimationStyleMetadata): | ||||
|       cpl.CompileAnimationStyleMetadata { | ||||
|     return new cpl.CompileAnimationStyleMetadata(value.offset, value.styles); | ||||
|   } | ||||
| 
 | ||||
|   getAnimationMetadata(value: AnimationMetadata): cpl.CompileAnimationMetadata { | ||||
|   private _getAnimationMetadata(value: AnimationMetadata): cpl.CompileAnimationMetadata { | ||||
|     if (value instanceof AnimationStyleMetadata) { | ||||
|       return this.getAnimationStyleMetadata(value); | ||||
|       return this._getAnimationStyleMetadata(value); | ||||
|     } | ||||
| 
 | ||||
|     if (value instanceof AnimationKeyframesSequenceMetadata) { | ||||
|       return new cpl.CompileAnimationKeyframesSequenceMetadata( | ||||
|           value.steps.map(entry => this.getAnimationStyleMetadata(entry))); | ||||
|           value.steps.map(entry => this._getAnimationStyleMetadata(entry))); | ||||
|     } | ||||
| 
 | ||||
|     if (value instanceof AnimationAnimateMetadata) { | ||||
|       const animateData = | ||||
|           <cpl.CompileAnimationStyleMetadata|cpl.CompileAnimationKeyframesSequenceMetadata>this | ||||
|               .getAnimationMetadata(value.styles); | ||||
|               ._getAnimationMetadata(value.styles); | ||||
|       return new cpl.CompileAnimationAnimateMetadata(value.timings, animateData); | ||||
|     } | ||||
| 
 | ||||
|     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) { | ||||
|         return new cpl.CompileAnimationGroupMetadata(steps); | ||||
| @ -116,52 +134,35 @@ export class CompileMetadataResolver { | ||||
|     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); | ||||
|     let meta = this._directiveCache.get(directiveType); | ||||
|     if (!meta) { | ||||
|       const dirMeta = this._directiveResolver.resolve(directiveType, throwIfNotFound); | ||||
|       if (!dirMeta) { | ||||
|         return null; | ||||
|       } | ||||
|       let templateMeta: cpl.CompileTemplateMetadata = null; | ||||
|     const dirMeta = this._directiveResolver.resolve(directiveType); | ||||
|     if (!dirMeta) { | ||||
|       return null; | ||||
|     } | ||||
|     let moduleUrl = staticTypeModuleUrl(directiveType); | ||||
| 
 | ||||
|     const createDirectiveMetadata = (templateMeta: cpl.CompileTemplateMetadata) => { | ||||
|       let changeDetectionStrategy: ChangeDetectionStrategy = null; | ||||
|       let viewProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = []; | ||||
|       let moduleUrl = staticTypeModuleUrl(directiveType); | ||||
|       let entryComponentMetadata: cpl.CompileTypeMetadata[] = []; | ||||
|       let entryComponentMetadata: cpl.CompileIdentifierMetadata[] = []; | ||||
|       let selector = dirMeta.selector; | ||||
| 
 | ||||
|       if (dirMeta instanceof 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; | ||||
|         if (dirMeta.viewProviders) { | ||||
|           viewProviders = this.getProvidersMetadata( | ||||
|           viewProviders = this._getProvidersMetadata( | ||||
|               dirMeta.viewProviders, entryComponentMetadata, | ||||
|               `viewProviders for "${stringify(directiveType)}"`); | ||||
|         } | ||||
|         moduleUrl = componentModuleUrl(this._reflector, directiveType, dirMeta); | ||||
|         if (dirMeta.entryComponents) { | ||||
|           entryComponentMetadata = | ||||
|               flattenAndDedupeArray(dirMeta.entryComponents) | ||||
|                   .map((type) => this.getTypeMetadata(type, staticTypeModuleUrl(type))) | ||||
|                   .map((type) => this._getIdentifierMetadata(type, staticTypeModuleUrl(type))) | ||||
|                   .concat(entryComponentMetadata); | ||||
|         } | ||||
|         if (!selector) { | ||||
| @ -176,22 +177,22 @@ export class CompileMetadataResolver { | ||||
| 
 | ||||
|       let providers: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = []; | ||||
|       if (isPresent(dirMeta.providers)) { | ||||
|         providers = this.getProvidersMetadata( | ||||
|         providers = this._getProvidersMetadata( | ||||
|             dirMeta.providers, entryComponentMetadata, | ||||
|             `providers for "${stringify(directiveType)}"`); | ||||
|       } | ||||
|       let queries: cpl.CompileQueryMetadata[] = []; | ||||
|       let viewQueries: cpl.CompileQueryMetadata[] = []; | ||||
|       if (isPresent(dirMeta.queries)) { | ||||
|         queries = this.getQueriesMetadata(dirMeta.queries, false, directiveType); | ||||
|         viewQueries = this.getQueriesMetadata(dirMeta.queries, true, directiveType); | ||||
|         queries = this._getQueriesMetadata(dirMeta.queries, false, directiveType); | ||||
|         viewQueries = this._getQueriesMetadata(dirMeta.queries, true, directiveType); | ||||
|       } | ||||
| 
 | ||||
|       meta = cpl.CompileDirectiveMetadata.create({ | ||||
|       const meta = cpl.CompileDirectiveMetadata.create({ | ||||
|         selector: selector, | ||||
|         exportAs: dirMeta.exportAs, | ||||
|         isComponent: !!templateMeta, | ||||
|         type: this.getTypeMetadata(directiveType, moduleUrl), | ||||
|         type: this._getTypeMetadata(directiveType, moduleUrl), | ||||
|         template: templateMeta, | ||||
|         changeDetection: changeDetectionStrategy, | ||||
|         inputs: dirMeta.inputs, | ||||
| @ -204,189 +205,278 @@ export class CompileMetadataResolver { | ||||
|         entryComponents: entryComponentMetadata | ||||
|       }); | ||||
|       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); | ||||
|     let compileMeta = this._ngModuleCache.get(moduleType); | ||||
|     if (!compileMeta) { | ||||
|       const meta = this._ngModuleResolver.resolve(moduleType, throwIfNotFound); | ||||
|       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); | ||||
|     if (compileMeta) { | ||||
|       return 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; | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   private _verifyModule(moduleMeta: cpl.CompileNgModuleMetadata) { | ||||
|     moduleMeta.exportedDirectives.forEach((dirMeta) => { | ||||
|       if (!moduleMeta.transitiveModule.directivesSet.has(dirMeta.type.reference)) { | ||||
|     moduleMeta.exportedDirectives.forEach((dirIdentifier) => { | ||||
|       if (!moduleMeta.transitiveModule.directivesSet.has(dirIdentifier.reference)) { | ||||
|         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) => { | ||||
|       if (!moduleMeta.transitiveModule.pipesSet.has(pipeMeta.type.reference)) { | ||||
|     moduleMeta.exportedPipes.forEach((pipeIdentifier) => { | ||||
|       if (!moduleMeta.transitiveModule.pipesSet.has(pipeIdentifier.reference)) { | ||||
|         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 { | ||||
|     if (this._directiveResolver.resolve(type, false)) { | ||||
|     if (this._directiveResolver.isDirective(type)) { | ||||
|       return 'directive'; | ||||
|     } | ||||
| 
 | ||||
|     if (this._pipeResolver.resolve(type, false)) { | ||||
|     if (this._pipeResolver.isPipe(type)) { | ||||
|       return 'pipe'; | ||||
|     } | ||||
| 
 | ||||
|     if (this._ngModuleResolver.resolve(type, false)) { | ||||
|     if (this._ngModuleResolver.isNgModule(type)) { | ||||
|       return 'module'; | ||||
|     } | ||||
| 
 | ||||
| @ -397,6 +487,7 @@ export class CompileMetadataResolver { | ||||
|     return 'value'; | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   private _addTypeToModule(type: Type<any>, moduleType: Type<any>) { | ||||
|     const oldModule = this._ngModuleOfTypes.get(type); | ||||
|     if (oldModule && oldModule !== moduleType) { | ||||
| @ -421,81 +512,72 @@ export class CompileMetadataResolver { | ||||
|     const directives = | ||||
|         flattenArray(transitiveExportedModules.map((ngModule) => ngModule.exportedDirectives)); | ||||
|     const pipes = flattenArray(transitiveExportedModules.map((ngModule) => ngModule.exportedPipes)); | ||||
|     const loadingPromises = ListWrapper.flatten( | ||||
|         transitiveExportedModules.map(ngModule => ngModule.transitiveModule.loadingPromises)); | ||||
|     return new cpl.TransitiveCompileNgModuleMetadata( | ||||
|         transitiveModules, providers, entryComponents, directives, pipes); | ||||
|         transitiveModules, providers, entryComponents, directives, pipes, loadingPromises); | ||||
|   } | ||||
| 
 | ||||
|   private _addDirectiveToModule( | ||||
|       dirMeta: cpl.CompileDirectiveMetadata, moduleType: any, | ||||
|       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 { | ||||
|   private _getIdentifierMetadata(type: Type<any>, moduleUrl: string): | ||||
|       cpl.CompileIdentifierMetadata { | ||||
|     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({ | ||||
|       name: this.sanitizeTokenName(type), | ||||
|       moduleUrl, | ||||
|       reference: type, | ||||
|       diDeps: this.getDependenciesMetadata(type, dependencies), | ||||
|       lifecycleHooks: LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, type)), | ||||
|       name: identifier.name, | ||||
|       moduleUrl: identifier.moduleUrl, | ||||
|       reference: identifier.reference, | ||||
|       diDeps: this._getDependenciesMetadata(identifier.reference, dependencies), | ||||
|       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 { | ||||
|     factory = resolveForwardRef(factory); | ||||
|     return new cpl.CompileFactoryMetadata({ | ||||
|       name: this.sanitizeTokenName(factory), | ||||
|       moduleUrl, | ||||
|       reference: factory, | ||||
|       diDeps: this.getDependenciesMetadata(factory, dependencies) | ||||
|       diDeps: this._getDependenciesMetadata(factory, dependencies) | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   getPipeMetadata(pipeType: Type<any>, throwIfNotFound = true): cpl.CompilePipeMetadata { | ||||
|     pipeType = resolveForwardRef(pipeType); | ||||
|     let meta = this._pipeCache.get(pipeType); | ||||
|     if (!meta) { | ||||
|       const pipeMeta = this._pipeResolver.resolve(pipeType, throwIfNotFound); | ||||
|       if (!pipeMeta) { | ||||
|         return null; | ||||
|       } | ||||
| 
 | ||||
|       meta = new cpl.CompilePipeMetadata({ | ||||
|         type: this.getTypeMetadata(pipeType, staticTypeModuleUrl(pipeType)), | ||||
|         name: pipeMeta.name, | ||||
|         pure: pipeMeta.pure | ||||
|       }); | ||||
|       this._pipeCache.set(pipeType, meta); | ||||
|   /** | ||||
|    * Gets the metadata for the given pipe. | ||||
|    * This assumes `loadNgModuleMetadata` has been called first. | ||||
|    */ | ||||
|   getPipeMetadata(pipeType: any): cpl.CompilePipeMetadata { | ||||
|     const pipeMeta = this._pipeCache.get(pipeType); | ||||
|     if (!pipeMeta) { | ||||
|       throw new Error( | ||||
|           `Illegal state: getPipeMetadata can only be called after loadNgModuleMetadata for a module that declares it. Pipe ${stringify(pipeType)}.`); | ||||
|     } | ||||
|     return 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[] { | ||||
|     let hasUnknownDeps = false; | ||||
|     let params = dependencies || this._reflector.parameters(typeOrFunc) || []; | ||||
| @ -540,7 +622,7 @@ export class CompileMetadataResolver { | ||||
|         isSelf, | ||||
|         isSkipSelf, | ||||
|         isOptional, | ||||
|         token: this.getTokenMetadata(token) | ||||
|         token: this._getTokenMetadata(token) | ||||
|       }); | ||||
| 
 | ||||
|     }); | ||||
| @ -555,7 +637,7 @@ export class CompileMetadataResolver { | ||||
|     return dependenciesMetadata; | ||||
|   } | ||||
| 
 | ||||
|   getTokenMetadata(token: any): cpl.CompileTokenMetadata { | ||||
|   private _getTokenMetadata(token: any): cpl.CompileTokenMetadata { | ||||
|     token = resolveForwardRef(token); | ||||
|     let compileToken: cpl.CompileTokenMetadata; | ||||
|     if (typeof token === 'string') { | ||||
| @ -572,8 +654,8 @@ export class CompileMetadataResolver { | ||||
|     return compileToken; | ||||
|   } | ||||
| 
 | ||||
|   getProvidersMetadata( | ||||
|       providers: Provider[], targetEntryComponents: cpl.CompileTypeMetadata[], | ||||
|   private _getProvidersMetadata( | ||||
|       providers: Provider[], targetEntryComponents: cpl.CompileIdentifierMetadata[], | ||||
|       debugInfo?: string): Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> { | ||||
|     const compileProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = []; | ||||
|     providers.forEach((provider: any, providerIdx: number) => { | ||||
| @ -583,9 +665,9 @@ export class CompileMetadataResolver { | ||||
|       } | ||||
|       let compileProvider: cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]; | ||||
|       if (Array.isArray(provider)) { | ||||
|         compileProvider = this.getProvidersMetadata(provider, targetEntryComponents, debugInfo); | ||||
|         compileProvider = this._getProvidersMetadata(provider, targetEntryComponents, debugInfo); | ||||
|       } else if (provider instanceof cpl.ProviderMeta) { | ||||
|         let tokenMeta = this.getTokenMetadata(provider.token); | ||||
|         let tokenMeta = this._getTokenMetadata(provider.token); | ||||
|         if (tokenMeta.reference === | ||||
|             resolveIdentifierToken(Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS).reference) { | ||||
|           targetEntryComponents.push(...this._getEntryComponentsFromProvider(provider)); | ||||
| @ -593,7 +675,7 @@ export class CompileMetadataResolver { | ||||
|           compileProvider = this.getProviderMetadata(provider); | ||||
|         } | ||||
|       } else if (isValidType(provider)) { | ||||
|         compileProvider = this.getTypeMetadata(provider, staticTypeModuleUrl(provider)); | ||||
|         compileProvider = this._getTypeMetadata(provider, staticTypeModuleUrl(provider)); | ||||
|       } else { | ||||
|         const providersInfo = | ||||
|             (<string[]>providers.reduce( | ||||
| @ -620,8 +702,9 @@ export class CompileMetadataResolver { | ||||
|     return compileProviders; | ||||
|   } | ||||
| 
 | ||||
|   private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta): cpl.CompileTypeMetadata[] { | ||||
|     const components: cpl.CompileTypeMetadata[] = []; | ||||
|   private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta): | ||||
|       cpl.CompileIdentifierMetadata[] { | ||||
|     const components: cpl.CompileIdentifierMetadata[] = []; | ||||
|     const collectedIdentifiers: cpl.CompileIdentifierMetadata[] = []; | ||||
| 
 | ||||
|     if (provider.useFactory || provider.useExisting || provider.useClass) { | ||||
| @ -634,9 +717,8 @@ export class CompileMetadataResolver { | ||||
| 
 | ||||
|     convertToCompileValue(provider.useValue, collectedIdentifiers); | ||||
|     collectedIdentifiers.forEach((identifier) => { | ||||
|       const dirMeta = this.getDirectiveMetadata(identifier.reference, false); | ||||
|       if (dirMeta) { | ||||
|         components.push(dirMeta.type); | ||||
|       if (this._directiveResolver.isDirective(identifier.reference)) { | ||||
|         components.push(identifier); | ||||
|       } | ||||
|     }); | ||||
|     return components; | ||||
| @ -648,27 +730,27 @@ export class CompileMetadataResolver { | ||||
|     let compileFactoryMetadata: cpl.CompileFactoryMetadata = null; | ||||
| 
 | ||||
|     if (provider.useClass) { | ||||
|       compileTypeMetadata = this.getTypeMetadata( | ||||
|       compileTypeMetadata = this._getTypeMetadata( | ||||
|           provider.useClass, staticTypeModuleUrl(provider.useClass), provider.dependencies); | ||||
|       compileDeps = compileTypeMetadata.diDeps; | ||||
|     } else if (provider.useFactory) { | ||||
|       compileFactoryMetadata = this.getFactoryMetadata( | ||||
|       compileFactoryMetadata = this._getFactoryMetadata( | ||||
|           provider.useFactory, staticTypeModuleUrl(provider.useFactory), provider.dependencies); | ||||
|       compileDeps = compileFactoryMetadata.diDeps; | ||||
|     } | ||||
| 
 | ||||
|     return new cpl.CompileProviderMetadata({ | ||||
|       token: this.getTokenMetadata(provider.token), | ||||
|       token: this._getTokenMetadata(provider.token), | ||||
|       useClass: compileTypeMetadata, | ||||
|       useValue: convertToCompileValue(provider.useValue, []), | ||||
|       useFactory: compileFactoryMetadata, | ||||
|       useExisting: provider.useExisting ? this.getTokenMetadata(provider.useExisting) : null, | ||||
|       useExisting: provider.useExisting ? this._getTokenMetadata(provider.useExisting) : null, | ||||
|       deps: compileDeps, | ||||
|       multi: provider.multi | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   getQueriesMetadata( | ||||
|   private _getQueriesMetadata( | ||||
|       queries: {[key: string]: Query}, isViewQuery: boolean, | ||||
|       directiveType: Type<any>): cpl.CompileQueryMetadata[] { | ||||
|     const res: cpl.CompileQueryMetadata[] = []; | ||||
| @ -676,7 +758,7 @@ export class CompileMetadataResolver { | ||||
|     Object.keys(queries).forEach((propertyName: string) => { | ||||
|       const query = queries[propertyName]; | ||||
|       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*/); } | ||||
| 
 | ||||
|   getQueryMetadata(q: Query, propertyName: string, typeOrFunc: Type<any>|Function): | ||||
|   private _getQueryMetadata(q: Query, propertyName: string, typeOrFunc: Type<any>|Function): | ||||
|       cpl.CompileQueryMetadata { | ||||
|     var selectors: cpl.CompileTokenMetadata[]; | ||||
|     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 { | ||||
|       if (!q.selector) { | ||||
|         throw new Error( | ||||
|             `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({ | ||||
|       selectors, | ||||
|       first: q.first, | ||||
|       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 { | ||||
|   constructor(private _reflector: ReflectorReader = reflector) {} | ||||
| 
 | ||||
|   isNgModule(type: any) { return this._reflector.annotations(type).some(_isNgModuleMetadata); } | ||||
| 
 | ||||
|   resolve(type: Type<any>, throwIfNotFound = true): NgModule { | ||||
|     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
 | ||||
| export function analyzeNgModules( | ||||
|     programStaticSymbols: StaticSymbol[], options: {transitiveModules: boolean}, | ||||
|     metadataResolver: CompileMetadataResolver): { | ||||
|     metadataResolver: CompileMetadataResolver): Promise<{ | ||||
|   ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>, | ||||
|   files: Array<{srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}> | ||||
| } { | ||||
|   const { | ||||
|     ngModules: programNgModules, | ||||
|     pipesAndDirectives: programPipesOrDirectives, | ||||
|   } = _extractModulesAndPipesOrDirectives(programStaticSymbols, metadataResolver); | ||||
| }> { | ||||
|   return _loadNgModules(programStaticSymbols, options, metadataResolver).then(_analyzeNgModules); | ||||
| } | ||||
| 
 | ||||
| function _analyzeNgModules(ngModuleMetas: CompileNgModuleMetadata[]) { | ||||
|   const moduleMetasByRef = new Map<any, CompileNgModuleMetadata>(); | ||||
| 
 | ||||
|   programNgModules.forEach(modMeta => { | ||||
|     if (options.transitiveModules) { | ||||
|       // For every input modules add the list of transitively included modules
 | ||||
|       modMeta.transitiveModule.modules.forEach( | ||||
|           modMeta => { moduleMetasByRef.set(modMeta.type.reference, modMeta); }); | ||||
|     } else { | ||||
|       moduleMetasByRef.set(modMeta.type.reference, modMeta); | ||||
|     } | ||||
|   }); | ||||
| 
 | ||||
|   const ngModuleMetas = Array.from(moduleMetasByRef.values()); | ||||
|   ngModuleMetas.forEach((ngModule) => moduleMetasByRef.set(ngModule.type.reference, ngModule)); | ||||
|   const ngModuleByPipeOrDirective = new Map<StaticSymbol, CompileNgModuleMetadata>(); | ||||
|   const ngModulesByFile = new Map<string, StaticSymbol[]>(); | ||||
|   const ngDirectivesByFile = new Map<string, StaticSymbol[]>(); | ||||
| @ -67,31 +55,20 @@ export function analyzeNgModules( | ||||
|     ngModulesByFile.set( | ||||
|         srcFileUrl, (ngModulesByFile.get(srcFileUrl) || []).concat(ngModuleMeta.type.reference)); | ||||
| 
 | ||||
|     ngModuleMeta.declaredDirectives.forEach((dirMeta: CompileDirectiveMetadata) => { | ||||
|       const fileUrl = dirMeta.type.reference.filePath; | ||||
|     ngModuleMeta.declaredDirectives.forEach((dirIdentifier) => { | ||||
|       const fileUrl = dirIdentifier.reference.filePath; | ||||
|       filePaths.add(fileUrl); | ||||
|       ngDirectivesByFile.set( | ||||
|           fileUrl, (ngDirectivesByFile.get(fileUrl) || []).concat(dirMeta.type.reference)); | ||||
|       ngModuleByPipeOrDirective.set(dirMeta.type.reference, ngModuleMeta); | ||||
|           fileUrl, (ngDirectivesByFile.get(fileUrl) || []).concat(dirIdentifier.reference)); | ||||
|       ngModuleByPipeOrDirective.set(dirIdentifier.reference, ngModuleMeta); | ||||
|     }); | ||||
| 
 | ||||
|     ngModuleMeta.declaredPipes.forEach((pipeMeta: CompilePipeMetadata) => { | ||||
|       const fileUrl = pipeMeta.type.reference.filePath; | ||||
|     ngModuleMeta.declaredPipes.forEach((pipeIdentifier) => { | ||||
|       const fileUrl = pipeIdentifier.reference.filePath; | ||||
|       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[]}[] = []; | ||||
| 
 | ||||
|   filePaths.forEach((srcUrl) => { | ||||
| @ -112,35 +89,29 @@ export class OfflineCompiler { | ||||
|   private _animationCompiler = new AnimationCompiler(); | ||||
| 
 | ||||
|   constructor( | ||||
|       private _metadataResolver: CompileMetadataResolver, | ||||
|       private _directiveNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser, | ||||
|       private _metadataResolver: CompileMetadataResolver, private _templateParser: TemplateParser, | ||||
|       private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler, | ||||
|       private _dirWrapperCompiler: DirectiveWrapperCompiler, | ||||
|       private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter, | ||||
|       private _localeId: string, private _translationFormat: string, | ||||
|       private _animationParser: AnimationParser) {} | ||||
| 
 | ||||
|   clearCache() { | ||||
|     this._directiveNormalizer.clearCache(); | ||||
|     this._metadataResolver.clearCache(); | ||||
|   } | ||||
|   clearCache() { this._metadataResolver.clearCache(); } | ||||
| 
 | ||||
|   compileModules(staticSymbols: StaticSymbol[], options: {transitiveModules: boolean}): | ||||
|       Promise<SourceModule[]> { | ||||
|     const {ngModuleByPipeOrDirective, files} = | ||||
|         analyzeNgModules(staticSymbols, options, this._metadataResolver); | ||||
| 
 | ||||
|     const sourceModules = files.map( | ||||
|         file => this._compileSrcFile( | ||||
|             file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.ngModules)); | ||||
| 
 | ||||
|     return Promise.all(sourceModules) | ||||
|         .then((modules: SourceModule[][]) => ListWrapper.flatten(modules)); | ||||
|     return analyzeNgModules(staticSymbols, options, this._metadataResolver) | ||||
|         .then(({ngModuleByPipeOrDirective, files}) => { | ||||
|           const sourceModules = files.map( | ||||
|               file => this._compileSrcFile( | ||||
|                   file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.ngModules)); | ||||
|           return ListWrapper.flatten(sourceModules); | ||||
|         }); | ||||
|   } | ||||
| 
 | ||||
|   private _compileSrcFile( | ||||
|       srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>, | ||||
|       directives: StaticSymbol[], ngModules: StaticSymbol[]): Promise<SourceModule[]> { | ||||
|       directives: StaticSymbol[], ngModules: StaticSymbol[]): SourceModule[] { | ||||
|     const fileSuffix = _splitTypescriptSuffix(srcFileUrl)[1]; | ||||
|     const statements: o.Statement[] = []; | ||||
|     const exportedVars: string[] = []; | ||||
| @ -155,48 +126,38 @@ export class OfflineCompiler { | ||||
|         (directiveType) => this._compileDirectiveWrapper(directiveType, statements))); | ||||
| 
 | ||||
|     // compile components
 | ||||
|     return Promise | ||||
|         .all(directives.map((dirType) => { | ||||
|           const compMeta = this._metadataResolver.getDirectiveMetadata(<any>dirType); | ||||
|           if (!compMeta.isComponent) { | ||||
|             return Promise.resolve(null); | ||||
|           } | ||||
|           const ngModule = ngModuleByPipeOrDirective.get(dirType); | ||||
|           if (!ngModule) { | ||||
|             throw new Error( | ||||
|                 `Internal Error: cannot determine the module for component ${compMeta.type.name}!`); | ||||
|           } | ||||
|     directives.forEach((dirType) => { | ||||
|       const compMeta = this._metadataResolver.getDirectiveMetadata(<any>dirType); | ||||
|       if (!compMeta.isComponent) { | ||||
|         return Promise.resolve(null); | ||||
|       } | ||||
|       const ngModule = ngModuleByPipeOrDirective.get(dirType); | ||||
|       if (!ngModule) { | ||||
|         throw new Error( | ||||
|             `Internal Error: cannot determine the module for component ${compMeta.type.name}!`); | ||||
|       } | ||||
| 
 | ||||
|           return Promise | ||||
|               .all([compMeta, ...ngModule.transitiveModule.directives].map( | ||||
|                   dirMeta => this._directiveNormalizer.normalizeDirective(dirMeta).asyncResult)) | ||||
|               .then((normalizedCompWithDirectives) => { | ||||
|                 const [compMeta, ...dirMetas] = normalizedCompWithDirectives; | ||||
|                 _assertComponent(compMeta); | ||||
|       _assertComponent(compMeta); | ||||
| 
 | ||||
|                 // compile styles
 | ||||
|                 const stylesCompileResults = this._styleCompiler.compileComponent(compMeta); | ||||
|                 stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => { | ||||
|                   outputSourceModules.push( | ||||
|                       this._codgenStyles(srcFileUrl, compiledStyleSheet, fileSuffix)); | ||||
|                 }); | ||||
|       // compile styles
 | ||||
|       const stylesCompileResults = this._styleCompiler.compileComponent(compMeta); | ||||
|       stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => { | ||||
|         outputSourceModules.push(this._codgenStyles(srcFileUrl, compiledStyleSheet, fileSuffix)); | ||||
|       }); | ||||
| 
 | ||||
|                 // compile components
 | ||||
|                 exportedVars.push( | ||||
|                     this._compileComponentFactory(compMeta, fileSuffix, statements), | ||||
|                     this._compileComponent( | ||||
|                         compMeta, dirMetas, ngModule.transitiveModule.pipes, ngModule.schemas, | ||||
|                         stylesCompileResults.componentStylesheet, fileSuffix, statements)); | ||||
|               }); | ||||
|         })) | ||||
|         .then(() => { | ||||
|           if (statements.length > 0) { | ||||
|             const srcModule = this._codegenSourceModule( | ||||
|                 srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars); | ||||
|             outputSourceModules.unshift(srcModule); | ||||
|           } | ||||
|           return outputSourceModules; | ||||
|         }); | ||||
|       // compile components
 | ||||
|       exportedVars.push( | ||||
|           this._compileComponentFactory(compMeta, ngModule, fileSuffix, statements), | ||||
|           this._compileComponent( | ||||
|               compMeta, ngModule, ngModule.transitiveModule.directives, | ||||
|               stylesCompileResults.componentStylesheet, fileSuffix, statements)); | ||||
|     }); | ||||
|     if (statements.length > 0) { | ||||
|       const srcModule = this._codegenSourceModule( | ||||
|           srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars); | ||||
|       outputSourceModules.unshift(srcModule); | ||||
|     } | ||||
|     return outputSourceModules; | ||||
|   } | ||||
| 
 | ||||
|   private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string { | ||||
| @ -238,11 +199,11 @@ export class OfflineCompiler { | ||||
|   } | ||||
| 
 | ||||
|   private _compileComponentFactory( | ||||
|       compMeta: CompileDirectiveMetadata, fileSuffix: string, | ||||
|       compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: string, | ||||
|       targetStatements: o.Statement[]): string { | ||||
|     const hostMeta = createHostComponentMeta(compMeta); | ||||
|     const hostViewFactoryVar = | ||||
|         this._compileComponent(hostMeta, [compMeta], [], [], null, fileSuffix, targetStatements); | ||||
|     const hostViewFactoryVar = this._compileComponent( | ||||
|         hostMeta, ngModule, [compMeta.type], null, fileSuffix, targetStatements); | ||||
|     const compFactoryVar = _componentFactoryName(compMeta.type); | ||||
|     targetStatements.push( | ||||
|         o.variable(compFactoryVar) | ||||
| @ -262,12 +223,18 @@ export class OfflineCompiler { | ||||
|   } | ||||
| 
 | ||||
|   private _compileComponent( | ||||
|       compMeta: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[], | ||||
|       pipes: CompilePipeMetadata[], schemas: SchemaMetadata[], componentStyles: CompiledStylesheet, | ||||
|       compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, | ||||
|       directiveIdentifiers: CompileIdentifierMetadata[], componentStyles: CompiledStylesheet, | ||||
|       fileSuffix: string, targetStatements: o.Statement[]): string { | ||||
|     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( | ||||
|         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 compiledAnimations = | ||||
|         this._animationCompiler.compile(compMeta.type.name, parsedAnimations); | ||||
| @ -358,27 +325,50 @@ function _splitTypescriptSuffix(path: string): string[] { | ||||
|   return [path, '']; | ||||
| } | ||||
| 
 | ||||
| // Group the symbols by types:
 | ||||
| // - NgModules,
 | ||||
| // - Pipes and Directives.
 | ||||
| function _extractModulesAndPipesOrDirectives( | ||||
|     programStaticSymbols: StaticSymbol[], metadataResolver: CompileMetadataResolver) { | ||||
|   const ngModules: CompileNgModuleMetadata[] = []; | ||||
|   const pipesAndDirectives: StaticSymbol[] = []; | ||||
| 
 | ||||
|   programStaticSymbols.forEach(staticSymbol => { | ||||
|     const ngModule = metadataResolver.getNgModuleMetadata(staticSymbol, false); | ||||
|     const directive = metadataResolver.getDirectiveMetadata(staticSymbol, false); | ||||
|     const pipe = metadataResolver.getPipeMetadata(<any>staticSymbol, false); | ||||
| // Load the NgModules and check
 | ||||
| // that all directives / pipes that are present in the program
 | ||||
| // are also declared by a module.
 | ||||
| function _loadNgModules( | ||||
|     programStaticSymbols: StaticSymbol[], options: {transitiveModules: boolean}, | ||||
|     metadataResolver: CompileMetadataResolver): Promise<CompileNgModuleMetadata[]> { | ||||
|   const ngModules = new Map<any, CompileNgModuleMetadata>(); | ||||
|   const programPipesAndDirectives: StaticSymbol[] = []; | ||||
|   const ngModulePipesAndDirective = new Set<StaticSymbol>(); | ||||
|   const loadingPromises: Promise<any>[] = []; | ||||
| 
 | ||||
|   const addNgModule = (staticSymbol: any) => { | ||||
|     if (ngModules.has(staticSymbol)) { | ||||
|       return false; | ||||
|     } | ||||
|     const {ngModule, loading} = metadataResolver.loadNgModuleMetadata(staticSymbol, false, false); | ||||
|     if (ngModule) { | ||||
|       ngModules.push(ngModule); | ||||
|     } else if (directive) { | ||||
|       pipesAndDirectives.push(staticSymbol); | ||||
|     } else if (pipe) { | ||||
|       pipesAndDirectives.push(staticSymbol); | ||||
|       ngModules.set(ngModule.type.reference, ngModule); | ||||
|       loadingPromises.push(loading); | ||||
|       ngModule.declaredDirectives.forEach((dir) => ngModulePipesAndDirective.add(dir.reference)); | ||||
|       ngModule.declaredPipes.forEach((pipe) => ngModulePipesAndDirective.add(pipe.reference)); | ||||
|       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 { | ||||
|   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`. | ||||
|    */ | ||||
|  | ||||
| @ -20,7 +20,6 @@ import {NgModuleCompiler} from './ng_module_compiler'; | ||||
| import * as ir from './output/output_ast'; | ||||
| import {interpretStatements} from './output/output_interpreter'; | ||||
| import {jitStatements} from './output/output_jit'; | ||||
| import {ComponentStillLoadingError} from './private_import_core'; | ||||
| import {CompiledStylesheet, StyleCompiler} from './style_compiler'; | ||||
| import {TemplateParser} from './template_parser/template_parser'; | ||||
| import {SyncAsyncResult} from './util'; | ||||
| @ -47,9 +46,8 @@ export class RuntimeCompiler implements Compiler { | ||||
| 
 | ||||
|   constructor( | ||||
|       private _injector: Injector, private _metadataResolver: CompileMetadataResolver, | ||||
|       private _templateNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser, | ||||
|       private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler, | ||||
|       private _ngModuleCompiler: NgModuleCompiler, | ||||
|       private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler, | ||||
|       private _viewCompiler: ViewCompiler, private _ngModuleCompiler: NgModuleCompiler, | ||||
|       private _directiveWrapperCompiler: DirectiveWrapperCompiler, | ||||
|       private _compilerConfig: CompilerConfig, private _animationParser: AnimationParser) {} | ||||
| 
 | ||||
| @ -74,38 +72,47 @@ export class RuntimeCompiler implements Compiler { | ||||
| 
 | ||||
|   private _compileModuleAndComponents<T>(moduleType: Type<T>, isSync: boolean): | ||||
|       SyncAsyncResult<NgModuleFactory<T>> { | ||||
|     const componentPromise = this._compileComponents(moduleType, isSync); | ||||
|     const ngModuleFactory = this._compileModule(moduleType); | ||||
|     return new SyncAsyncResult(ngModuleFactory, componentPromise.then(() => ngModuleFactory)); | ||||
|     const loadingPromise = this._loadModules(moduleType, isSync); | ||||
|     const createResult = () => { | ||||
|       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): | ||||
|       SyncAsyncResult<ModuleWithComponentFactories<T>> { | ||||
|     const componentPromise = this._compileComponents(moduleType, isSync); | ||||
|     const ngModuleFactory = this._compileModule(moduleType); | ||||
|     const moduleMeta = this._metadataResolver.getNgModuleMetadata(moduleType); | ||||
|     const componentFactories: ComponentFactory<any>[] = []; | ||||
|     const templates = new Set<CompiledTemplate>(); | ||||
|     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 loadingPromise = this._loadModules(moduleType, isSync); | ||||
|     const createResult = () => { | ||||
|       const componentFactories: ComponentFactory<any>[] = []; | ||||
|       this._compileComponents(moduleType, componentFactories); | ||||
|       return new ModuleWithComponentFactories(this._compileModule(moduleType), componentFactories); | ||||
|     }; | ||||
|     const asyncResult = isSync ? Promise.resolve(compile()) : componentPromise.then(compile); | ||||
|     return new SyncAsyncResult(syncResult, asyncResult); | ||||
|     if (isSync) { | ||||
|       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> { | ||||
| @ -137,23 +144,30 @@ export class RuntimeCompiler implements Compiler { | ||||
|   /** | ||||
|    * @internal | ||||
|    */ | ||||
|   _compileComponents(mainModule: Type<any>, isSync: boolean): Promise<any> { | ||||
|     const templates = new Set<CompiledTemplate>(); | ||||
|     var loadingPromises: Promise<any>[] = []; | ||||
| 
 | ||||
|   _compileComponents(mainModule: Type<any>, allComponentFactories: ComponentFactory<any>[]) { | ||||
|     const ngModule = this._metadataResolver.getNgModuleMetadata(mainModule); | ||||
|     const moduleByDirective = new Map<any, CompileNgModuleMetadata>(); | ||||
|     const templates = new Set<CompiledTemplate>(); | ||||
| 
 | ||||
|     ngModule.transitiveModule.modules.forEach((localModuleMeta) => { | ||||
|       localModuleMeta.declaredDirectives.forEach((dirMeta) => { | ||||
|         moduleByDirective.set(dirMeta.type.reference, localModuleMeta); | ||||
|       localModuleMeta.declaredDirectives.forEach((dirIdentifier) => { | ||||
|         moduleByDirective.set(dirIdentifier.reference, localModuleMeta); | ||||
|         const dirMeta = this._metadataResolver.getDirectiveMetadata(dirIdentifier.reference); | ||||
|         this._compileDirectiveWrapper(dirMeta, localModuleMeta); | ||||
|         if (dirMeta.isComponent) { | ||||
|           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) => { | ||||
|       localModuleMeta.declaredDirectives.forEach((dirMeta) => { | ||||
|       localModuleMeta.declaredDirectives.forEach((dirIdentifier) => { | ||||
|         const dirMeta = this._metadataResolver.getDirectiveMetadata(dirIdentifier.reference); | ||||
|         if (dirMeta.isComponent) { | ||||
|           dirMeta.entryComponents.forEach((entryComponentType) => { | ||||
|             const moduleMeta = moduleByDirective.get(entryComponentType.reference); | ||||
| @ -167,24 +181,7 @@ export class RuntimeCompiler implements Compiler { | ||||
|         templates.add(this._createCompiledHostTemplate(entryComponentType.reference, moduleMeta)); | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     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); | ||||
|     } | ||||
|     templates.forEach((template) => this._compileTemplate(template)); | ||||
|   } | ||||
| 
 | ||||
|   clearCacheFor(type: Type<any>) { | ||||
| @ -193,7 +190,6 @@ export class RuntimeCompiler implements Compiler { | ||||
|     this._compiledHostTemplateCache.delete(type); | ||||
|     var compiledTemplate = this._compiledTemplateCache.get(type); | ||||
|     if (compiledTemplate) { | ||||
|       this._templateNormalizer.clearCacheFor(compiledTemplate.normalizedCompMeta); | ||||
|       this._compiledTemplateCache.delete(type); | ||||
|     } | ||||
|   } | ||||
| @ -202,7 +198,6 @@ export class RuntimeCompiler implements Compiler { | ||||
|     this._metadataResolver.clearCache(); | ||||
|     this._compiledTemplateCache.clear(); | ||||
|     this._compiledHostTemplateCache.clear(); | ||||
|     this._templateNormalizer.clearCache(); | ||||
|     this._compiledNgModuleCache.clear(); | ||||
|   } | ||||
| 
 | ||||
| @ -218,8 +213,7 @@ export class RuntimeCompiler implements Compiler { | ||||
|       assertComponent(compMeta); | ||||
|       var hostMeta = createHostComponentMeta(compMeta); | ||||
|       compiledTemplate = new CompiledTemplate( | ||||
|           true, compMeta.selector, compMeta.type, ngModule, [compMeta], | ||||
|           this._templateNormalizer.normalizeDirective(hostMeta)); | ||||
|           true, compMeta.selector, compMeta.type, hostMeta, ngModule, [compMeta.type]); | ||||
|       this._compiledHostTemplateCache.set(compType, compiledTemplate); | ||||
|     } | ||||
|     return compiledTemplate; | ||||
| @ -231,8 +225,8 @@ export class RuntimeCompiler implements Compiler { | ||||
|     if (!compiledTemplate) { | ||||
|       assertComponent(compMeta); | ||||
|       compiledTemplate = new CompiledTemplate( | ||||
|           false, compMeta.selector, compMeta.type, ngModule, ngModule.transitiveModule.directives, | ||||
|           this._templateNormalizer.normalizeDirective(compMeta)); | ||||
|           false, compMeta.selector, compMeta.type, compMeta, ngModule, | ||||
|           ngModule.transitiveModule.directives); | ||||
|       this._compiledTemplateCache.set(compMeta.type.reference, compiledTemplate); | ||||
|     } | ||||
|     return compiledTemplate; | ||||
| @ -243,16 +237,7 @@ export class RuntimeCompiler implements Compiler { | ||||
|                                       this._compiledTemplateCache.get(compType); | ||||
|     if (!compiledTemplate) { | ||||
|       throw new Error( | ||||
|           `Illegal state: Compiled view for component ${stringify(compType)} 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!`); | ||||
|           `Illegal state: Compiled view for component ${stringify(compType)} (host: ${isHost}) does not exist!`); | ||||
|     } | ||||
|     return compiledTemplate; | ||||
|   } | ||||
| @ -285,34 +270,36 @@ export class RuntimeCompiler implements Compiler { | ||||
|     if (template.isCompiled) { | ||||
|       return; | ||||
|     } | ||||
|     const compMeta = template.normalizedCompMeta; | ||||
|     const compMeta = template.compMeta; | ||||
|     const externalStylesheetsByModuleUrl = new Map<string, CompiledStylesheet>(); | ||||
|     const stylesCompileResult = this._styleCompiler.compileComponent(compMeta); | ||||
|     stylesCompileResult.externalStylesheets.forEach( | ||||
|         (r) => { externalStylesheetsByModuleUrl.set(r.meta.moduleUrl, r); }); | ||||
|     this._resolveStylesCompileResult( | ||||
|         stylesCompileResult.componentStylesheet, externalStylesheetsByModuleUrl); | ||||
|     const viewCompMetas = template.viewComponentTypes.map( | ||||
|         (compType) => this._assertComponentLoaded(compType, false).normalizedCompMeta); | ||||
|     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( | ||||
|         compMeta, compMeta.template.template, template.viewDirectives.concat(viewCompMetas), | ||||
|         template.viewPipes, template.schemas, compMeta.type.name); | ||||
|         compMeta, compMeta.template.template, directives, pipes, template.ngModule.schemas, | ||||
|         compMeta.type.name); | ||||
|     const compiledAnimations = | ||||
|         this._animationCompiler.compile(compMeta.type.name, parsedAnimations); | ||||
|     const compileResult = this._viewCompiler.compileComponent( | ||||
|         compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar), | ||||
|         template.viewPipes, compiledAnimations); | ||||
|         pipes, compiledAnimations); | ||||
|     compileResult.dependencies.forEach((dep) => { | ||||
|       let depTemplate: CompiledTemplate; | ||||
|       if (dep instanceof ViewClassDependency) { | ||||
|         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.name = `View_${vfd.comp.name}`; | ||||
|       } else if (dep instanceof ComponentFactoryDependency) { | ||||
|         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.name = `compFactory_${cfd.comp.name}`; | ||||
|       } else if (dep instanceof DirectiveWrapperDependency) { | ||||
| @ -361,29 +348,12 @@ class CompiledTemplate { | ||||
|   private _viewClass: Function = null; | ||||
|   proxyViewClass: Type<any>; | ||||
|   proxyComponentFactory: ComponentFactory<any>; | ||||
|   loading: Promise<any> = null; | ||||
|   private _normalizedCompMeta: CompileDirectiveMetadata = null; | ||||
|   isCompiled = false; | ||||
|   isCompiledWithDeps = false; | ||||
|   viewComponentTypes: Type<any>[] = []; | ||||
|   viewDirectives: CompileDirectiveMetadata[] = []; | ||||
|   viewPipes: CompilePipeMetadata[]; | ||||
|   schemas: SchemaMetadata[]; | ||||
| 
 | ||||
|   constructor( | ||||
|       public isHost: boolean, selector: string, public compType: CompileIdentifierMetadata, | ||||
|       public ngModule: CompileNgModuleMetadata, | ||||
|       viewDirectiveAndComponents: CompileDirectiveMetadata[], | ||||
|       _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); | ||||
|       } | ||||
|     }); | ||||
|       public compMeta: CompileDirectiveMetadata, public ngModule: CompileNgModuleMetadata, | ||||
|       public directives: CompileIdentifierMetadata[]) { | ||||
|     const self = this; | ||||
|     this.proxyViewClass = <any>function() { | ||||
|       if (!self._viewClass) { | ||||
| @ -395,21 +365,6 @@ class CompiledTemplate { | ||||
|     this.proxyComponentFactory = isHost ? | ||||
|         new ComponentFactory<any>(selector, this.proxyViewClass, compType.reference) : | ||||
|         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) { | ||||
| @ -417,8 +372,6 @@ class CompiledTemplate { | ||||
|     this.proxyViewClass.prototype = viewClass.prototype; | ||||
|     this.isCompiled = true; | ||||
|   } | ||||
| 
 | ||||
|   depsCompiled() { this.isCompiledWithDeps = true; } | ||||
| } | ||||
| 
 | ||||
| function assertComponent(meta: CompileDirectiveMetadata) { | ||||
|  | ||||
| @ -18,79 +18,78 @@ import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@ang | ||||
| 
 | ||||
| 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() { | ||||
|   describe('DirectiveNormalizer', () => { | ||||
|     var dirType: CompileTypeMetadata; | ||||
|     var dirTypeWithHttpUrl: CompileTypeMetadata; | ||||
| 
 | ||||
|     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', () => { | ||||
|       it('should throw if no template was specified', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            expect(() => normalizer.normalizeDirective(new CompileDirectiveMetadata({ | ||||
|              type: dirType, | ||||
|              isComponent: true, | ||||
|              template: | ||||
|                  new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}) | ||||
|            }))).toThrowError('No template specified for component SomeComp'); | ||||
|            expect(() => normalizer.normalizeTemplate({ | ||||
|              componentType: SomeComp, | ||||
|              moduleUrl: SOME_MODULE_URL, | ||||
|            })).toThrowError('No template specified for component SomeComp'); | ||||
|          })); | ||||
|     }); | ||||
| 
 | ||||
|     describe('normalizeTemplateSync', () => { | ||||
|       it('should store the template', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            let template = normalizer.normalizeTemplateSync(dirType, new CompileTemplateMetadata({ | ||||
|                                                              encapsulation: null, | ||||
|                                                              template: 'a', | ||||
|                                                              templateUrl: null, | ||||
|                                                              styles: [], | ||||
|                                                              styleUrls: [] | ||||
|                                                            })); | ||||
|            let template = normalizer.normalizeTemplateSync({ | ||||
|              componentType: SomeComp, | ||||
|              moduleUrl: SOME_MODULE_URL, | ||||
|              encapsulation: null, | ||||
|              template: 'a', | ||||
|              templateUrl: null, | ||||
|              styles: [], | ||||
|              styleUrls: [] | ||||
|            }); | ||||
|            expect(template.template).toEqual('a'); | ||||
|            expect(template.templateUrl).toEqual('package:some/module/a.js'); | ||||
|          })); | ||||
| 
 | ||||
|       it('should resolve styles on the annotation against the moduleUrl', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            let template = normalizer.normalizeTemplateSync(dirType, new CompileTemplateMetadata({ | ||||
|                                                              encapsulation: null, | ||||
|                                                              template: '', | ||||
|                                                              templateUrl: null, | ||||
|                                                              styles: [], | ||||
|                                                              styleUrls: ['test.css'] | ||||
|                                                            })); | ||||
|            let template = normalizer.normalizeTemplateSync({ | ||||
|              componentType: SomeComp, | ||||
|              moduleUrl: SOME_MODULE_URL, | ||||
|              encapsulation: null, | ||||
|              template: '', | ||||
|              templateUrl: null, | ||||
|              styles: [], | ||||
|              styleUrls: ['test.css'] | ||||
|            }); | ||||
|            expect(template.styleUrls).toEqual(['package:some/module/test.css']); | ||||
|          })); | ||||
| 
 | ||||
|       it('should resolve styles in the template against the moduleUrl', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            let template = | ||||
|                normalizer.normalizeTemplateSync(dirType, new CompileTemplateMetadata({ | ||||
|                                                   encapsulation: null, | ||||
|                                                   template: '<style>@import test.css</style>', | ||||
|                                                   templateUrl: null, | ||||
|                                                   styles: [], | ||||
|                                                   styleUrls: [] | ||||
|                                                 })); | ||||
|            let template = normalizer.normalizeTemplateSync({ | ||||
|              componentType: SomeComp, | ||||
|              moduleUrl: SOME_MODULE_URL, | ||||
|              encapsulation: null, | ||||
|              template: '<style>@import test.css</style>', | ||||
|              templateUrl: null, | ||||
|              styles: [], | ||||
|              styleUrls: [] | ||||
|            }); | ||||
|            expect(template.styleUrls).toEqual(['package:some/module/test.css']); | ||||
|          })); | ||||
| 
 | ||||
|       it('should use ViewEncapsulation.Emulated by default', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            let template = normalizer.normalizeTemplateSync(dirType, new CompileTemplateMetadata({ | ||||
|                                                              encapsulation: null, | ||||
|                                                              template: '', | ||||
|                                                              templateUrl: null, | ||||
|                                                              styles: [], | ||||
|                                                              styleUrls: ['test.css'] | ||||
|                                                            })); | ||||
|            let template = normalizer.normalizeTemplateSync({ | ||||
|              componentType: SomeComp, | ||||
|              moduleUrl: SOME_MODULE_URL, | ||||
|              encapsulation: null, | ||||
|              template: '', | ||||
|              templateUrl: null, | ||||
|              styles: [], | ||||
|              styleUrls: ['test.css'] | ||||
|            }); | ||||
|            expect(template.encapsulation).toEqual(ViewEncapsulation.Emulated); | ||||
|          })); | ||||
| 
 | ||||
| @ -99,14 +98,15 @@ export function main() { | ||||
|              [CompilerConfig, DirectiveNormalizer], | ||||
|              (config: CompilerConfig, normalizer: DirectiveNormalizer) => { | ||||
|                config.defaultEncapsulation = ViewEncapsulation.None; | ||||
|                let template = | ||||
|                    normalizer.normalizeTemplateSync(dirType, new CompileTemplateMetadata({ | ||||
|                                                       encapsulation: null, | ||||
|                                                       template: '', | ||||
|                                                       templateUrl: null, | ||||
|                                                       styles: [], | ||||
|                                                       styleUrls: ['test.css'] | ||||
|                                                     })); | ||||
|                let template = normalizer.normalizeTemplateSync({ | ||||
|                  componentType: SomeComp, | ||||
|                  moduleUrl: SOME_MODULE_URL, | ||||
|                  encapsulation: null, | ||||
|                  template: '', | ||||
|                  templateUrl: null, | ||||
|                  styles: [], | ||||
|                  styleUrls: ['test.css'] | ||||
|                }); | ||||
|                expect(template.encapsulation).toEqual(ViewEncapsulation.None); | ||||
|              })); | ||||
|     }); | ||||
| @ -120,13 +120,15 @@ export function main() { | ||||
|               resourceLoader: MockResourceLoader) => { | ||||
|                resourceLoader.expect('package:some/module/sometplurl.html', 'a'); | ||||
|                normalizer | ||||
|                    .normalizeTemplateAsync(dirType, new CompileTemplateMetadata({ | ||||
|                                              encapsulation: null, | ||||
|                                              template: null, | ||||
|                                              templateUrl: 'sometplurl.html', | ||||
|                                              styles: [], | ||||
|                                              styleUrls: ['test.css'] | ||||
|                                            })) | ||||
|                    .normalizeTemplateAsync({ | ||||
|                      componentType: SomeComp, | ||||
|                      moduleUrl: SOME_MODULE_URL, | ||||
|                      encapsulation: null, | ||||
|                      template: null, | ||||
|                      templateUrl: 'sometplurl.html', | ||||
|                      styles: [], | ||||
|                      styleUrls: ['test.css'] | ||||
|                    }) | ||||
|                    .then((template: CompileTemplateMetadata) => { | ||||
|                      expect(template.template).toEqual('a'); | ||||
|                      expect(template.templateUrl).toEqual('package:some/module/sometplurl.html'); | ||||
| @ -142,13 +144,15 @@ export function main() { | ||||
|               resourceLoader: MockResourceLoader) => { | ||||
|                resourceLoader.expect('package:some/module/tpl/sometplurl.html', ''); | ||||
|                normalizer | ||||
|                    .normalizeTemplateAsync(dirType, new CompileTemplateMetadata({ | ||||
|                                              encapsulation: null, | ||||
|                                              template: null, | ||||
|                                              templateUrl: 'tpl/sometplurl.html', | ||||
|                                              styles: [], | ||||
|                                              styleUrls: ['test.css'] | ||||
|                                            })) | ||||
|                    .normalizeTemplateAsync({ | ||||
|                      componentType: SomeComp, | ||||
|                      moduleUrl: SOME_MODULE_URL, | ||||
|                      encapsulation: null, | ||||
|                      template: null, | ||||
|                      templateUrl: 'tpl/sometplurl.html', | ||||
|                      styles: [], | ||||
|                      styleUrls: ['test.css'] | ||||
|                    }) | ||||
|                    .then((template: CompileTemplateMetadata) => { | ||||
|                      expect(template.styleUrls).toEqual(['package:some/module/test.css']); | ||||
|                      async.done(); | ||||
| @ -164,13 +168,15 @@ export function main() { | ||||
|                resourceLoader.expect( | ||||
|                    'package:some/module/tpl/sometplurl.html', '<style>@import test.css</style>'); | ||||
|                normalizer | ||||
|                    .normalizeTemplateAsync(dirType, new CompileTemplateMetadata({ | ||||
|                                              encapsulation: null, | ||||
|                                              template: null, | ||||
|                                              templateUrl: 'tpl/sometplurl.html', | ||||
|                                              styles: [], | ||||
|                                              styleUrls: [] | ||||
|                                            })) | ||||
|                    .normalizeTemplateAsync({ | ||||
|                      componentType: SomeComp, | ||||
|                      moduleUrl: SOME_MODULE_URL, | ||||
|                      encapsulation: null, | ||||
|                      template: null, | ||||
|                      templateUrl: 'tpl/sometplurl.html', | ||||
|                      styles: [], | ||||
|                      styleUrls: [] | ||||
|                    }) | ||||
|                    .then((template: CompileTemplateMetadata) => { | ||||
|                      expect(template.styleUrls).toEqual(['package:some/module/tpl/test.css']); | ||||
|                      async.done(); | ||||
| @ -249,13 +255,15 @@ export function main() { | ||||
|              (async: AsyncTestCompleter, normalizer: DirectiveNormalizer, | ||||
|               resourceLoader: MockResourceLoader) => { | ||||
|                resourceLoader.expect('package:some/module/cmp.html', 'a'); | ||||
|                var templateMeta = new CompileTemplateMetadata({ | ||||
|                var prenormMeta = { | ||||
|                  componentType: SomeComp, | ||||
|                  moduleUrl: SOME_MODULE_URL, | ||||
|                  templateUrl: 'cmp.html', | ||||
|                }); | ||||
|                }; | ||||
|                Promise | ||||
|                    .all([ | ||||
|                      normalizer.normalizeTemplateAsync(dirType, templateMeta), | ||||
|                      normalizer.normalizeTemplateAsync(dirType, templateMeta) | ||||
|                      normalizer.normalizeTemplateAsync(prenormMeta), | ||||
|                      normalizer.normalizeTemplateAsync(prenormMeta) | ||||
|                    ]) | ||||
|                    .then((templates: CompileTemplateMetadata[]) => { | ||||
|                      expect(templates[0].template).toEqual('a'); | ||||
| @ -273,8 +281,13 @@ export function main() { | ||||
| 
 | ||||
|            var viewEncapsulation = ViewEncapsulation.Native; | ||||
|            var template = normalizer.normalizeLoadedTemplate( | ||||
|                dirType, new CompileTemplateMetadata( | ||||
|                             {encapsulation: viewEncapsulation, styles: [], styleUrls: []}), | ||||
|                { | ||||
|                  componentType: SomeComp, | ||||
|                  moduleUrl: SOME_MODULE_URL, | ||||
|                  encapsulation: viewEncapsulation, | ||||
|                  styles: [], | ||||
|                  styleUrls: [] | ||||
|                }, | ||||
|                '', 'package:some/module/'); | ||||
|            expect(template.encapsulation).toBe(viewEncapsulation); | ||||
|          })); | ||||
| @ -282,17 +295,27 @@ export function main() { | ||||
|       it('should keep the template as html', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            var template = normalizer.normalizeLoadedTemplate( | ||||
|                dirType, | ||||
|                new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), 'a', | ||||
|                'package:some/module/'); | ||||
|                { | ||||
|                  componentType: SomeComp, | ||||
|                  moduleUrl: SOME_MODULE_URL, | ||||
|                  encapsulation: null, | ||||
|                  styles: [], | ||||
|                  styleUrls: [] | ||||
|                }, | ||||
|                'a', 'package:some/module/'); | ||||
|            expect(template.template).toEqual('a'); | ||||
|          })); | ||||
| 
 | ||||
|       it('should collect ngContent', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            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/'); | ||||
|            expect(template.ngContentSelectors).toEqual(['a']); | ||||
|          })); | ||||
| @ -300,8 +323,13 @@ export function main() { | ||||
|       it('should normalize ngContent wildcard selector', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            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>', | ||||
|                'package:some/module/'); | ||||
|            expect(template.ngContentSelectors).toEqual(['*', '*', '*']); | ||||
| @ -310,8 +338,13 @@ export function main() { | ||||
|       it('should collect top level styles in the template', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            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/'); | ||||
|            expect(template.styles).toEqual(['a']); | ||||
|          })); | ||||
| @ -319,8 +352,13 @@ export function main() { | ||||
|       it('should collect styles inside in elements', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            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/'); | ||||
|            expect(template.styles).toEqual(['a']); | ||||
|          })); | ||||
| @ -328,8 +366,13 @@ export function main() { | ||||
|       it('should collect styleUrls in the template', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            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/'); | ||||
|            expect(template.styleUrls).toEqual(['package:some/module/aUrl']); | ||||
|          })); | ||||
| @ -337,8 +380,13 @@ export function main() { | ||||
|       it('should collect styleUrls in elements', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            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/'); | ||||
|            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', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            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/'); | ||||
|            expect(template.styleUrls).toEqual([]); | ||||
|          })); | ||||
| @ -355,8 +408,13 @@ export function main() { | ||||
|       it('should ignore link elements with absolute urls but non package: scheme', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            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/'); | ||||
|            expect(template.styleUrls).toEqual([]); | ||||
|          })); | ||||
| @ -364,8 +422,13 @@ export function main() { | ||||
|       it('should extract @import style urls into styleAbsUrl', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            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'); | ||||
|            expect(template.styles).toEqual(['']); | ||||
|            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', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            var template = normalizer.normalizeLoadedTemplate( | ||||
|                dirType, new CompileTemplateMetadata({ | ||||
|                { | ||||
|                  componentType: SomeComp, | ||||
|                  moduleUrl: SOME_MODULE_URL, | ||||
|                  encapsulation: null, | ||||
|                  styles: ['.foo{background-image: url(\'double.jpg\');'], | ||||
|                  styleUrls: [] | ||||
|                }), | ||||
|                }, | ||||
|                '', 'package:some/module/id'); | ||||
|            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', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            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'); | ||||
|            expect(template.styles).toEqual([]); | ||||
|            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', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            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'); | ||||
|            expect(template.styles).toEqual([]); | ||||
|            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', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            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'); | ||||
|            expect(template.encapsulation).toEqual(ViewEncapsulation.None); | ||||
|          })); | ||||
| @ -415,8 +495,13 @@ export function main() { | ||||
|       it('should ignore ng-content in elements with ngNonBindable', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            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>', | ||||
|                'package:some/module/'); | ||||
|            expect(template.ngContentSelectors).toEqual([]); | ||||
| @ -425,8 +510,13 @@ export function main() { | ||||
|       it('should still collect <style> in elements with ngNonBindable', | ||||
|          inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { | ||||
|            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/'); | ||||
|            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 {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 {TestBed, inject} from '@angular/core/testing'; | ||||
| import {TestBed, async, inject} from '@angular/core/testing'; | ||||
| 
 | ||||
| import {stringify} from '../src/facade/lang'; | ||||
| 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'; | ||||
| 
 | ||||
| @ -20,186 +22,305 @@ export function main() { | ||||
|   describe('CompileMetadataResolver', () => { | ||||
|     beforeEach(() => { TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); }); | ||||
| 
 | ||||
|     describe('getDirectiveMetadata', () => { | ||||
|       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', | ||||
|     it('should throw on the get... methods if the module has not been loaded yet', | ||||
|        inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { | ||||
| 
 | ||||
|          @Component({template: ''}) | ||||
|          class MyComp { | ||||
|          @NgModule({}) | ||||
|          class SomeModule { | ||||
|          } | ||||
| 
 | ||||
|          @NgModule({declarations: [MyComp, MyComp]}) | ||||
|          class MyModule { | ||||
|          @Pipe({name: 'pipe'}) | ||||
|          class SomePipe { | ||||
|          } | ||||
| 
 | ||||
|          const modMeta = resolver.getNgModuleMetadata(MyModule); | ||||
|          expect(modMeta.declaredDirectives.length).toBe(1); | ||||
|          expect(modMeta.declaredDirectives[0].type.reference).toBe(MyComp); | ||||
|          expect(() => resolver.getNgModuleMetadata(SomeModule)).toThrowError(/Illegal state/); | ||||
|          expect(() => resolver.getDirectiveMetadata(ComponentWithEverythingInline)) | ||||
|              .toThrowError(/Illegal state/); | ||||
|          expect(() => resolver.getPipeMetadata(SomePipe)).toThrowError(/Illegal state/); | ||||
|        })); | ||||
| 
 | ||||
|     it('should read metadata in sync for components with inline resources', | ||||
|        inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { | ||||
|          @NgModule({declarations: [ComponentWithEverythingInline]}) | ||||
|          class SomeModule { | ||||
|          } | ||||
|          resolver.loadNgModuleMetadata(SomeModule, true); | ||||
| 
 | ||||
|          const meta = resolver.getDirectiveMetadata(ComponentWithEverythingInline); | ||||
|          expect(meta.selector).toEqual('someSelector'); | ||||
|          expect(meta.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: ''}) | ||||
| @ -210,6 +331,14 @@ class ComponentWithoutModuleId { | ||||
| class ComponentWithInvalidModuleId { | ||||
| } | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'someSelector', | ||||
|   templateUrl: 'someTemplateUrl', | ||||
|   styleUrls: ['someStyleUrl'], | ||||
| }) | ||||
| class ComponentWithExternalResources { | ||||
| } | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'someSelector', | ||||
|   inputs: ['someProp'], | ||||
| @ -223,13 +352,11 @@ class ComponentWithInvalidModuleId { | ||||
|   moduleId: 'someModuleId', | ||||
|   changeDetection: ChangeDetectionStrategy.Default, | ||||
|   template: 'someTemplate', | ||||
|   templateUrl: 'someTemplateUrl', | ||||
|   encapsulation: ViewEncapsulation.Emulated, | ||||
|   styles: ['someStyle'], | ||||
|   styleUrls: ['someStyleUrl'], | ||||
|   interpolation: ['{{', '}}'] | ||||
| }) | ||||
| class ComponentWithEverything implements OnChanges, | ||||
| class ComponentWithEverythingInline implements OnChanges, | ||||
|     OnInit, DoCheck, OnDestroy, AfterContentInit, AfterContentChecked, AfterViewInit, | ||||
|     AfterViewChecked { | ||||
|   ngOnChanges(changes: SimpleChanges): void {} | ||||
|  | ||||
| @ -8,8 +8,7 @@ | ||||
| 
 | ||||
| import {DirectiveResolver, ResourceLoader} from '@angular/compiler'; | ||||
| import {Compiler, Component, Injector, NgModule, NgModuleFactory} from '@angular/core'; | ||||
| import {TestBed, async, fakeAsync, tick} from '@angular/core/testing'; | ||||
| import {beforeEach, describe, inject, it} from '@angular/core/testing/testing_internal'; | ||||
| import {TestBed, async, fakeAsync, inject, tick} from '@angular/core/testing'; | ||||
| import {expect} from '@angular/platform-browser/testing/matchers'; | ||||
| 
 | ||||
| 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 {ComponentRef} from '@angular/core/src/linker/component_factory'; | ||||
| 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 {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; | ||||
| import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; | ||||
| @ -149,11 +149,19 @@ export function main() { | ||||
| 
 | ||||
|     afterEach(destroyPlatform); | ||||
| 
 | ||||
|     it('should throw if bootstrapped Directive is not a Component', () => { | ||||
|       expect(() => bootstrap(HelloRootDirectiveIsNotCmp)) | ||||
|           .toThrowError( | ||||
|               `Could not compile '${stringify(HelloRootDirectiveIsNotCmp)}' because it is not a component.`); | ||||
|     }); | ||||
|     it('should throw if bootstrapped Directive is not a Component', | ||||
|        inject([AsyncTestCompleter], (done: AsyncTestCompleter) => { | ||||
|          var logger = new MockConsole(); | ||||
|          var errorHandler = new ErrorHandler(false); | ||||
|          errorHandler._console = logger as any; | ||||
|          bootstrap(HelloRootDirectiveIsNotCmp, [ | ||||
|            {provide: ErrorHandler, useValue: errorHandler} | ||||
|          ]).catch((e) => { | ||||
|            expect(e.message).toBe( | ||||
|                `Could not compile '${stringify(HelloRootDirectiveIsNotCmp)}' because it is not a component.`); | ||||
|            done.done(); | ||||
|          }); | ||||
|        })); | ||||
| 
 | ||||
|     it('should throw if no element is found', | ||||
|        inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user