diff --git a/modules/@angular/compiler/src/aot/compiler.ts b/modules/@angular/compiler/src/aot/compiler.ts index 6e017cce8c..5406ad9296 100644 --- a/modules/@angular/compiler/src/aot/compiler.ts +++ b/modules/@angular/compiler/src/aot/compiler.ts @@ -10,7 +10,7 @@ import {SchemaMetadata} from '@angular/core'; import {AnimationCompiler} from '../animation/animation_compiler'; import {AnimationParser} from '../animation/animation_parser'; -import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTypeSummary, createHostComponentMeta, identifierModuleUrl, identifierName} from '../compile_metadata'; +import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTypeSummary, componentFactoryName, createHostComponentMeta, identifierModuleUrl, identifierName} from '../compile_metadata'; import {DirectiveNormalizer} from '../directive_normalizer'; import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from '../directive_wrapper_compiler'; import {ListWrapper} from '../facade/collection'; @@ -22,13 +22,14 @@ import * as o from '../output/output_ast'; import {CompiledStylesheet, StyleCompiler} from '../style_compiler'; import {SummaryResolver} from '../summary_resolver'; import {TemplateParser} from '../template_parser/template_parser'; -import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler'; +import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler'; import {AotCompilerHost} from './compiler_host'; import {GeneratedFile} from './generated_file'; import {StaticSymbol} from './static_symbol'; import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver'; -import {serializeSummaries, summaryFileName} from './summary_serializer'; +import {serializeSummaries} from './summary_serializer'; +import {ngfactoryFilePath, splitTypescriptSuffix, summaryFileName} from './util'; export class AotCompiler { private _animationCompiler = new AnimationCompiler(); @@ -65,12 +66,13 @@ export class AotCompiler { srcFileUrl: string, ngModuleByPipeOrDirective: Map, directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: StaticSymbol[], injectables: StaticSymbol[]): GeneratedFile[] { - const fileSuffix = _splitTypescriptSuffix(srcFileUrl)[1]; + const fileSuffix = splitTypescriptSuffix(srcFileUrl)[1]; const statements: o.Statement[] = []; const exportedVars: string[] = []; const generatedFiles: GeneratedFile[] = []; - generatedFiles.push(this._createSummary(srcFileUrl, directives, pipes, ngModules, injectables)); + generatedFiles.push(this._createSummary( + srcFileUrl, directives, pipes, ngModules, injectables, statements, exportedVars)); // compile all ng modules exportedVars.push( @@ -109,7 +111,7 @@ export class AotCompiler { }); if (statements.length > 0) { const srcModule = this._codegenSourceModule( - srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars); + srcFileUrl, ngfactoryFilePath(srcFileUrl), statements, exportedVars); generatedFiles.unshift(srcModule); } return generatedFiles; @@ -117,7 +119,8 @@ export class AotCompiler { private _createSummary( srcFileUrl: string, directives: StaticSymbol[], pipes: StaticSymbol[], - ngModules: StaticSymbol[], injectables: StaticSymbol[]): GeneratedFile { + ngModules: StaticSymbol[], injectables: StaticSymbol[], targetStatements: o.Statement[], + targetExportedVars: string[]): GeneratedFile { const symbolSummaries = this._symbolResolver.getSymbolsOf(srcFileUrl) .map(symbol => this._symbolResolver.resolveSymbol(symbol)); const typeSummaries = [ @@ -126,8 +129,13 @@ export class AotCompiler { ...pipes.map(ref => this._metadataResolver.getPipeSummary(ref)), ...injectables.map(ref => this._metadataResolver.getInjectableSummary(ref)) ]; - const json = serializeSummaries( - this._host, this._summaryResolver, this._symbolResolver, symbolSummaries, typeSummaries); + const {json, exportAs} = serializeSummaries( + this._summaryResolver, this._symbolResolver, symbolSummaries, typeSummaries); + exportAs.forEach((entry) => { + targetStatements.push( + o.variable(entry.exportAs).set(o.importExpr({reference: entry.symbol})).toDeclStmt()); + targetExportedVars.push(entry.exportAs); + }); return new GeneratedFile(srcFileUrl, summaryFileName(srcFileUrl), json); } @@ -150,12 +158,6 @@ export class AotCompiler { } const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers); - - appCompileResult.dependencies.forEach((dep) => { - dep.placeholder.reference = this._symbolResolver.getStaticSymbol( - _ngfactoryModuleUrl(identifierModuleUrl(dep.comp)), _componentFactoryName(dep.comp)); - }); - targetStatements.push(...appCompileResult.statements); return appCompileResult.ngModuleFactoryVar; } @@ -172,13 +174,12 @@ export class AotCompiler { private _compileComponentFactory( compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: string, targetStatements: o.Statement[]): string { + const hostType = this._metadataResolver.getHostComponentType(compMeta.type.reference); const hostMeta = createHostComponentMeta( - this._symbolResolver.getStaticSymbol( - identifierModuleUrl(compMeta.type), `${identifierName(compMeta.type)}_Host`), - compMeta); + hostType, compMeta, this._metadataResolver.getHostComponentViewClass(hostType)); const hostViewFactoryVar = this._compileComponent( hostMeta, ngModule, [compMeta.type], null, fileSuffix, targetStatements); - const compFactoryVar = _componentFactoryName(compMeta.type); + const compFactoryVar = componentFactoryName(compMeta.type.reference); targetStatements.push( o.variable(compFactoryVar) .set(o.importExpr( @@ -219,7 +220,7 @@ export class AotCompiler { ..._resolveStyleStatements(this._symbolResolver, componentStyles, fileSuffix)); } compiledAnimations.forEach(entry => targetStatements.push(...entry.statements)); - targetStatements.push(..._resolveViewStatements(this._symbolResolver, viewResult)); + targetStatements.push(...viewResult.statements); return viewResult.viewClassVar; } @@ -241,27 +242,6 @@ export class AotCompiler { } } -function _resolveViewStatements( - reflector: StaticSymbolResolver, compileResult: ViewCompileResult): o.Statement[] { - compileResult.dependencies.forEach((dep) => { - if (dep instanceof ViewClassDependency) { - const vfd = dep; - vfd.placeholder.reference = - reflector.getStaticSymbol(_ngfactoryModuleUrl(identifierModuleUrl(vfd.comp)), dep.name); - } else if (dep instanceof ComponentFactoryDependency) { - const cfd = dep; - cfd.placeholder.reference = reflector.getStaticSymbol( - _ngfactoryModuleUrl(identifierModuleUrl(cfd.comp)), _componentFactoryName(cfd.comp)); - } else if (dep instanceof DirectiveWrapperDependency) { - const dwd = dep; - dwd.placeholder.reference = - reflector.getStaticSymbol(_ngfactoryModuleUrl(identifierModuleUrl(dwd.dir)), dwd.name); - } - }); - return compileResult.statements; -} - - function _resolveStyleStatements( reflector: StaticSymbolResolver, compileResult: CompiledStylesheet, fileSuffix: string): o.Statement[] { @@ -272,15 +252,6 @@ function _resolveStyleStatements( return compileResult.statements; } -function _ngfactoryModuleUrl(dirUrl: string): string { - const urlWithSuffix = _splitTypescriptSuffix(dirUrl); - return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`; -} - -function _componentFactoryName(comp: CompileIdentifierMetadata): string { - return `${identifierName(comp)}NgFactory`; -} - function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string): string { return `${stylesheetUrl}${shim ? '.shim' : ''}.ngstyle${suffix}`; } @@ -292,20 +263,6 @@ function _assertComponent(meta: CompileDirectiveMetadata) { } } -function _splitTypescriptSuffix(path: string): string[] { - if (path.endsWith('.d.ts')) { - return [path.slice(0, -5), '.ts']; - } - - const lastDot = path.lastIndexOf('.'); - - if (lastDot !== -1) { - return [path.substring(0, lastDot), path.substring(lastDot)]; - } - - return [path, '']; -} - export interface NgAnalyzedModules { ngModules: CompileNgModuleMetadata[]; ngModuleByPipeOrDirective: Map; diff --git a/modules/@angular/compiler/src/aot/compiler_factory.ts b/modules/@angular/compiler/src/aot/compiler_factory.ts index 6ce8fadbb4..6bb7f64dd6 100644 --- a/modules/@angular/compiler/src/aot/compiler_factory.ts +++ b/modules/@angular/compiler/src/aot/compiler_factory.ts @@ -34,11 +34,12 @@ import {AotCompilerHost} from './compiler_host'; import {AotCompilerOptions} from './compiler_options'; import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities'; import {StaticReflector} from './static_reflector'; -import {StaticSymbolCache} from './static_symbol'; +import {StaticSymbol, StaticSymbolCache} from './static_symbol'; import {StaticSymbolResolver} from './static_symbol_resolver'; import {AotSummaryResolver} from './summary_resolver'; + /** * Creates a new AotCompiler based on options and a host. */ @@ -69,13 +70,19 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom const resolver = new CompileMetadataResolver( new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector), new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer, - staticReflector); + symbolCache, staticReflector); // TODO(vicb): do not pass options.i18nFormat here + const importResolver = { + getImportAs: (symbol: StaticSymbol) => symbolResolver.getImportAs(symbol), + fileNameToModuleName: (fileName: string, containingFilePath: string) => + compilerHost.fileNameToModuleName(fileName, containingFilePath) + }; const compiler = new AotCompiler( compilerHost, resolver, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config, elementSchemaRegistry), new DirectiveWrapperCompiler(config, expressionParser, elementSchemaRegistry, console), - new NgModuleCompiler(), new TypeScriptEmitter(compilerHost), summaryResolver, options.locale, - options.i18nFormat, new AnimationParser(elementSchemaRegistry), symbolResolver); + new NgModuleCompiler(), new TypeScriptEmitter(importResolver), summaryResolver, + options.locale, options.i18nFormat, new AnimationParser(elementSchemaRegistry), + symbolResolver); return {compiler, reflector: staticReflector}; } diff --git a/modules/@angular/compiler/src/aot/compiler_host.ts b/modules/@angular/compiler/src/aot/compiler_host.ts index 9138ecfebe..dbe52ab719 100644 --- a/modules/@angular/compiler/src/aot/compiler_host.ts +++ b/modules/@angular/compiler/src/aot/compiler_host.ts @@ -6,19 +6,24 @@ * found in the LICENSE file at https://angular.io/license */ -import {ImportResolver} from '../output/path_util'; - import {StaticSymbol} from './static_symbol'; import {StaticSymbolResolverHost} from './static_symbol_resolver'; import {AotSummaryResolverHost} from './summary_resolver'; -import {AotSummarySerializerHost} from './summary_serializer'; /** * The host of the AotCompiler disconnects the implementation from TypeScript / other language * services and from underlying file systems. */ -export interface AotCompilerHost extends StaticSymbolResolverHost, ImportResolver, - AotSummaryResolverHost, AotSummarySerializerHost { +export interface AotCompilerHost extends StaticSymbolResolverHost, AotSummaryResolverHost { + /** + * Converts a file path to a module name that can be used as an `import. + * I.e. `path/to/importedFile.ts` should be imported by `path/to/containingFile.ts`. + * + * See ImportResolver. + */ + fileNameToModuleName(importedFilePath: string, containingFilePath: string): string + /*|null*/; + /** * Loads a resource (e.g. html / css) */ diff --git a/modules/@angular/compiler/src/aot/static_reflector.ts b/modules/@angular/compiler/src/aot/static_reflector.ts index 86bfcff60f..27a3c25c1b 100644 --- a/modules/@angular/compiler/src/aot/static_reflector.ts +++ b/modules/@angular/compiler/src/aot/static_reflector.ts @@ -83,8 +83,11 @@ export class StaticReflector implements ReflectorReader { annotations = []; const classMetadata = this.getTypeMetadata(type); if (classMetadata['extends']) { - const parentAnnotations = this.annotations(this.simplify(type, classMetadata['extends'])); - annotations.push(...parentAnnotations); + const parentType = this.simplify(type, classMetadata['extends']); + if (parentType instanceof StaticSymbol) { + const parentAnnotations = this.annotations(parentType); + annotations.push(...parentAnnotations); + } } if (classMetadata['decorators']) { const ownAnnotations: any[] = this.simplify(type, classMetadata['decorators']); @@ -101,10 +104,13 @@ export class StaticReflector implements ReflectorReader { const classMetadata = this.getTypeMetadata(type); propMetadata = {}; if (classMetadata['extends']) { - const parentPropMetadata = this.propMetadata(this.simplify(type, classMetadata['extends'])); - Object.keys(parentPropMetadata).forEach((parentProp) => { - propMetadata[parentProp] = parentPropMetadata[parentProp]; - }); + const parentType = this.simplify(type, classMetadata['extends']); + if (parentType instanceof StaticSymbol) { + const parentPropMetadata = this.propMetadata(parentType); + Object.keys(parentPropMetadata).forEach((parentProp) => { + propMetadata[parentProp] = parentPropMetadata[parentProp]; + }); + } } const members = classMetadata['members'] || {}; @@ -156,7 +162,10 @@ export class StaticReflector implements ReflectorReader { parameters.push(nestedResult); }); } else if (classMetadata['extends']) { - parameters = this.parameters(this.simplify(type, classMetadata['extends'])); + const parentType = this.simplify(type, classMetadata['extends']); + if (parentType instanceof StaticSymbol) { + parameters = this.parameters(parentType); + } } if (!parameters) { parameters = []; @@ -176,10 +185,13 @@ export class StaticReflector implements ReflectorReader { const classMetadata = this.getTypeMetadata(type); methodNames = {}; if (classMetadata['extends']) { - const parentMethodNames = this._methodNames(this.simplify(type, classMetadata['extends'])); - Object.keys(parentMethodNames).forEach((parentProp) => { - methodNames[parentProp] = parentMethodNames[parentProp]; - }); + const parentType = this.simplify(type, classMetadata['extends']); + if (parentType instanceof StaticSymbol) { + const parentMethodNames = this._methodNames(parentType); + Object.keys(parentMethodNames).forEach((parentProp) => { + methodNames[parentProp] = parentMethodNames[parentProp]; + }); + } } const members = classMetadata['members'] || {}; diff --git a/modules/@angular/compiler/src/aot/static_symbol.ts b/modules/@angular/compiler/src/aot/static_symbol.ts index f90132e490..cb47dd9e67 100644 --- a/modules/@angular/compiler/src/aot/static_symbol.ts +++ b/modules/@angular/compiler/src/aot/static_symbol.ts @@ -12,7 +12,14 @@ * This token is unique for a filePath and name and can be used as a hash table key. */ export class StaticSymbol { - constructor(public filePath: string, public name: string, public members?: string[]) {} + constructor(public filePath: string, public name: string, public members: string[]) {} + + assertNoMembers() { + if (this.members.length) { + throw new Error( + `Illegal state: symbol without members expected, but got ${JSON.stringify(this)}.`); + } + } } /** diff --git a/modules/@angular/compiler/src/aot/static_symbol_resolver.ts b/modules/@angular/compiler/src/aot/static_symbol_resolver.ts index 29867d1c87..9b61292848 100644 --- a/modules/@angular/compiler/src/aot/static_symbol_resolver.ts +++ b/modules/@angular/compiler/src/aot/static_symbol_resolver.ts @@ -45,11 +45,18 @@ const SUPPORTED_SCHEMA_VERSION = 3; /** * This class is responsible for loading metadata per symbol, * and normalizing references between symbols. + * + * Internally, it only uses symbols without members, + * and deduces the values for symbols with members based + * on these symbols. */ export class StaticSymbolResolver { private metadataCache = new Map(); + // Note: this will only contain StaticSymbols without members! private resolvedSymbols = new Map(); private resolvedFilePaths = new Set(); + // Note: this will only contain StaticSymbols without members! + private importAs = new Map(); constructor( private host: StaticSymbolResolverHost, private staticSymbolCache: StaticSymbolCache, @@ -60,13 +67,33 @@ export class StaticSymbolResolver { if (staticSymbol.members.length > 0) { return this._resolveSymbolMembers(staticSymbol); } - let result = this._resolveSymbolFromSummary(staticSymbol); + let result = this.resolvedSymbols.get(staticSymbol); + if (result) { + return result; + } + result = this._resolveSymbolFromSummary(staticSymbol); + if (result) { + return result; + } + // Note: Some users use libraries that were not compiled with ngc, i.e. they don't + // have summaries, only .d.ts files. So we always need to check both, the summary + // and metadata. + this._createSymbolsOf(staticSymbol.filePath); + result = this.resolvedSymbols.get(staticSymbol); + return result; + } + + getImportAs(staticSymbol: StaticSymbol): StaticSymbol { + if (staticSymbol.members.length) { + const baseSymbol = this.getStaticSymbol(staticSymbol.filePath, staticSymbol.name); + const baseImportAs = this.getImportAs(baseSymbol); + return baseImportAs ? + this.getStaticSymbol(baseImportAs.filePath, baseImportAs.name, staticSymbol.members) : + null; + } + let result = this.summaryResolver.getImportAs(staticSymbol); if (!result) { - // Note: Some users use libraries that were not compiled with ngc, i.e. they don't - // have summaries, only .d.ts files. So we always need to check both, the summary - // and metadata. - this._createSymbolsOf(staticSymbol.filePath); - result = this.resolvedSymbols.get(staticSymbol); + result = this.importAs.get(staticSymbol); } return result; } @@ -135,10 +162,13 @@ export class StaticSymbolResolver { const metadata = this.getModuleMetadata(filePath); if (metadata['metadata']) { // handle direct declarations of the symbol - Object.keys(metadata['metadata']).forEach((symbolName) => { - const symbolMeta = metadata['metadata'][symbolName]; - resolvedSymbols.push( - this.createResolvedSymbol(this.getStaticSymbol(filePath, symbolName), symbolMeta)); + const topLevelSymbolNames = + new Set(Object.keys(metadata['metadata']).map(unescapeIdentifier)); + Object.keys(metadata['metadata']).forEach((metadataKey) => { + const symbolMeta = metadata['metadata'][metadataKey]; + resolvedSymbols.push(this.createResolvedSymbol( + this.getStaticSymbol(filePath, unescapeIdentifier(metadataKey)), topLevelSymbolNames, + symbolMeta)); }); } @@ -154,15 +184,16 @@ export class StaticSymbolResolver { } else { symbolName = exportSymbol.as; } + symbolName = unescapeIdentifier(symbolName); let symName = symbolName; if (typeof exportSymbol !== 'string') { - symName = exportSymbol.name; + symName = unescapeIdentifier(exportSymbol.name); } const resolvedModule = this.resolveModule(moduleExport.from, filePath); if (resolvedModule) { const targetSymbol = this.getStaticSymbol(resolvedModule, symName); const sourceSymbol = this.getStaticSymbol(filePath, symbolName); - resolvedSymbols.push(new ResolvedStaticSymbol(sourceSymbol, targetSymbol)); + resolvedSymbols.push(this.createExport(sourceSymbol, targetSymbol)); } }); } else { @@ -172,7 +203,7 @@ export class StaticSymbolResolver { const nestedExports = this.getSymbolsOf(resolvedModule); nestedExports.forEach((targetSymbol) => { const sourceSymbol = this.getStaticSymbol(filePath, targetSymbol.name); - resolvedSymbols.push(new ResolvedStaticSymbol(sourceSymbol, targetSymbol)); + resolvedSymbols.push(this.createExport(sourceSymbol, targetSymbol)); }); } } @@ -182,7 +213,9 @@ export class StaticSymbolResolver { (resolvedSymbol) => this.resolvedSymbols.set(resolvedSymbol.symbol, resolvedSymbol)); } - private createResolvedSymbol(sourceSymbol: StaticSymbol, metadata: any): ResolvedStaticSymbol { + private createResolvedSymbol( + sourceSymbol: StaticSymbol, topLevelSymbolNames: Set, + metadata: any): ResolvedStaticSymbol { const self = this; class ReferenceTransformer extends ValueTransformer { @@ -196,7 +229,7 @@ export class StaticSymbolResolver { return result; } else if (symbolic === 'reference') { const module = map['module']; - const name = map['name']; + const name = map['name'] ? unescapeIdentifier(map['name']) : map['name']; if (!name) { return null; } @@ -209,28 +242,43 @@ export class StaticSymbolResolver { message: `Could not resolve ${module} relative to ${sourceSymbol.filePath}.` }; } - } else { - const isFunctionParam = functionParams.indexOf(name) >= 0; - if (!isFunctionParam) { - filePath = sourceSymbol.filePath; - } - } - if (filePath) { return self.getStaticSymbol(filePath, name); - } else { + } else if (functionParams.indexOf(name) >= 0) { // reference to a function parameter return {__symbolic: 'reference', name: name}; + } else { + if (topLevelSymbolNames.has(name)) { + return self.getStaticSymbol(sourceSymbol.filePath, name); + } + // ambient value + null; } } else { return super.visitStringMap(map, functionParams); } } } - const transformedMeta = visitValue(metadata, new ReferenceTransformer(), []); + if (transformedMeta instanceof StaticSymbol) { + return this.createExport(sourceSymbol, transformedMeta); + } return new ResolvedStaticSymbol(sourceSymbol, transformedMeta); } + private createExport(sourceSymbol: StaticSymbol, targetSymbol: StaticSymbol): + ResolvedStaticSymbol { + sourceSymbol.assertNoMembers(); + targetSymbol.assertNoMembers(); + if (this.summaryResolver.isLibraryFile(sourceSymbol.filePath)) { + // This case is for an ng library importing symbols from a plain ts library + // transitively. + // Note: We rely on the fact that we discover symbols in the direction + // from source files to library files + this.importAs.set(targetSymbol, this.getImportAs(sourceSymbol) || sourceSymbol); + } + return new ResolvedStaticSymbol(sourceSymbol, targetSymbol); + } + private reportError(error: Error, context: StaticSymbol, path?: string) { if (this.errorRecorder) { this.errorRecorder(error, (context && context.filePath) || path); @@ -287,3 +335,11 @@ export class StaticSymbolResolver { } } } + +const UNDERSCORE_CHAR_CODE = 95; + +// Remove extra underscore from escaped identifier. +// See https://github.com/Microsoft/TypeScript/blob/master/src/compiler/utilities.ts +export function unescapeIdentifier(identifier: string): string { + return identifier.startsWith('___') ? identifier.substr(1) : identifier; +} \ No newline at end of file diff --git a/modules/@angular/compiler/src/aot/summary_resolver.ts b/modules/@angular/compiler/src/aot/summary_resolver.ts index 91731e7f3c..98a9168a62 100644 --- a/modules/@angular/compiler/src/aot/summary_resolver.ts +++ b/modules/@angular/compiler/src/aot/summary_resolver.ts @@ -10,9 +10,8 @@ import {Summary, SummaryResolver} from '../summary_resolver'; import {StaticSymbol, StaticSymbolCache} from './static_symbol'; import {ResolvedStaticSymbol} from './static_symbol_resolver'; -import {deserializeSummaries, summaryFileName} from './summary_serializer'; - -const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/; +import {deserializeSummaries} from './summary_serializer'; +import {ngfactoryFilePath, stripNgFactory, summaryFileName} from './util'; export interface AotSummaryResolverHost { /** @@ -24,23 +23,34 @@ export interface AotSummaryResolverHost { * Returns whether a file is a source file or not. */ isSourceFile(sourceFilePath: string): boolean; + /** + * Returns the output file path of a source file. + * E.g. + * `some_file.ts` -> `some_file.d.ts` + */ + getOutputFileName(sourceFilePath: string): string; } export class AotSummaryResolver implements SummaryResolver { + // Note: this will only contain StaticSymbols without members! private summaryCache = new Map>(); private loadedFilePaths = new Set(); + // Note: this will only contain StaticSymbols without members! + private importAs = new Map(); constructor(private host: AotSummaryResolverHost, private staticSymbolCache: StaticSymbolCache) {} - private _assertNoMembers(symbol: StaticSymbol) { - if (symbol.members.length) { - throw new Error( - `Internal state: StaticSymbols in summaries can't have members! ${JSON.stringify(symbol)}`); - } + isLibraryFile(filePath: string): boolean { + // Note: We need to strip the .ngfactory. file path, + // so this method also works for generated files + // (for which host.isSourceFile will always return false). + return !this.host.isSourceFile(stripNgFactory(filePath)); } + getLibraryFileName(filePath: string) { return this.host.getOutputFileName(filePath); } + resolveSummary(staticSymbol: StaticSymbol): Summary { - this._assertNoMembers(staticSymbol); + staticSymbol.assertNoMembers(); let summary = this.summaryCache.get(staticSymbol); if (!summary) { this._loadSummaryFile(staticSymbol.filePath); @@ -54,12 +64,17 @@ export class AotSummaryResolver implements SummaryResolver { return Array.from(this.summaryCache.keys()).filter((symbol) => symbol.filePath === filePath); } + getImportAs(staticSymbol: StaticSymbol): StaticSymbol { + staticSymbol.assertNoMembers(); + return this.importAs.get(staticSymbol); + } + private _loadSummaryFile(filePath: string) { if (this.loadedFilePaths.has(filePath)) { return; } this.loadedFilePaths.add(filePath); - if (!this.host.isSourceFile(filePath)) { + if (this.isLibraryFile(filePath)) { const summaryFilePath = summaryFileName(filePath); let json: string; try { @@ -69,8 +84,13 @@ export class AotSummaryResolver implements SummaryResolver { throw e; } if (json) { - const readSummaries = deserializeSummaries(this.staticSymbolCache, json); - readSummaries.forEach((summary) => { this.summaryCache.set(summary.symbol, summary); }); + const {summaries, importAs} = deserializeSummaries(this.staticSymbolCache, json); + summaries.forEach((summary) => this.summaryCache.set(summary.symbol, summary)); + importAs.forEach((importAs) => { + this.importAs.set( + importAs.symbol, + this.staticSymbolCache.get(ngfactoryFilePath(filePath), importAs.importAs)); + }); } } } diff --git a/modules/@angular/compiler/src/aot/summary_serializer.ts b/modules/@angular/compiler/src/aot/summary_serializer.ts index b8d38db56d..09495fd786 100644 --- a/modules/@angular/compiler/src/aot/summary_serializer.ts +++ b/modules/@angular/compiler/src/aot/summary_serializer.ts @@ -15,25 +15,11 @@ import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolv const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/; -export interface AotSummarySerializerHost { - /** - * Returns the output file path of a source file. - * E.g. - * `some_file.ts` -> `some_file.d.ts` - */ - getOutputFileName(sourceFilePath: string): string; - /** - * Returns whether a file is a source file or not. - */ - isSourceFile(sourceFilePath: string): boolean; -} - export function serializeSummaries( - host: AotSummarySerializerHost, summaryResolver: SummaryResolver, - symbolResolver: StaticSymbolResolver, - - symbols: ResolvedStaticSymbol[], types: CompileTypeSummary[]): string { - const serializer = new Serializer(host); + summaryResolver: SummaryResolver, symbolResolver: StaticSymbolResolver, + symbols: ResolvedStaticSymbol[], types: CompileTypeSummary[]): + {json: string, exportAs: {symbol: StaticSymbol, exportAs: string}[]} { + const serializer = new Serializer(symbolResolver, summaryResolver); // for symbols, we use everything except for the class metadata itself // (we keep the statics though), as the class metadata is contained in the @@ -46,7 +32,7 @@ export function serializeSummaries( // we execute the loop! for (let processedIndex = 0; processedIndex < serializer.symbols.length; processedIndex++) { const symbol = serializer.symbols[processedIndex]; - if (!host.isSourceFile(symbol.filePath)) { + if (summaryResolver.isLibraryFile(symbol.filePath)) { let summary = summaryResolver.resolveSummary(symbol); if (!summary) { // some symbols might originate from a plain typescript library @@ -74,8 +60,11 @@ export function serializeSummaries( const ngModuleSummary = typeSummary; ngModuleSummary.exportedDirectives.concat(ngModuleSummary.exportedPipes).forEach((id) => { const symbol: StaticSymbol = id.reference; - if (!host.isSourceFile(symbol.filePath)) { - serializer.addOrMergeSummary(summaryResolver.resolveSummary(symbol)); + if (summaryResolver.isLibraryFile(symbol.filePath)) { + const summary = summaryResolver.resolveSummary(symbol); + if (summary) { + serializer.addOrMergeSummary(summary); + } } }); } @@ -83,18 +72,14 @@ export function serializeSummaries( return serializer.serialize(); } -export function deserializeSummaries( - symbolCache: StaticSymbolCache, json: string): Summary[] { +export function deserializeSummaries(symbolCache: StaticSymbolCache, json: string): + {summaries: Summary[], importAs: {symbol: StaticSymbol, importAs: string}[]} { const deserializer = new Deserializer(symbolCache); return deserializer.deserialize(json); } -export function summaryFileName(fileName: string): string { - const fileNameWithoutSuffix = fileName.replace(STRIP_SRC_FILE_SUFFIXES, ''); - return `${fileNameWithoutSuffix}.ngsummary.json`; -} - class Serializer extends ValueTransformer { + // Note: This only contains symbols without members. symbols: StaticSymbol[] = []; private indexBySymbol = new Map(); // This now contains a `__symbol: number` in the place of @@ -102,7 +87,11 @@ class Serializer extends ValueTransformer { private processedSummaryBySymbol = new Map(); private processedSummaries: any[] = []; - constructor(private host: AotSummarySerializerHost) { super(); } + constructor( + private symbolResolver: StaticSymbolResolver, + private summaryResolver: SummaryResolver) { + super(); + } addOrMergeSummary(summary: Summary) { let symbolMeta = summary.metadata; @@ -129,34 +118,44 @@ class Serializer extends ValueTransformer { } } - serialize(): string { - return JSON.stringify({ + serialize(): {json: string, exportAs: {symbol: StaticSymbol, exportAs: string}[]} { + const exportAs: {symbol: StaticSymbol, exportAs: string}[] = []; + const json = JSON.stringify({ summaries: this.processedSummaries, symbols: this.symbols.map((symbol, index) => { + symbol.assertNoMembers(); + let importAs: string; + if (this.summaryResolver.isLibraryFile(symbol.filePath)) { + importAs = `${symbol.name}_${index}`; + exportAs.push({symbol, exportAs: importAs}); + } return { __symbol: index, name: symbol.name, // We convert the source filenames tinto output filenames, // as the generated summary file will be used when teh current // compilation unit is used as a library - filePath: this.host.getOutputFileName(symbol.filePath) + filePath: this.summaryResolver.getLibraryFileName(symbol.filePath), + importAs: importAs }; }) }); + return {json, exportAs}; } private processValue(value: any): any { return visitValue(value, this, null); } visitOther(value: any, context: any): any { if (value instanceof StaticSymbol) { - let index = this.indexBySymbol.get(value); + const baseSymbol = this.symbolResolver.getStaticSymbol(value.filePath, value.name); + let index = this.indexBySymbol.get(baseSymbol); // Note: == by purpose to compare with undefined! if (index == null) { index = this.indexBySymbol.size; - this.indexBySymbol.set(value, index); - this.symbols.push(value); + this.indexBySymbol.set(baseSymbol, index); + this.symbols.push(baseSymbol); } - return {__symbol: index}; + return {__symbol: index, members: value.members}; } } } @@ -166,16 +165,28 @@ class Deserializer extends ValueTransformer { constructor(private symbolCache: StaticSymbolCache) { super(); } - deserialize(json: string): Summary[] { + deserialize(json: string): + {summaries: Summary[], importAs: {symbol: StaticSymbol, importAs: string}[]} { const data: {summaries: any[], symbols: any[]} = JSON.parse(json); - this.symbols = data.symbols.map( - serializedSymbol => this.symbolCache.get(serializedSymbol.filePath, serializedSymbol.name)); - return visitValue(data.summaries, this, null); + const importAs: {symbol: StaticSymbol, importAs: string}[] = []; + this.symbols = []; + data.symbols.forEach((serializedSymbol) => { + const symbol = this.symbolCache.get(serializedSymbol.filePath, serializedSymbol.name); + this.symbols.push(symbol); + if (serializedSymbol.importAs) { + importAs.push({symbol: symbol, importAs: serializedSymbol.importAs}); + } + }); + const summaries = visitValue(data.summaries, this, null); + return {summaries, importAs}; } visitStringMap(map: {[key: string]: any}, context: any): any { if ('__symbol' in map) { - return this.symbols[map['__symbol']]; + const baseSymbol = this.symbols[map['__symbol']]; + const members = map['members']; + return members.length ? this.symbolCache.get(baseSymbol.filePath, baseSymbol.name, members) : + baseSymbol; } else { return super.visitStringMap(map, context); } diff --git a/modules/@angular/compiler/src/aot/util.ts b/modules/@angular/compiler/src/aot/util.ts new file mode 100644 index 0000000000..60e3347f10 --- /dev/null +++ b/modules/@angular/compiler/src/aot/util.ts @@ -0,0 +1,37 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/; + +export function ngfactoryFilePath(filePath: string): string { + const urlWithSuffix = splitTypescriptSuffix(filePath); + return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`; +} + +export function stripNgFactory(filePath: string): string { + return filePath.replace(/\.ngfactory\./, '.'); +} + +export function splitTypescriptSuffix(path: string): string[] { + if (path.endsWith('.d.ts')) { + return [path.slice(0, -5), '.ts']; + } + + const lastDot = path.lastIndexOf('.'); + + if (lastDot !== -1) { + return [path.substring(0, lastDot), path.substring(lastDot)]; + } + + return [path, '']; +} + +export function summaryFileName(fileName: string): string { + const fileNameWithoutSuffix = fileName.replace(STRIP_SRC_FILE_SUFFIXES, ''); + return `${fileNameWithoutSuffix}.ngsummary.json`; +} diff --git a/modules/@angular/compiler/src/compile_metadata.ts b/modules/@angular/compiler/src/compile_metadata.ts index 02e16f5dad..87b1ff572b 100644 --- a/modules/@angular/compiler/src/compile_metadata.ts +++ b/modules/@angular/compiler/src/compile_metadata.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core'; +import {ChangeDetectionStrategy, ComponentFactory, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core'; import {StaticSymbol} from './aot/static_symbol'; import {ListWrapper} from './facade/collection'; @@ -112,6 +112,24 @@ export function identifierModuleUrl(compileIdentifier: CompileIdentifierMetadata return reflector.importUri(ref); } +export function viewClassName(compType: any, embeddedTemplateIndex: number): string { + return `View_${identifierName({reference: compType})}_${embeddedTemplateIndex}`; +} + +export function hostViewClassName(compType: any): string { + return `HostView_${identifierName({reference: compType})}`; +} + +export function dirWrapperClassName(dirType: any) { + return `Wrapper_${identifierName({reference: dirType})}`; +} + +export function componentFactoryName(compType: any): string { + return `${identifierName({reference: compType})}NgFactory`; +} + +export interface ProxyClass { setDelegate(delegate: any): void; } + export interface CompileIdentifierMetadata { reference: any; } export enum CompileSummaryKind { @@ -241,7 +259,7 @@ export class CompileTemplateMetadata { externalStylesheets?: CompileStylesheetMetadata[], ngContentSelectors?: string[], animations?: CompileAnimationEntryMetadata[], - interpolation?: [string, string] + interpolation?: [string, string], } = {}) { this.encapsulation = encapsulation; this.template = template; @@ -261,11 +279,16 @@ export class CompileTemplateMetadata { return { animations: this.animations.map(anim => anim.name), ngContentSelectors: this.ngContentSelectors, - encapsulation: this.encapsulation + encapsulation: this.encapsulation, }; } } +export interface CompileEntryComponentMetadata { + componentType: any; + componentFactory: StaticSymbol|ComponentFactory; +} + // Note: This should only use interfaces as nested data types // as we need to be able to serialize this from/to JSON! export interface CompileDirectiveSummary extends CompileTypeSummary { @@ -281,9 +304,12 @@ export interface CompileDirectiveSummary extends CompileTypeSummary { providers: CompileProviderMetadata[]; viewProviders: CompileProviderMetadata[]; queries: CompileQueryMetadata[]; - entryComponents: CompileIdentifierMetadata[]; + entryComponents: CompileEntryComponentMetadata[]; changeDetection: ChangeDetectionStrategy; template: CompileTemplateSummary; + wrapperType: StaticSymbol|ProxyClass; + componentViewType: StaticSymbol|ProxyClass; + componentFactory: StaticSymbol|ComponentFactory; } /** @@ -292,7 +318,8 @@ export interface CompileDirectiveSummary extends CompileTypeSummary { export class CompileDirectiveMetadata { static create( {isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host, - providers, viewProviders, queries, viewQueries, entryComponents, template}: { + providers, viewProviders, queries, viewQueries, entryComponents, template, wrapperType, + componentViewType, componentFactory}: { isHost?: boolean, type?: CompileTypeMetadata, isComponent?: boolean, @@ -306,8 +333,11 @@ export class CompileDirectiveMetadata { viewProviders?: CompileProviderMetadata[], queries?: CompileQueryMetadata[], viewQueries?: CompileQueryMetadata[], - entryComponents?: CompileIdentifierMetadata[], - template?: CompileTemplateMetadata + entryComponents?: CompileEntryComponentMetadata[], + template?: CompileTemplateMetadata, + wrapperType?: StaticSymbol|ProxyClass, + componentViewType?: StaticSymbol|ProxyClass, + componentFactory?: StaticSymbol|ComponentFactory, } = {}): CompileDirectiveMetadata { const hostListeners: {[key: string]: string} = {}; const hostProperties: {[key: string]: string} = {}; @@ -359,6 +389,9 @@ export class CompileDirectiveMetadata { viewQueries, entryComponents, template, + wrapperType, + componentViewType, + componentFactory, }); } isHost: boolean; @@ -376,32 +409,39 @@ export class CompileDirectiveMetadata { viewProviders: CompileProviderMetadata[]; queries: CompileQueryMetadata[]; viewQueries: CompileQueryMetadata[]; - entryComponents: CompileIdentifierMetadata[]; + entryComponents: CompileEntryComponentMetadata[]; template: CompileTemplateMetadata; - constructor( - {isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs, - hostListeners, hostProperties, hostAttributes, providers, viewProviders, queries, - viewQueries, entryComponents, template}: { - isHost?: boolean, - type?: CompileTypeMetadata, - isComponent?: boolean, - selector?: string, - exportAs?: string, - changeDetection?: ChangeDetectionStrategy, - inputs?: {[key: string]: string}, - outputs?: {[key: string]: string}, - hostListeners?: {[key: string]: string}, - hostProperties?: {[key: string]: string}, - hostAttributes?: {[key: string]: string}, - providers?: CompileProviderMetadata[], - viewProviders?: CompileProviderMetadata[], - queries?: CompileQueryMetadata[], - viewQueries?: CompileQueryMetadata[], - entryComponents?: CompileIdentifierMetadata[], - template?: CompileTemplateMetadata, - } = {}) { + wrapperType: StaticSymbol|ProxyClass; + componentViewType: StaticSymbol|ProxyClass; + componentFactory: StaticSymbol|ComponentFactory; + + constructor({isHost, type, isComponent, selector, exportAs, + changeDetection, inputs, outputs, hostListeners, hostProperties, + hostAttributes, providers, viewProviders, queries, viewQueries, + entryComponents, template, wrapperType, componentViewType, componentFactory}: { + isHost?: boolean, + type?: CompileTypeMetadata, + isComponent?: boolean, + selector?: string, + exportAs?: string, + changeDetection?: ChangeDetectionStrategy, + inputs?: {[key: string]: string}, + outputs?: {[key: string]: string}, + hostListeners?: {[key: string]: string}, + hostProperties?: {[key: string]: string}, + hostAttributes?: {[key: string]: string}, + providers?: CompileProviderMetadata[], + viewProviders?: CompileProviderMetadata[], + queries?: CompileQueryMetadata[], + viewQueries?: CompileQueryMetadata[], + entryComponents?: CompileEntryComponentMetadata[], + template?: CompileTemplateMetadata, + wrapperType?: StaticSymbol|ProxyClass, + componentViewType?: StaticSymbol|ProxyClass, + componentFactory?: StaticSymbol|ComponentFactory, + } = {}) { this.isHost = !!isHost; this.type = type; this.isComponent = isComponent; @@ -418,8 +458,11 @@ export class CompileDirectiveMetadata { this.queries = _normalizeArray(queries); this.viewQueries = _normalizeArray(viewQueries); this.entryComponents = _normalizeArray(entryComponents); - this.template = template; + + this.wrapperType = wrapperType; + this.componentViewType = componentViewType; + this.componentFactory = componentFactory; } toSummary(): CompileDirectiveSummary { @@ -439,7 +482,10 @@ export class CompileDirectiveMetadata { queries: this.queries, entryComponents: this.entryComponents, changeDetection: this.changeDetection, - template: this.template && this.template.toSummary() + template: this.template && this.template.toSummary(), + wrapperType: this.wrapperType, + componentViewType: this.componentViewType, + componentFactory: this.componentFactory }; } } @@ -448,11 +494,12 @@ export class CompileDirectiveMetadata { * Construct {@link CompileDirectiveMetadata} from {@link ComponentTypeMetadata} and a selector. */ export function createHostComponentMeta( - typeReference: any, compMeta: CompileDirectiveMetadata): CompileDirectiveMetadata { + hostTypeReference: any, compMeta: CompileDirectiveMetadata, + hostViewType: StaticSymbol | ProxyClass): CompileDirectiveMetadata { const template = CssSelector.parse(compMeta.selector)[0].getMatchingElementTemplate(); return CompileDirectiveMetadata.create({ isHost: true, - type: {reference: typeReference, diDeps: [], lifecycleHooks: []}, + type: {reference: hostTypeReference, diDeps: [], lifecycleHooks: []}, template: new CompileTemplateMetadata({ encapsulation: ViewEncapsulation.None, template: template, @@ -471,7 +518,8 @@ export function createHostComponentMeta( providers: [], viewProviders: [], queries: [], - viewQueries: [] + viewQueries: [], + componentViewType: hostViewType }); } @@ -517,7 +565,7 @@ export interface CompileNgModuleSummary extends CompileTypeSummary { exportedPipes: CompileIdentifierMetadata[]; // Note: This is transitive. - entryComponents: CompileIdentifierMetadata[]; + entryComponents: CompileEntryComponentMetadata[]; // Note: This is transitive. providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[]; // Note: This is transitive. @@ -534,7 +582,7 @@ export class CompileNgModuleMetadata { declaredPipes: CompileIdentifierMetadata[]; exportedPipes: CompileIdentifierMetadata[]; - entryComponents: CompileIdentifierMetadata[]; + entryComponents: CompileEntryComponentMetadata[]; bootstrapComponents: CompileIdentifierMetadata[]; providers: CompileProviderMetadata[]; @@ -555,7 +603,7 @@ export class CompileNgModuleMetadata { exportedDirectives?: CompileIdentifierMetadata[], declaredPipes?: CompileIdentifierMetadata[], exportedPipes?: CompileIdentifierMetadata[], - entryComponents?: CompileIdentifierMetadata[], + entryComponents?: CompileEntryComponentMetadata[], bootstrapComponents?: CompileIdentifierMetadata[], importedModules?: CompileNgModuleSummary[], exportedModules?: CompileNgModuleSummary[], @@ -603,7 +651,7 @@ export class TransitiveCompileNgModuleMetadata { modulesSet = new Set(); modules: CompileTypeMetadata[] = []; entryComponentsSet = new Set(); - entryComponents: CompileIdentifierMetadata[] = []; + entryComponents: CompileEntryComponentMetadata[] = []; providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[] = []; @@ -641,10 +689,10 @@ export class TransitiveCompileNgModuleMetadata { this.modules.push(id); } } - addEntryComponent(id: CompileIdentifierMetadata) { - if (!this.entryComponentsSet.has(id.reference)) { - this.entryComponentsSet.add(id.reference); - this.entryComponents.push(id); + addEntryComponent(ec: CompileEntryComponentMetadata) { + if (!this.entryComponentsSet.has(ec.componentType)) { + this.entryComponentsSet.add(ec.componentType); + this.entryComponents.push(ec); } } } diff --git a/modules/@angular/compiler/src/directive_wrapper_compiler.ts b/modules/@angular/compiler/src/directive_wrapper_compiler.ts index d6405366c2..c8bca611e7 100644 --- a/modules/@angular/compiler/src/directive_wrapper_compiler.ts +++ b/modules/@angular/compiler/src/directive_wrapper_compiler.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, identifierModuleUrl, identifierName} from './compile_metadata'; +import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, dirWrapperClassName, identifierModuleUrl, identifierName} from './compile_metadata'; import {createCheckBindingField, createCheckBindingStmt} from './compiler_util/binding_util'; import {EventHandlerVars, convertActionBinding, convertPropertyBinding} from './compiler_util/expression_converter'; import {triggerAnimation, writeToRenderer} from './compiler_util/render_util'; @@ -52,10 +52,6 @@ const RESET_CHANGES_STMT = o.THIS_EXPR.prop(CHANGES_FIELD_NAME).set(o.literalMap */ @CompilerInjectable() export class DirectiveWrapperCompiler { - static dirWrapperClassName(id: CompileIdentifierMetadata) { - return `Wrapper_${identifierName(id)}`; - } - constructor( private compilerConfig: CompilerConfig, private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry, private _console: Console) {} @@ -149,7 +145,7 @@ class DirectiveWrapperBuilder implements ClassBuilder { .toStmt()); return createClassStmt({ - name: DirectiveWrapperCompiler.dirWrapperClassName(this.dirMeta.type), + name: dirWrapperClassName(this.dirMeta.type.reference), ctorParams: dirDepParamNames.map((paramName) => new o.FnParam(paramName, o.DYNAMIC_TYPE)), builders: [{fields, ctorStmts, methods}, this] }); diff --git a/modules/@angular/compiler/src/i18n/extractor.ts b/modules/@angular/compiler/src/i18n/extractor.ts index a46e641b59..deeddf67fa 100644 --- a/modules/@angular/compiler/src/i18n/extractor.ts +++ b/modules/@angular/compiler/src/i18n/extractor.ts @@ -109,7 +109,7 @@ export class Extractor { const resolver = new CompileMetadataResolver( new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector), new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer, - staticReflector); + symbolCache, staticReflector); // TODO(vicb): implicit tags & attributes const messageBundle = new MessageBundle(htmlParser, [], {}); diff --git a/modules/@angular/compiler/src/jit/compiler.ts b/modules/@angular/compiler/src/jit/compiler.ts index 84ad9a4e6c..4a7b54f5ad 100644 --- a/modules/@angular/compiler/src/jit/compiler.ts +++ b/modules/@angular/compiler/src/jit/compiler.ts @@ -10,7 +10,7 @@ import {Compiler, ComponentFactory, Injector, ModuleWithComponentFactories, NgMo import {AnimationCompiler} from '../animation/animation_compiler'; import {AnimationParser} from '../animation/animation_parser'; -import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, ProviderMeta, createHostComponentMeta, identifierName} from '../compile_metadata'; +import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, ProviderMeta, ProxyClass, createHostComponentMeta, identifierName} from '../compile_metadata'; import {CompilerConfig} from '../config'; import {DirectiveNormalizer} from '../directive_normalizer'; import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler'; @@ -21,10 +21,11 @@ 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 {view_utils} from '../private_import_core'; import {CompiledStylesheet, StyleCompiler} from '../style_compiler'; import {TemplateParser} from '../template_parser/template_parser'; import {SyncAsyncResult} from '../util'; -import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompiler} from '../view_compiler/view_compiler'; +import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency, ViewCompiler} from '../view_compiler/view_compiler'; @@ -130,10 +131,6 @@ export class JitCompiler implements Compiler { const extraProviders = [this._metadataResolver.getProviderMetadata(new ProviderMeta( Compiler, {useFactory: () => new ModuleBoundCompiler(this, moduleMeta.type.reference)}))]; const compileResult = this._ngModuleCompiler.compile(moduleMeta, extraProviders); - compileResult.dependencies.forEach((dep) => { - dep.placeholder.reference = - this._assertComponentKnown(dep.comp.reference, true).proxyComponentFactory; - }); if (!this._compilerConfig.useJit) { ngModuleFactory = interpretStatements(compileResult.statements, compileResult.ngModuleFactoryVar); @@ -168,7 +165,7 @@ export class JitCompiler implements Compiler { const template = this._createCompiledHostTemplate(dirMeta.type.reference, localModuleMeta); templates.add(template); - allComponentFactories.push(template.proxyComponentFactory); + allComponentFactories.push(>dirMeta.componentFactory); } } }); @@ -180,15 +177,16 @@ export class JitCompiler implements Compiler { const dirMeta = this._metadataResolver.getDirectiveMetadata(dirIdentifier.reference); if (dirMeta.isComponent) { dirMeta.entryComponents.forEach((entryComponentType) => { - const moduleMeta = moduleByDirective.get(entryComponentType.reference); + const moduleMeta = moduleByDirective.get(entryComponentType.componentType); templates.add( - this._createCompiledHostTemplate(entryComponentType.reference, moduleMeta)); + this._createCompiledHostTemplate(entryComponentType.componentType, moduleMeta)); }); } }); localModuleMeta.entryComponents.forEach((entryComponentType) => { - const moduleMeta = moduleByDirective.get(entryComponentType.reference); - templates.add(this._createCompiledHostTemplate(entryComponentType.reference, moduleMeta)); + const moduleMeta = moduleByDirective.get(entryComponentType.componentType); + templates.add( + this._createCompiledHostTemplate(entryComponentType.componentType, moduleMeta)); }); }); templates.forEach((template) => this._compileTemplate(template)); @@ -222,12 +220,12 @@ export class JitCompiler implements Compiler { const compMeta = this._metadataResolver.getDirectiveMetadata(compType); assertComponent(compMeta); - const HostClass = function HostClass() {}; - (HostClass).overriddenName = `${identifierName(compMeta.type)}_Host`; - - const hostMeta = createHostComponentMeta(HostClass, compMeta); - compiledTemplate = new CompiledTemplate( - true, compMeta.selector, compMeta.type, hostMeta, ngModule, [compMeta.type]); + const componentFactory = >compMeta.componentFactory; + const hostClass = this._metadataResolver.getHostComponentType(compType); + const hostMeta = createHostComponentMeta( + hostClass, compMeta, view_utils.getComponentFactoryViewClass(componentFactory)); + compiledTemplate = + new CompiledTemplate(true, compMeta.type, hostMeta, ngModule, [compMeta.type]); this._compiledHostTemplateCache.set(compType, compiledTemplate); } return compiledTemplate; @@ -239,8 +237,7 @@ export class JitCompiler implements Compiler { if (!compiledTemplate) { assertComponent(compMeta); compiledTemplate = new CompiledTemplate( - false, compMeta.selector, compMeta.type, compMeta, ngModule, - ngModule.transitiveModule.directives); + false, compMeta.type, compMeta, ngModule, ngModule.transitiveModule.directives); this._compiledTemplateCache.set(compMeta.type.reference, compiledTemplate); } return compiledTemplate; @@ -277,6 +274,7 @@ export class JitCompiler implements Compiler { `/${identifierName(moduleMeta.type)}/${identifierName(dirMeta.type)}/wrapper.ngfactory.js`, statements, compileResult.dirWrapperClassVar); } + (dirMeta.wrapperType).setDelegate(directiveWrapperClass); this._compiledDirectiveWrapperCache.set(dirMeta.type.reference, directiveWrapperClass); } @@ -304,21 +302,6 @@ export class JitCompiler implements Compiler { const compileResult = this._viewCompiler.compileComponent( compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar), pipes, compiledAnimations); - compileResult.dependencies.forEach((dep) => { - let depTemplate: CompiledTemplate; - if (dep instanceof ViewClassDependency) { - const vfd = dep; - depTemplate = this._assertComponentKnown(vfd.comp.reference, false); - vfd.placeholder.reference = depTemplate.proxyViewClass; - } else if (dep instanceof ComponentFactoryDependency) { - const cfd = dep; - depTemplate = this._assertComponentKnown(cfd.comp.reference, true); - cfd.placeholder.reference = depTemplate.proxyComponentFactory; - } else if (dep instanceof DirectiveWrapperDependency) { - const dwd = dep; - dwd.placeholder.reference = this._assertDirectiveWrapper(dwd.dir.reference); - } - }); const statements = stylesCompileResult.componentStylesheet.statements .concat(...compiledAnimations.map(ca => ca.statements)) .concat(compileResult.statements); @@ -358,30 +341,18 @@ export class JitCompiler implements Compiler { class CompiledTemplate { private _viewClass: Function = null; - proxyViewClass: Type; - proxyComponentFactory: ComponentFactory; isCompiled = false; constructor( - public isHost: boolean, selector: string, public compType: CompileIdentifierMetadata, + public isHost: boolean, public compType: CompileIdentifierMetadata, public compMeta: CompileDirectiveMetadata, public ngModule: CompileNgModuleMetadata, public directives: CompileIdentifierMetadata[]) { const self = this; - this.proxyViewClass = function() { - if (!self._viewClass) { - throw new Error( - `Illegal state: CompiledTemplate for ${stringify(self.compType)} is not compiled yet!`); - } - return self._viewClass.apply(this, arguments); - }; - this.proxyComponentFactory = isHost ? - new ComponentFactory(selector, this.proxyViewClass, compType.reference) : - null; } compiled(viewClass: Function) { this._viewClass = viewClass; - this.proxyViewClass.prototype = viewClass.prototype; + (this.compMeta.componentViewType).setDelegate(viewClass); this.isCompiled = true; } } diff --git a/modules/@angular/compiler/src/metadata_resolver.ts b/modules/@angular/compiler/src/metadata_resolver.ts index 7e34ab09ac..fc26623085 100644 --- a/modules/@angular/compiler/src/metadata_resolver.ts +++ b/modules/@angular/compiler/src/metadata_resolver.ts @@ -6,9 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, Attribute, ChangeDetectionStrategy, Component, Directive, Host, Inject, Injectable, ModuleWithProviders, OpaqueToken, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core'; +import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, Attribute, ChangeDetectionStrategy, Component, ComponentFactory, Directive, Host, Inject, Injectable, ModuleWithProviders, OpaqueToken, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core'; -import {StaticSymbol} from './aot/static_symbol'; +import {StaticSymbol, StaticSymbolCache} from './aot/static_symbol'; +import {ngfactoryFilePath} from './aot/util'; import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions'; import * as cpl from './compile_metadata'; import {DirectiveNormalizer} from './directive_normalizer'; @@ -38,6 +39,8 @@ export const ERROR_COLLECTOR_TOKEN = new OpaqueToken('ErrorCollector'); // to wait correctly. @CompilerInjectable() export class CompileMetadataResolver { + private _nonNormalizedDirectiveCache = + new Map, {annotation: Directive, metadata: cpl.CompileDirectiveMetadata}>(); private _directiveCache = new Map, cpl.CompileDirectiveMetadata>(); private _summaryCache = new Map, cpl.CompileTypeSummary>(); private _pipeCache = new Map, cpl.CompilePipeMetadata>(); @@ -49,12 +52,14 @@ export class CompileMetadataResolver { private _pipeResolver: PipeResolver, private _summaryResolver: SummaryResolver, private _schemaRegistry: ElementSchemaRegistry, private _directiveNormalizer: DirectiveNormalizer, + @Optional() private _staticSymbolCache: StaticSymbolCache, private _reflector: ReflectorReader = reflector, @Optional() @Inject(ERROR_COLLECTOR_TOKEN) private _errorCollector?: ErrorCollector) {} clearCacheFor(type: Type) { const dirMeta = this._directiveCache.get(type); this._directiveCache.delete(type); + this._nonNormalizedDirectiveCache.delete(type); this._summaryCache.delete(type); this._pipeCache.delete(type); this._ngModuleOfTypes.delete(type); @@ -67,6 +72,7 @@ export class CompileMetadataResolver { clearCache() { this._directiveCache.clear(); + this._nonNormalizedDirectiveCache.clear(); this._summaryCache.clear(); this._pipeCache.clear(); this._ngModuleCache.clear(); @@ -74,6 +80,66 @@ export class CompileMetadataResolver { this._directiveNormalizer.clearCache(); } + private _createProxyClass(baseType: any, name: string): cpl.ProxyClass { + let delegate: any = null; + const proxyClass: cpl.ProxyClass = function() { + if (!delegate) { + throw new Error( + `Illegal state: Class ${name} for type ${stringify(baseType)} is not compiled yet!`); + } + return delegate.apply(this, arguments); + }; + proxyClass.setDelegate = (d) => { + delegate = d; + (proxyClass).prototype = d.prototype; + }; + // Make stringify work correctly + (proxyClass).overriddenName = name; + return proxyClass; + } + + private getGeneratedClass(dirType: any, name: string): StaticSymbol|cpl.ProxyClass { + if (dirType instanceof StaticSymbol) { + return this._staticSymbolCache.get(ngfactoryFilePath(dirType.filePath), name); + } else { + return this._createProxyClass(dirType, name); + } + } + + private getDirectiveWrapperClass(dirType: any): StaticSymbol|cpl.ProxyClass { + return this.getGeneratedClass(dirType, cpl.dirWrapperClassName(dirType)); + } + + private getComponentViewClass(dirType: any): StaticSymbol|cpl.ProxyClass { + return this.getGeneratedClass(dirType, cpl.viewClassName(dirType, 0)); + } + + getHostComponentViewClass(dirType: any): StaticSymbol|cpl.ProxyClass { + return this.getGeneratedClass(dirType, cpl.hostViewClassName(dirType)); + } + + getHostComponentType(dirType: any): StaticSymbol|Type { + const name = `${cpl.identifierName({reference: dirType})}_Host`; + if (dirType instanceof StaticSymbol) { + return this._staticSymbolCache.get(dirType.filePath, name); + } else { + const HostClass = function HostClass() {}; + HostClass.overriddenName = name; + + return HostClass; + } + } + + private getComponentFactory(selector: string, dirType: any): StaticSymbol|ComponentFactory { + if (dirType instanceof StaticSymbol) { + return this._staticSymbolCache.get( + ngfactoryFilePath(dirType.filePath), cpl.componentFactoryName(dirType)); + } else { + const hostView = this.getHostComponentViewClass(dirType); + return new ComponentFactory(selector, hostView, dirType); + } + } + getAnimationEntryMetadata(entry: AnimationEntryMetadata): cpl.CompileAnimationEntryMetadata { const defs = entry.definitions.map(def => this._getAnimationStateMetadata(def)); return new cpl.CompileAnimationEntryMetadata(entry.name, defs); @@ -162,6 +228,9 @@ export class CompileMetadataResolver { queries: metadata.queries, viewQueries: metadata.viewQueries, entryComponents: metadata.entryComponents, + wrapperType: metadata.wrapperType, + componentViewType: metadata.componentViewType, + componentFactory: metadata.componentFactory, template: templateMetadata }); this._directiveCache.set(directiveType, normalizedDirMeta); @@ -201,7 +270,14 @@ export class CompileMetadataResolver { getNonNormalizedDirectiveMetadata(directiveType: any): {annotation: Directive, metadata: cpl.CompileDirectiveMetadata} { directiveType = resolveForwardRef(directiveType); - const dirMeta = this._directiveResolver.resolve(directiveType); + if (!directiveType) { + return null; + } + let cacheEntry = this._nonNormalizedDirectiveCache.get(directiveType); + if (cacheEntry) { + return cacheEntry; + } + const dirMeta = this._directiveResolver.resolve(directiveType, false); if (!dirMeta) { return null; } @@ -230,7 +306,7 @@ export class CompileMetadataResolver { let changeDetectionStrategy: ChangeDetectionStrategy = null; let viewProviders: cpl.CompileProviderMetadata[] = []; - let entryComponentMetadata: cpl.CompileIdentifierMetadata[] = []; + let entryComponentMetadata: cpl.CompileEntryComponentMetadata[] = []; let selector = dirMeta.selector; if (dirMeta instanceof Component) { @@ -243,7 +319,7 @@ export class CompileMetadataResolver { } if (dirMeta.entryComponents) { entryComponentMetadata = flattenAndDedupeArray(dirMeta.entryComponents) - .map((type) => this._getIdentifierMetadata(type)) + .map((type) => this._getEntryComponentMetadata(type)) .concat(entryComponentMetadata); } if (!selector) { @@ -287,9 +363,17 @@ export class CompileMetadataResolver { viewProviders: viewProviders, queries: queries, viewQueries: viewQueries, - entryComponents: entryComponentMetadata + entryComponents: entryComponentMetadata, + wrapperType: this.getDirectiveWrapperClass(directiveType), + componentViewType: nonNormalizedTemplateMetadata ? this.getComponentViewClass(directiveType) : + undefined, + componentFactory: nonNormalizedTemplateMetadata ? + this.getComponentFactory(selector, directiveType) : + undefined }); - return {metadata, annotation: dirMeta}; + cacheEntry = {metadata, annotation: dirMeta}; + this._nonNormalizedDirectiveCache.set(directiveType, cacheEntry); + return cacheEntry; } /** @@ -371,7 +455,7 @@ export class CompileMetadataResolver { const importedModules: cpl.CompileNgModuleSummary[] = []; const exportedModules: cpl.CompileNgModuleSummary[] = []; const providers: cpl.CompileProviderMetadata[] = []; - const entryComponents: cpl.CompileIdentifierMetadata[] = []; + const entryComponents: cpl.CompileEntryComponentMetadata[] = []; const bootstrapComponents: cpl.CompileIdentifierMetadata[] = []; const schemas: SchemaMetadata[] = []; @@ -488,7 +572,7 @@ export class CompileMetadataResolver { if (meta.entryComponents) { entryComponents.push(...flattenAndDedupeArray(meta.entryComponents) - .map(type => this._getIdentifierMetadata(type))); + .map(type => this._getEntryComponentMetadata(type))); } if (meta.bootstrap) { @@ -504,7 +588,8 @@ export class CompileMetadataResolver { }); } - entryComponents.push(...bootstrapComponents); + entryComponents.push( + ...bootstrapComponents.map(type => this._getEntryComponentMetadata(type.reference))); if (meta.schemas) { schemas.push(...flattenAndDedupeArray(meta.schemas)); @@ -769,7 +854,7 @@ export class CompileMetadataResolver { } private _getProvidersMetadata( - providers: Provider[], targetEntryComponents: cpl.CompileIdentifierMetadata[], + providers: Provider[], targetEntryComponents: cpl.CompileEntryComponentMetadata[], debugInfo?: string, compileProviders: cpl.CompileProviderMetadata[] = [], type?: any): cpl.CompileProviderMetadata[] { providers.forEach((provider: any, providerIdx: number) => { @@ -813,8 +898,8 @@ export class CompileMetadataResolver { } private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta, type?: any): - cpl.CompileIdentifierMetadata[] { - const components: cpl.CompileIdentifierMetadata[] = []; + cpl.CompileEntryComponentMetadata[] { + const components: cpl.CompileEntryComponentMetadata[] = []; const collectedIdentifiers: cpl.CompileIdentifierMetadata[] = []; if (provider.useFactory || provider.useExisting || provider.useClass) { @@ -832,14 +917,27 @@ export class CompileMetadataResolver { extractIdentifiers(provider.useValue, collectedIdentifiers); collectedIdentifiers.forEach((identifier) => { - if (this._directiveResolver.isDirective(identifier.reference) || - this._loadSummary(identifier.reference, cpl.CompileSummaryKind.Directive)) { - components.push(identifier); + const entry = this._getEntryComponentMetadata(identifier.reference); + if (entry) { + components.push(entry); } }); return components; } + private _getEntryComponentMetadata(dirType: any): cpl.CompileEntryComponentMetadata { + const dirMeta = this.getNonNormalizedDirectiveMetadata(dirType); + if (dirMeta) { + return {componentType: dirType, componentFactory: dirMeta.metadata.componentFactory}; + } else { + const dirSummary = + this._loadSummary(dirType, cpl.CompileSummaryKind.Directive); + if (dirSummary) { + return {componentType: dirType, componentFactory: dirSummary.componentFactory}; + } + } + } + getProviderMetadata(provider: cpl.ProviderMeta): cpl.CompileProviderMetadata { let compileDeps: cpl.CompileDiDependencyMetadata[]; let compileTypeMetadata: cpl.CompileTypeMetadata = null; diff --git a/modules/@angular/compiler/src/ng_module_compiler.ts b/modules/@angular/compiler/src/ng_module_compiler.ts index aa210ede5f..bcb826934b 100644 --- a/modules/@angular/compiler/src/ng_module_compiler.ts +++ b/modules/@angular/compiler/src/ng_module_compiler.ts @@ -19,9 +19,12 @@ import {LifecycleHooks} from './private_import_core'; import {NgModuleProviderAnalyzer} from './provider_analyzer'; import {ProviderAst} from './template_parser/template_ast'; +/** + * This is currently not read, but will probably be used in the future. + * We keep it as we already pass it through all the rigth places... + */ export class ComponentFactoryDependency { - constructor( - public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {} + constructor(public compType: any) {} } export class NgModuleCompileResult { @@ -46,13 +49,12 @@ export class NgModuleCompiler { const bootstrapComponentFactories: CompileIdentifierMetadata[] = []; const entryComponentFactories = ngModuleMeta.transitiveModule.entryComponents.map((entryComponent) => { - const id: CompileIdentifierMetadata = {reference: null}; if (ngModuleMeta.bootstrapComponents.some( - (id) => id.reference === entryComponent.reference)) { - bootstrapComponentFactories.push(id); + (id) => id.reference === entryComponent.componentType)) { + bootstrapComponentFactories.push({reference: entryComponent.componentFactory}); } - deps.push(new ComponentFactoryDependency(entryComponent, id)); - return id; + deps.push(new ComponentFactoryDependency(entryComponent.componentType)); + return {reference: entryComponent.componentFactory}; }); const builder = new _InjectorBuilder( ngModuleMeta, entryComponentFactories, bootstrapComponentFactories, sourceSpan); diff --git a/modules/@angular/compiler/src/output/js_emitter.ts b/modules/@angular/compiler/src/output/js_emitter.ts index c24e6a7ef2..09d28d8d34 100644 --- a/modules/@angular/compiler/src/output/js_emitter.ts +++ b/modules/@angular/compiler/src/output/js_emitter.ts @@ -7,7 +7,8 @@ */ -import {identifierModuleUrl, identifierName} from '../compile_metadata'; +import {StaticSymbol} from '../aot/static_symbol'; +import {CompileIdentifierMetadata} from '../compile_metadata'; import {isBlank, isPresent} from '../facade/lang'; import {EmitterVisitorContext, OutputEmitter} from './abstract_emitter'; @@ -16,17 +17,17 @@ import * as o from './output_ast'; import {ImportResolver} from './path_util'; export class JavaScriptEmitter implements OutputEmitter { - constructor(private _importGenerator: ImportResolver) {} - emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string { - const converter = new JsEmitterVisitor(moduleUrl); + constructor(private _importResolver: ImportResolver) {} + emitStatements(genFilePath: string, stmts: o.Statement[], exportedVars: string[]): string { + const converter = new JsEmitterVisitor(genFilePath, this._importResolver); const ctx = EmitterVisitorContext.createRoot(exportedVars); converter.visitAllStatements(stmts, ctx); const srcParts: string[] = []; - converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => { + converter.importsWithPrefixes.forEach((prefix, importedFilePath) => { // Note: can't write the real word for import as it screws up system.js auto detection... srcParts.push( `var ${prefix} = req` + - `uire('${this._importGenerator.fileNameToModuleName(importedModuleUrl, moduleUrl)}');`); + `uire('${this._importResolver.fileNameToModuleName(importedFilePath, genFilePath)}');`); }); srcParts.push(ctx.toSource()); return srcParts.join('\n'); @@ -36,20 +37,23 @@ export class JavaScriptEmitter implements OutputEmitter { class JsEmitterVisitor extends AbstractJsEmitterVisitor { importsWithPrefixes = new Map(); - constructor(private _moduleUrl: string) { super(); } + constructor(private _genFilePath: string, private _importResolver: ImportResolver) { super(); } + + private _resolveStaticSymbol(value: CompileIdentifierMetadata): StaticSymbol { + const reference = value.reference; + if (!(reference instanceof StaticSymbol)) { + throw new Error(`Internal error: unknown identifier ${JSON.stringify(value)}`); + } + return this._importResolver.getImportAs(reference) || reference; + } visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any { - const name = identifierName(ast.value); - const moduleUrl = identifierModuleUrl(ast.value); - if (isBlank(name)) { - console.error('>>>', ast.value); - throw new Error(`Internal error: unknown identifier ${ast.value}`); - } - if (isPresent(moduleUrl) && moduleUrl != this._moduleUrl) { - let prefix = this.importsWithPrefixes.get(moduleUrl); + const {name, filePath} = this._resolveStaticSymbol(ast.value); + if (filePath != this._genFilePath) { + let prefix = this.importsWithPrefixes.get(filePath); if (isBlank(prefix)) { prefix = `import${this.importsWithPrefixes.size}`; - this.importsWithPrefixes.set(moduleUrl, prefix); + this.importsWithPrefixes.set(filePath, prefix); } ctx.print(`${prefix}.`); } diff --git a/modules/@angular/compiler/src/output/path_util.ts b/modules/@angular/compiler/src/output/path_util.ts index e9869cdc31..b80d861382 100644 --- a/modules/@angular/compiler/src/output/path_util.ts +++ b/modules/@angular/compiler/src/output/path_util.ts @@ -6,6 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ +import {StaticSymbol} from '../aot/static_symbol'; + /** * Interface that defines how import statements should be generated. */ @@ -16,4 +18,10 @@ export abstract class ImportResolver { */ abstract fileNameToModuleName(importedFilePath: string, containingFilePath: string): string /*|null*/; + + /** + * Converts the given StaticSymbol into another StaticSymbol that should be used + * to generate the import from. + */ + abstract getImportAs(symbol: StaticSymbol): StaticSymbol /*|null*/; } diff --git a/modules/@angular/compiler/src/output/ts_emitter.ts b/modules/@angular/compiler/src/output/ts_emitter.ts index dbf65ced31..1cddf917eb 100644 --- a/modules/@angular/compiler/src/output/ts_emitter.ts +++ b/modules/@angular/compiler/src/output/ts_emitter.ts @@ -7,18 +7,22 @@ */ -import {CompileIdentifierMetadata, identifierModuleUrl, identifierName} from '../compile_metadata'; +import {StaticSymbol} from '../aot/static_symbol'; +import {CompileIdentifierMetadata} from '../compile_metadata'; import {isBlank, isPresent} from '../facade/lang'; import {AbstractEmitterVisitor, CATCH_ERROR_VAR, CATCH_STACK_VAR, EmitterVisitorContext, OutputEmitter} from './abstract_emitter'; import * as o from './output_ast'; import {ImportResolver} from './path_util'; -const _debugModuleUrl = '/debug/lib'; +const _debugFilePath = '/debug/lib'; export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.Type | any[]): string { - const converter = new _TsEmitterVisitor(_debugModuleUrl); + const converter = new _TsEmitterVisitor(_debugFilePath, { + fileNameToModuleName(filePath: string, containingFilePath: string) { return filePath; }, + getImportAs(symbol: StaticSymbol) { return null; } + }); const ctx = EmitterVisitorContext.createRoot([]); const asts: any[] = Array.isArray(ast) ? ast : [ast]; @@ -37,17 +41,23 @@ export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.T } export class TypeScriptEmitter implements OutputEmitter { - constructor(private _importGenerator: ImportResolver) {} - emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string { - const converter = new _TsEmitterVisitor(moduleUrl); + constructor(private _importResolver: ImportResolver) {} + emitStatements(genFilePath: string, stmts: o.Statement[], exportedVars: string[]): string { + const converter = new _TsEmitterVisitor(genFilePath, this._importResolver); const ctx = EmitterVisitorContext.createRoot(exportedVars); converter.visitAllStatements(stmts, ctx); const srcParts: string[] = []; - converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => { + converter.reexports.forEach((reexports, exportedFilePath) => { + const reexportsCode = + reexports.map(reexport => `${reexport.name} as ${reexport.as}`).join(','); + srcParts.push( + `export {${reexportsCode}} from '${this._importResolver.fileNameToModuleName(exportedFilePath, genFilePath)}';`); + }); + converter.importsWithPrefixes.forEach((prefix, importedFilePath) => { // Note: can't write the real word for import as it screws up system.js auto detection... srcParts.push( `imp` + - `ort * as ${prefix} from '${this._importGenerator.fileNameToModuleName(importedModuleUrl, moduleUrl)}';`); + `ort * as ${prefix} from '${this._importResolver.fileNameToModuleName(importedFilePath, genFilePath)}';`); }); srcParts.push(ctx.toSource()); return srcParts.join('\n'); @@ -55,9 +65,12 @@ export class TypeScriptEmitter implements OutputEmitter { } class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor { - constructor(private _moduleUrl: string) { super(false); } + constructor(private _genFilePath: string, private _importResolver: ImportResolver) { + super(false); + } importsWithPrefixes = new Map(); + reexports = new Map(); visitType(t: o.Type, ctx: EmitterVisitorContext, defaultType: string = 'any') { if (isPresent(t)) { @@ -98,6 +111,19 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor } visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any { + if (ctx.isExportedVar(stmt.name) && stmt.value instanceof o.ExternalExpr && !stmt.type) { + // check for a reexport + const {name, filePath, members} = this._resolveStaticSymbol(stmt.value.value); + if (members.length === 0 && filePath !== this._genFilePath) { + let reexports = this.reexports.get(filePath); + if (!reexports) { + reexports = []; + this.reexports.set(filePath, reexports); + } + reexports.push({name, as: stmt.name}); + return null; + } + } if (ctx.isExportedVar(stmt.name)) { ctx.print(`export `); } @@ -320,25 +346,29 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor }, params, ctx, ','); } + private _resolveStaticSymbol(value: CompileIdentifierMetadata): StaticSymbol { + const reference = value.reference; + if (!(reference instanceof StaticSymbol)) { + throw new Error(`Internal error: unknown identifier ${JSON.stringify(value)}`); + } + return this._importResolver.getImportAs(reference) || reference; + } + private _visitIdentifier( value: CompileIdentifierMetadata, typeParams: o.Type[], ctx: EmitterVisitorContext): void { - const name = identifierName(value); - const moduleUrl = identifierModuleUrl(value); - if (isBlank(name)) { - throw new Error(`Internal error: unknown identifier ${value}`); - } - if (isPresent(moduleUrl) && moduleUrl != this._moduleUrl) { - let prefix = this.importsWithPrefixes.get(moduleUrl); + const {name, filePath, members} = this._resolveStaticSymbol(value); + if (filePath != this._genFilePath) { + let prefix = this.importsWithPrefixes.get(filePath); if (isBlank(prefix)) { prefix = `import${this.importsWithPrefixes.size}`; - this.importsWithPrefixes.set(moduleUrl, prefix); + this.importsWithPrefixes.set(filePath, prefix); } ctx.print(`${prefix}.`); } - if (value.reference && value.reference.members && value.reference.members.length) { - ctx.print(value.reference.name); + if (members.length) { + ctx.print(name); ctx.print('.'); - ctx.print(value.reference.members.join('.')); + ctx.print(members.join('.')); } else { ctx.print(name); } diff --git a/modules/@angular/compiler/src/summary_resolver.ts b/modules/@angular/compiler/src/summary_resolver.ts index 02d6e05243..55b53eae07 100644 --- a/modules/@angular/compiler/src/summary_resolver.ts +++ b/modules/@angular/compiler/src/summary_resolver.ts @@ -16,6 +16,9 @@ export interface Summary { @CompilerInjectable() export class SummaryResolver { + isLibraryFile(fileName: string): boolean { return false; }; + getLibraryFileName(fileName: string): string { return null; } resolveSummary(reference: T): Summary { return null; }; getSymbolsOf(filePath: string): T[] { return []; } + getImportAs(reference: T): T { return reference; } } diff --git a/modules/@angular/compiler/src/view_compiler/compile_element.ts b/modules/@angular/compiler/src/view_compiler/compile_element.ts index 19b9c61a9f..ce7d9a8880 100644 --- a/modules/@angular/compiler/src/view_compiler/compile_element.ts +++ b/modules/@angular/compiler/src/view_compiler/compile_element.ts @@ -97,12 +97,11 @@ export class CompileElement extends CompileNode { } private _createComponentFactoryResolver() { - const entryComponents = - this.component.entryComponents.map((entryComponent: CompileIdentifierMetadata) => { - const id: CompileIdentifierMetadata = {reference: null}; - this.view.targetDependencies.push(new ComponentFactoryDependency(entryComponent, id)); - return id; - }); + const entryComponents = this.component.entryComponents.map((entryComponent) => { + this.view.targetDependencies.push( + new ComponentFactoryDependency(entryComponent.componentType)); + return {reference: entryComponent.componentFactory}; + }); if (!entryComponents || entryComponents.length === 0) { return; } @@ -179,11 +178,11 @@ export class CompileElement extends CompileNode { const depsExpr = deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep)); if (isDirectiveWrapper) { - const directiveWrapperIdentifier: CompileIdentifierMetadata = {reference: null}; - this.view.targetDependencies.push(new DirectiveWrapperDependency( - provider.useClass, DirectiveWrapperCompiler.dirWrapperClassName(provider.useClass), - directiveWrapperIdentifier)); - return DirectiveWrapperExpressions.create(directiveWrapperIdentifier, depsExpr); + const dirMeta = + this._directives.find(dir => dir.type.reference === provider.useClass.reference); + this.view.targetDependencies.push( + new DirectiveWrapperDependency(dirMeta.type.reference)); + return DirectiveWrapperExpressions.create({reference: dirMeta.wrapperType}, depsExpr); } else { return o.importExpr(provider.useClass) .instantiate(depsExpr, o.importType(provider.useClass)); diff --git a/modules/@angular/compiler/src/view_compiler/compile_view.ts b/modules/@angular/compiler/src/view_compiler/compile_view.ts index bccefa2851..c1d999f596 100644 --- a/modules/@angular/compiler/src/view_compiler/compile_view.ts +++ b/modules/@angular/compiler/src/view_compiler/compile_view.ts @@ -7,7 +7,7 @@ */ import {AnimationEntryCompileResult} from '../animation/animation_compiler'; -import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeSummary, tokenName} from '../compile_metadata'; +import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeSummary, tokenName, viewClassName} from '../compile_metadata'; import {EventHandlerVars, NameResolver} from '../compiler_util/expression_converter'; import {createPureProxy} from '../compiler_util/identifier_util'; import {CompilerConfig} from '../config'; @@ -20,8 +20,8 @@ import {CompileElement, CompileNode} from './compile_element'; import {CompileMethod} from './compile_method'; import {CompilePipe} from './compile_pipe'; import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query'; -import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency} from './deps'; -import {getPropertyInView, getViewClassName} from './util'; +import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency} from './deps'; +import {getPropertyInView} from './util'; export enum CompileViewRootNodeType { Node, @@ -87,7 +87,7 @@ export class CompileView implements NameResolver { public animations: AnimationEntryCompileResult[], public viewIndex: number, public declarationElement: CompileElement, public templateVariableBindings: string[][], public targetDependencies: - Array) { + Array) { this.createMethod = new CompileMethod(this); this.animationBindingsMethod = new CompileMethod(this); this.injectorGetMethod = new CompileMethod(this); @@ -103,7 +103,7 @@ export class CompileView implements NameResolver { this.detachMethod = new CompileMethod(this); this.viewType = getViewType(component, viewIndex); - this.className = getViewClassName(component, viewIndex); + this.className = viewClassName(component.type.reference, viewIndex); this.classType = o.expressionType(o.variable(this.className)); this.classExpr = o.variable(this.className); if (this.viewType === ViewType.COMPONENT || this.viewType === ViewType.HOST) { diff --git a/modules/@angular/compiler/src/view_compiler/deps.ts b/modules/@angular/compiler/src/view_compiler/deps.ts index c00f3ee126..0f9fa388dc 100644 --- a/modules/@angular/compiler/src/view_compiler/deps.ts +++ b/modules/@angular/compiler/src/view_compiler/deps.ts @@ -8,19 +8,26 @@ import {CompileIdentifierMetadata} from '../compile_metadata'; -export class ViewClassDependency { - constructor( - public comp: CompileIdentifierMetadata, public name: string, - public placeholder: CompileIdentifierMetadata) {} +/** + * This is currently not read, but will probably be used in the future. + * We keep it as we already pass it through all the rigth places... + */ +export class ComponentViewDependency { + constructor(public compType: any) {} } +/** + * This is currently not read, but will probably be used in the future. + * We keep it as we already pass it through all the rigth places... + */ export class ComponentFactoryDependency { - constructor( - public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {} + constructor(public compType: any) {} } +/** + * This is currently not read, but will probably be used in the future. + * We keep it as we already pass it through all the rigth places... + */ export class DirectiveWrapperDependency { - constructor( - public dir: CompileIdentifierMetadata, public name: string, - public placeholder: CompileIdentifierMetadata) {} + constructor(public dirType: any) {} } diff --git a/modules/@angular/compiler/src/view_compiler/util.ts b/modules/@angular/compiler/src/view_compiler/util.ts index 7c07aa5622..4be25a22f4 100644 --- a/modules/@angular/compiler/src/view_compiler/util.ts +++ b/modules/@angular/compiler/src/view_compiler/util.ts @@ -71,12 +71,6 @@ export function injectFromViewParentInjector( return viewExpr.callMethod('injectorGet', args); } -export function getViewClassName( - component: CompileDirectiveSummary | CompileDirectiveMetadata, - embeddedTemplateIndex: number): string { - return `View_${identifierName(component.type)}${embeddedTemplateIndex}`; -} - export function getHandleEventMethodName(elementIndex: number): string { return `handleEvent_${elementIndex}`; } diff --git a/modules/@angular/compiler/src/view_compiler/view_builder.ts b/modules/@angular/compiler/src/view_compiler/view_builder.ts index dad3f07c66..305b686a5e 100644 --- a/modules/@angular/compiler/src/view_compiler/view_builder.ts +++ b/modules/@angular/compiler/src/view_compiler/view_builder.ts @@ -8,7 +8,7 @@ import {ViewEncapsulation} from '@angular/core'; -import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName} from '../compile_metadata'; +import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName, viewClassName} from '../compile_metadata'; import {createSharedBindingVariablesIfNeeded} from '../compiler_util/expression_converter'; import {createDiTokenExpression, createInlineArray} from '../compiler_util/identifier_util'; import {isPresent} from '../facade/lang'; @@ -22,8 +22,7 @@ import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventA import {CompileElement, CompileNode} from './compile_element'; import {CompileView, CompileViewRootNode, CompileViewRootNodeType} from './compile_view'; import {ChangeDetectorStatusEnum, DetectChangesVars, InjectMethodVars, ViewConstructorVars, ViewEncapsulationEnum, ViewProperties, ViewTypeEnum} from './constants'; -import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency} from './deps'; -import {getViewClassName} from './util'; +import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency} from './deps'; const IMPLICIT_TEMPLATE_VAR = '\$implicit'; const CLASS_ATTR = 'class'; @@ -36,7 +35,8 @@ const rootSelectorVar = o.variable('rootSelector'); export function buildView( view: CompileView, template: TemplateAst[], targetDependencies: - Array): number { + Array): + number { const builderVisitor = new ViewBuilderVisitor(view, targetDependencies); const parentEl = view.declarationElement.isNull() ? view.declarationElement : view.declarationElement.parent; @@ -63,7 +63,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor { constructor( public view: CompileView, public targetDependencies: - Array) {} + Array) {} private _isRootNode(parent: CompileElement): boolean { return parent.view !== this.view; } @@ -214,9 +214,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor { this.view.nodes.push(compileElement); let compViewExpr: o.ReadPropExpr = null; if (isPresent(component)) { - const nestedComponentIdentifier: CompileIdentifierMetadata = {reference: null}; - this.targetDependencies.push(new ViewClassDependency( - component.type, getViewClassName(component, 0), nestedComponentIdentifier)); + this.targetDependencies.push(new ComponentViewDependency(component.type.reference)); compViewExpr = o.THIS_EXPR.prop(`compView_${nodeIndex}`); // fix highlighting: ` this.view.fields.push(new o.ClassField( @@ -226,7 +224,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor { compileElement.setComponentView(compViewExpr); this.view.createMethod.addStmt( compViewExpr - .set(o.importExpr(nestedComponentIdentifier).instantiate([ + .set(o.importExpr({reference: component.componentViewType}).instantiate([ ViewProperties.viewUtils, o.THIS_EXPR, o.literal(nodeIndex), renderNode ])) .toStmt()); diff --git a/modules/@angular/compiler/src/view_compiler/view_compiler.ts b/modules/@angular/compiler/src/view_compiler/view_compiler.ts index aaaa5e2d1a..125f1dfee3 100644 --- a/modules/@angular/compiler/src/view_compiler/view_compiler.ts +++ b/modules/@angular/compiler/src/view_compiler/view_compiler.ts @@ -16,17 +16,17 @@ import {TemplateAst} from '../template_parser/template_ast'; import {CompileElement} from './compile_element'; import {CompileView} from './compile_view'; -import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency} from './deps'; +import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency} from './deps'; import {bindView} from './view_binder'; import {buildView, finishView} from './view_builder'; -export {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency} from './deps'; +export {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency} from './deps'; export class ViewCompileResult { constructor( public statements: o.Statement[], public viewClassVar: string, public dependencies: - Array) {} + Array) {} } @CompilerInjectable() @@ -38,7 +38,7 @@ export class ViewCompiler { pipes: CompilePipeSummary[], compiledAnimations: AnimationEntryCompileResult[]): ViewCompileResult { const dependencies: - Array = []; + Array = []; const view = new CompileView( component, this._genConfig, pipes, styles, compiledAnimations, 0, CompileElement.createNull(), [], dependencies); diff --git a/modules/@angular/compiler/test/aot/static_reflector_spec.ts b/modules/@angular/compiler/test/aot/static_reflector_spec.ts index 321808c8b8..515b924564 100644 --- a/modules/@angular/compiler/test/aot/static_reflector_spec.ts +++ b/modules/@angular/compiler/test/aot/static_reflector_spec.ts @@ -487,6 +487,8 @@ describe('StaticReflector', () => { export class Child extends Parent {} export class ChildNoDecorators extends Parent {} + + export class ChildInvalidParent extends a.InvalidParent {} ` }); @@ -500,6 +502,10 @@ describe('StaticReflector', () => { expect( reflector.annotations(reflector.getStaticSymbol('/tmp/src/main.ts', 'ChildNoDecorators'))) .toEqual([new ClassDecorator('parent')]); + + expect(reflector.annotations( + reflector.getStaticSymbol('/tmp/src/main.ts', 'ChildInvalidParent'))) + .toEqual([]); }); it('should inherit parameters', () => { @@ -520,6 +526,8 @@ describe('StaticReflector', () => { export class ChildWithCtor extends Parent { constructor(@ParamDecorator('c') c: C) {} } + + export class ChildInvalidParent extends a.InvalidParent {} ` }); @@ -537,6 +545,10 @@ describe('StaticReflector', () => { expect(reflector.parameters(reflector.getStaticSymbol('/tmp/src/main.ts', 'ChildWithCtor'))) .toEqual([[reflector.getStaticSymbol('/tmp/src/main.ts', 'C'), new ParamDecorator('c')]]); + + expect( + reflector.parameters(reflector.getStaticSymbol('/tmp/src/main.ts', 'ChildInvalidParent'))) + .toEqual([]); }); it('should inherit property metadata', () => { @@ -561,6 +573,8 @@ describe('StaticReflector', () => { @PropDecorator('c') c: C; } + + export class ChildInvalidParent extends a.InvalidParent {} ` }); @@ -577,6 +591,10 @@ describe('StaticReflector', () => { 'b': [new PropDecorator('b1'), new PropDecorator('b2')], 'c': [new PropDecorator('c')] }); + + expect(reflector.propMetadata( + reflector.getStaticSymbol('/tmp/src/main.ts', 'ChildInvalidParent'))) + .toEqual({}); }); it('should inherit lifecycle hooks', () => { @@ -591,6 +609,8 @@ describe('StaticReflector', () => { hook2() {} hook3() {} } + + export class ChildInvalidParent extends a.InvalidParent {} ` }); @@ -606,6 +626,10 @@ describe('StaticReflector', () => { expect(hooks(reflector.getStaticSymbol('/tmp/src/main.ts', 'Child'), [ 'hook1', 'hook2', 'hook3' ])).toEqual([true, true, true]); + + expect(hooks(reflector.getStaticSymbol('/tmp/src/main.ts', 'ChildInvalidParent'), [ + 'hook1', 'hook2', 'hook3' + ])).toEqual([false, false, false]); }); }); diff --git a/modules/@angular/compiler/test/aot/static_symbol_resolver_spec.ts b/modules/@angular/compiler/test/aot/static_symbol_resolver_spec.ts index f68c8adbc8..5c88f6d22a 100644 --- a/modules/@angular/compiler/test/aot/static_symbol_resolver_spec.ts +++ b/modules/@angular/compiler/test/aot/static_symbol_resolver_spec.ts @@ -16,7 +16,7 @@ import * as ts from 'typescript'; const TS_EXT = /(^.|(?!\.d)..)\.ts$/; describe('StaticSymbolResolver', () => { - const noContext = new StaticSymbol('', ''); + const noContext = new StaticSymbol('', '', []); let host: StaticSymbolResolverHost; let symbolResolver: StaticSymbolResolver; let symbolCache: StaticSymbolCache; @@ -24,10 +24,11 @@ describe('StaticSymbolResolver', () => { beforeEach(() => { symbolCache = new StaticSymbolCache(); }); function init( - testData: {[key: string]: any} = DEFAULT_TEST_DATA, summaries: Summary[] = []) { + testData: {[key: string]: any} = DEFAULT_TEST_DATA, summaries: Summary[] = [], + summaryImportAs: {symbol: StaticSymbol, importAs: StaticSymbol}[] = []) { host = new MockStaticSymbolResolverHost(testData); - symbolResolver = - new StaticSymbolResolver(host, symbolCache, new MockSummaryResolver(summaries)); + symbolResolver = new StaticSymbolResolver( + host, symbolCache, new MockSummaryResolver(summaries, summaryImportAs)); } beforeEach(() => init()); @@ -137,6 +138,73 @@ describe('StaticSymbolResolver', () => { ]); }); + describe('importAs', () => { + + it('should calculate importAs relationship for non source files without summaries', () => { + init( + { + '/test.d.ts': [{ + '__symbolic': 'module', + 'version': 3, + 'metadata': { + 'a': {'__symbolic': 'reference', 'name': 'b', 'module': './test2'}, + } + }], + '/test2.d.ts': [{ + '__symbolic': 'module', + 'version': 3, + 'metadata': { + 'b': {'__symbolic': 'reference', 'name': 'c', 'module': './test3'}, + } + }] + }, + []); + symbolResolver.getSymbolsOf('/test.d.ts'); + symbolResolver.getSymbolsOf('/test2.d.ts'); + + expect(symbolResolver.getImportAs(symbolCache.get('/test2.d.ts', 'b'))) + .toBe(symbolCache.get('/test.d.ts', 'a')); + expect(symbolResolver.getImportAs(symbolCache.get('/test3.d.ts', 'c'))) + .toBe(symbolCache.get('/test.d.ts', 'a')); + }); + + it('should calculate importAs relationship for non source files with summaries', () => { + init( + { + '/test.ts': ` + export {a} from './test2'; + ` + }, + [], [{ + symbol: symbolCache.get('/test2.d.ts', 'a'), + importAs: symbolCache.get('/test3.d.ts', 'b') + }]); + symbolResolver.getSymbolsOf('/test.ts'); + + expect(symbolResolver.getImportAs(symbolCache.get('/test2.d.ts', 'a'))) + .toBe(symbolCache.get('/test3.d.ts', 'b')); + }); + + it('should calculate importAs for symbols with members based on importAs for symbols without', + () => { + init( + { + '/test.ts': ` + export {a} from './test2'; + ` + }, + [], [{ + symbol: symbolCache.get('/test2.d.ts', 'a'), + importAs: symbolCache.get('/test3.d.ts', 'b') + }]); + symbolResolver.getSymbolsOf('/test.ts'); + + expect(symbolResolver.getImportAs(symbolCache.get('/test2.d.ts', 'a', ['someMember']))) + .toBe(symbolCache.get('/test3.d.ts', 'b', ['someMember'])); + }); + + }); + it('should replace references by StaticSymbols', () => { init({ '/test.ts': ` @@ -180,6 +248,42 @@ describe('StaticSymbolResolver', () => { .toBeFalsy(); }); + it('should fill references to ambient symbols with undefined', () => { + init({ + '/test.ts': ` + export var y = 1; + export var z = [window, z]; + ` + }); + + expect(symbolResolver.resolveSymbol(symbolCache.get('/test.ts', 'z')).metadata).toEqual([ + undefined, symbolCache.get('/test.ts', 'z') + ]); + }); + + it('should allow to use symbols with __', () => { + init({ + '/test.ts': ` + export {__a__ as __b__} from './test2'; + import {__c__} from './test2'; + + export var __x__ = 1; + export var __y__ = __c__; + ` + }); + + expect(symbolResolver.resolveSymbol(symbolCache.get('/test.ts', '__x__')).metadata).toBe(1); + expect(symbolResolver.resolveSymbol(symbolCache.get('/test.ts', '__y__')).metadata) + .toBe(symbolCache.get('/test2.d.ts', '__c__')); + expect(symbolResolver.resolveSymbol(symbolCache.get('/test.ts', '__b__')).metadata) + .toBe(symbolCache.get('/test2.d.ts', '__a__')); + + expect(symbolResolver.getSymbolsOf('/test.ts')).toEqual([ + symbolCache.get('/test.ts', '__x__'), symbolCache.get('/test.ts', '__y__'), + symbolCache.get('/test.ts', '__b__') + ]); + }); + it('should be able to trace a named export', () => { const symbol = symbolResolver .resolveSymbol(symbolResolver.getSymbolByModule( @@ -240,7 +344,10 @@ describe('StaticSymbolResolver', () => { }); export class MockSummaryResolver implements SummaryResolver { - constructor(private summaries: Summary[] = []) {} + constructor(private summaries: Summary[] = [], private importAs: { + symbol: StaticSymbol, + importAs: StaticSymbol + }[] = []) {} resolveSummary(reference: StaticSymbol): Summary { return this.summaries.find(summary => summary.symbol === reference); @@ -249,6 +356,13 @@ export class MockSummaryResolver implements SummaryResolver { return this.summaries.filter(summary => summary.symbol.filePath === filePath) .map(summary => summary.symbol); } + getImportAs(symbol: StaticSymbol): StaticSymbol { + const entry = this.importAs.find(entry => entry.symbol === symbol); + return entry ? entry.importAs : undefined; + } + + isLibraryFile(filePath: string): boolean { return filePath.endsWith('.d.ts'); } + getLibraryFileName(filePath: string): string { return filePath.replace(/(\.d)?\.ts$/, '.d.ts'); } } export class MockStaticSymbolResolverHost implements StaticSymbolResolverHost { diff --git a/modules/@angular/compiler/test/aot/summary_resolver_spec.ts b/modules/@angular/compiler/test/aot/summary_resolver_spec.ts index 76d1aafc8c..d24d499933 100644 --- a/modules/@angular/compiler/test/aot/summary_resolver_spec.ts +++ b/modules/@angular/compiler/test/aot/summary_resolver_spec.ts @@ -7,12 +7,12 @@ */ import {AotSummaryResolver, AotSummaryResolverHost, CompileSummaryKind, CompileTypeSummary, ResolvedStaticSymbol, StaticSymbol, StaticSymbolCache, StaticSymbolResolver} from '@angular/compiler'; -import {AotSummarySerializerHost, deserializeSummaries, serializeSummaries} from '@angular/compiler/src/aot/summary_serializer'; +import {deserializeSummaries, serializeSummaries} from '@angular/compiler/src/aot/summary_serializer'; import * as path from 'path'; import {MockStaticSymbolResolverHost, MockSummaryResolver} from './static_symbol_resolver_spec'; -const EXT = /\.ts$|.d.ts$/; +const EXT = /(\.d)?\.ts$/; export function main() { describe('AotSummaryResolver', () => { @@ -32,8 +32,7 @@ export function main() { const mockSummaryResolver = new MockSummaryResolver([]); const symbolResolver = new StaticSymbolResolver( new MockStaticSymbolResolverHost({}), symbolCache, mockSummaryResolver); - return serializeSummaries( - new MockAotSummarySerializerHost(), mockSummaryResolver, symbolResolver, symbols, types); + return serializeSummaries(mockSummaryResolver, symbolResolver, symbols, types).json; } it('should load serialized summary files', () => { @@ -56,17 +55,48 @@ export function main() { expect(summaryResolver.resolveSummary(asymbol)).toBe(summaryResolver.resolveSummary(asymbol)); }); - it('should return all sumbols in a summary', () => { + it('should return all symbols in a summary', () => { const asymbol = symbolCache.get('/a.d.ts', 'a'); init({'/a.ngsummary.json': serialize([{symbol: asymbol, metadata: 1}], [])}); expect(summaryResolver.getSymbolsOf('/a.d.ts')).toEqual([asymbol]); + }); + it('should fill importAs for deep symbols', () => { + const libSymbol = symbolCache.get('/lib.d.ts', 'Lib'); + const srcSymbol = symbolCache.get('/src.ts', 'Src'); + init({ + '/src.ngsummary.json': + serialize([{symbol: srcSymbol, metadata: 1}, {symbol: libSymbol, metadata: 2}], []) + }); + summaryResolver.getSymbolsOf('/src.d.ts'); + + expect(summaryResolver.getImportAs(symbolCache.get('/src.d.ts', 'Src'))).toBeFalsy(); + expect(summaryResolver.getImportAs(libSymbol)) + .toBe(symbolCache.get('/src.ngfactory.ts', 'Lib_1')); + }); + + describe('isLibraryFile', () => { + it('should use host.isSourceFile to calculate the result', () => { + init(); + expect(summaryResolver.isLibraryFile('someFile.ts')).toBe(false); + expect(summaryResolver.isLibraryFile('someFile.d.ts')).toBe(true); + }); + + it('should calculate the result for generated files based on the result for non generated files', + () => { + init(); + spyOn(host, 'isSourceFile').and.callThrough(); + expect(summaryResolver.isLibraryFile('someFile.ngfactory.ts')).toBe(false); + expect(host.isSourceFile).toHaveBeenCalledWith('someFile.ts'); + }); }); }); } -export class MockAotSummarySerializerHost implements AotSummarySerializerHost { +export class MockAotSummaryResolverHost implements AotSummaryResolverHost { + constructor(private summaries: {[fileName: string]: string}) {} + fileNameToModuleName(fileName: string): string { return './' + path.basename(fileName).replace(EXT, ''); } @@ -76,11 +106,6 @@ export class MockAotSummarySerializerHost implements AotSummarySerializerHost { } isSourceFile(filePath: string) { return !filePath.endsWith('.d.ts'); } -} - -export class MockAotSummaryResolverHost extends MockAotSummarySerializerHost implements - AotSummaryResolverHost { - constructor(private summaries: {[fileName: string]: string}) { super(); } loadSummary(filePath: string): string { return this.summaries[filePath]; } } \ No newline at end of file diff --git a/modules/@angular/compiler/test/aot/summary_serializer_spec.ts b/modules/@angular/compiler/test/aot/summary_serializer_spec.ts index 064f3a72a4..abd39dab27 100644 --- a/modules/@angular/compiler/test/aot/summary_serializer_spec.ts +++ b/modules/@angular/compiler/test/aot/summary_serializer_spec.ts @@ -7,7 +7,8 @@ */ import {AotSummaryResolver, AotSummaryResolverHost, CompileSummaryKind, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost} from '@angular/compiler'; -import {AotSummarySerializerHost, deserializeSummaries, serializeSummaries, summaryFileName} from '@angular/compiler/src/aot/summary_serializer'; +import {deserializeSummaries, serializeSummaries} from '@angular/compiler/src/aot/summary_serializer'; +import {summaryFileName} from '@angular/compiler/src/aot/util'; import {MockStaticSymbolResolverHost} from './static_symbol_resolver_spec'; import {MockAotSummaryResolverHost} from './summary_resolver_spec'; @@ -42,7 +43,7 @@ export function main() { it('should serialize various data correctly', () => { init(); const serializedData = serializeSummaries( - host, summaryResolver, symbolResolver, + summaryResolver, symbolResolver, [ { symbol: symbolCache.get('/tmp/some_values.ts', 'Values'), @@ -50,7 +51,9 @@ export function main() { aNumber: 1, aString: 'hello', anArray: [1, 2], - aStaticSymbol: symbolCache.get('/tmp/some_symbol.ts', 'someName') + aStaticSymbol: symbolCache.get('/tmp/some_symbol.ts', 'someName'), + aStaticSymbolWithMembers: + symbolCache.get('/tmp/some_symbol.ts', 'someName', ['someMember']), } }, { @@ -66,11 +69,11 @@ export function main() { summaryKind: CompileSummaryKind.Injectable, type: { reference: symbolCache.get('/tmp/some_service.ts', 'SomeService'), - }, + } }]); - const summaries = deserializeSummaries(symbolCache, serializedData); + const summaries = deserializeSummaries(symbolCache, serializedData.json).summaries; expect(summaries.length).toBe(2); // Note: change from .ts to .d.ts is expected @@ -79,7 +82,9 @@ export function main() { aNumber: 1, aString: 'hello', anArray: [1, 2], - aStaticSymbol: symbolCache.get('/tmp/some_symbol.d.ts', 'someName') + aStaticSymbol: symbolCache.get('/tmp/some_symbol.d.ts', 'someName'), + aStaticSymbolWithMembers: + symbolCache.get('/tmp/some_symbol.d.ts', 'someName', ['someMember']) }); expect(summaries[1].symbol).toBe(symbolCache.get('/tmp/some_service.d.ts', 'SomeService')); @@ -91,8 +96,8 @@ export function main() { it('should automatically add exported directives / pipes of NgModules that are not source files', () => { - init({}); - const externalSerialized = serializeSummaries(host, summaryResolver, symbolResolver, [], [ + init(); + const externalSerialized = serializeSummaries(summaryResolver, symbolResolver, [], [ { summaryKind: CompileSummaryKind.Pipe, type: { @@ -107,11 +112,11 @@ export function main() { } ]); init({ - '/tmp/external.ngsummary.json': externalSerialized, + '/tmp/external.ngsummary.json': externalSerialized.json, }); const serialized = serializeSummaries( - host, summaryResolver, symbolResolver, [], [{ + summaryResolver, symbolResolver, [], [{ summaryKind: CompileSummaryKind.NgModule, type: {reference: symbolCache.get('/tmp/some_module.ts', 'SomeModule')}, exportedPipes: [ @@ -124,7 +129,7 @@ export function main() { ] }]); - const summaries = deserializeSummaries(symbolCache, serialized); + const summaries = deserializeSummaries(symbolCache, serialized.json).summaries; expect(summaries.length).toBe(3); expect(summaries[0].symbol).toBe(symbolCache.get('/tmp/some_module.d.ts', 'SomeModule')); expect(summaries[1].symbol).toBe(symbolCache.get('/tmp/external.d.ts', 'SomeExternalDir')); @@ -134,8 +139,9 @@ export function main() { it('should automatically add the metadata of referenced symbols that are not in the soure files', () => { + init(); const externalSerialized = serializeSummaries( - host, summaryResolver, symbolResolver, + summaryResolver, symbolResolver, [ { symbol: symbolCache.get('/tmp/external.ts', 'PROVIDERS'), @@ -154,7 +160,7 @@ export function main() { }]); init( { - '/tmp/external.ngsummary.json': externalSerialized, + '/tmp/external.ngsummary.json': externalSerialized.json, }, { '/tmp/local.ts': ` @@ -164,7 +170,7 @@ export function main() { {__symbolic: 'module', version: 3, metadata: {'external': 'b'}} }); const serialized = serializeSummaries( - host, summaryResolver, symbolResolver, [{ + summaryResolver, symbolResolver, [{ symbol: symbolCache.get('/tmp/test.ts', 'main'), metadata: { local: symbolCache.get('/tmp/local.ts', 'local'), @@ -174,7 +180,7 @@ export function main() { }], []); - const summaries = deserializeSummaries(symbolCache, serialized); + const summaries = deserializeSummaries(symbolCache, serialized.json).summaries; // Note: local should not show up! expect(summaries.length).toBe(4); expect(summaries[0].symbol).toBe(symbolCache.get('/tmp/test.d.ts', 'main')); @@ -195,5 +201,28 @@ export function main() { expect(summaries[3].type.type.reference) .toBe(symbolCache.get('/tmp/external_svc.d.ts', 'SomeService')); }); + + it('should create "importAs" names for non source symbols', () => { + init(); + const serialized = serializeSummaries( + summaryResolver, symbolResolver, [{ + symbol: symbolCache.get('/tmp/test.ts', 'main'), + metadata: [ + symbolCache.get('/tmp/external.d.ts', 'lib'), + symbolCache.get('/tmp/external.d.ts', 'lib', ['someMember']), + ] + }], + []); + // Note: no entry for the symbol with members! + expect(serialized.exportAs).toEqual([ + {symbol: symbolCache.get('/tmp/external.d.ts', 'lib'), exportAs: 'lib_1'} + ]); + + const deserialized = deserializeSummaries(symbolCache, serialized.json); + // Note: no entry for the symbol with members! + expect(deserialized.importAs).toEqual([ + {symbol: symbolCache.get('/tmp/external.d.ts', 'lib'), importAs: 'lib_1'} + ]); + }); }); } diff --git a/modules/@angular/compiler/test/output/js_emitter_spec.ts b/modules/@angular/compiler/test/output/js_emitter_spec.ts index 81c5ce8c0f..6d53cef55b 100644 --- a/modules/@angular/compiler/test/output/js_emitter_spec.ts +++ b/modules/@angular/compiler/test/output/js_emitter_spec.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ +import {StaticSymbol} from '@angular/compiler/src/aot/static_symbol'; import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata'; import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter'; import * as o from '@angular/compiler/src/output/output_ast'; @@ -15,16 +16,17 @@ const someModuleUrl = 'somePackage/somePath'; const anotherModuleUrl = 'somePackage/someOtherPath'; const sameModuleIdentifier: CompileIdentifierMetadata = { - reference: {name: 'someLocalId', filePath: someModuleUrl} + reference: new StaticSymbol(someModuleUrl, 'someLocalId', []) }; const externalModuleIdentifier: CompileIdentifierMetadata = { - reference: {name: 'someExternalId', filePath: anotherModuleUrl} + reference: new StaticSymbol(anotherModuleUrl, 'someExternalId', []) }; class SimpleJsImportGenerator implements ImportResolver { fileNameToModuleName(importedUrlStr: string, moduleUrlStr: string): string { return importedUrlStr; } + getImportAs(symbol: StaticSymbol): StaticSymbol { return null; } } export function main() { @@ -33,11 +35,13 @@ export function main() { // - declaring fields describe('JavaScriptEmitter', () => { + let importResolver: ImportResolver; let emitter: JavaScriptEmitter; let someVar: o.ReadVarExpr; beforeEach(() => { - emitter = new JavaScriptEmitter(new SimpleJsImportGenerator()); + importResolver = new SimpleJsImportGenerator(); + emitter = new JavaScriptEmitter(importResolver); someVar = o.variable('someVar'); }); @@ -124,6 +128,16 @@ export function main() { ].join('\n')); }); + it('should support `importAs` for external identifiers', () => { + spyOn(importResolver, 'getImportAs') + .and.returnValue(new StaticSymbol('somePackage/importAsModule', 'importAsName', [])); + expect(emitStmt(o.importExpr(externalModuleIdentifier).toStmt())).toEqual([ + `var import0 = re` + + `quire('somePackage/importAsModule');`, + `import0.importAsName;` + ].join('\n')); + }); + it('should support operators', () => { const lhs = o.variable('lhs'); const rhs = o.variable('rhs'); diff --git a/modules/@angular/compiler/test/output/output_emitter_util.ts b/modules/@angular/compiler/test/output/output_emitter_util.ts index 6dbb4d6aef..8aa99bc0ca 100644 --- a/modules/@angular/compiler/test/output/output_emitter_util.ts +++ b/modules/@angular/compiler/test/output/output_emitter_util.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ +import {StaticSymbol} from '@angular/compiler/src/aot/static_symbol'; import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata'; import {assetUrl, createIdentifier} from '@angular/compiler/src/identifiers'; import * as o from '@angular/compiler/src/output/output_ast'; @@ -262,4 +263,5 @@ export class SimpleJsImportGenerator implements ImportResolver { fileNameToModuleName(importedUrlStr: string, moduleUrlStr: string): string { return importedUrlStr; } + getImportAs(symbol: StaticSymbol): StaticSymbol { return null; } } diff --git a/modules/@angular/compiler/test/output/ts_emitter_node_only_spec.ts b/modules/@angular/compiler/test/output/ts_emitter_node_only_spec.ts index affcf18b2a..af274a9457 100644 --- a/modules/@angular/compiler/test/output/ts_emitter_node_only_spec.ts +++ b/modules/@angular/compiler/test/output/ts_emitter_node_only_spec.ts @@ -74,4 +74,5 @@ class StubReflectorHost implements StaticSymbolResolverHost { class StubImportResolver extends ImportResolver { fileNameToModuleName(importedFilePath: string, containingFilePath: string): string { return ''; } + getImportAs(symbol: StaticSymbol): StaticSymbol { return null; } } diff --git a/modules/@angular/compiler/test/output/ts_emitter_spec.ts b/modules/@angular/compiler/test/output/ts_emitter_spec.ts index 3a6d6cdf22..58506f5530 100644 --- a/modules/@angular/compiler/test/output/ts_emitter_spec.ts +++ b/modules/@angular/compiler/test/output/ts_emitter_spec.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ +import {StaticSymbol} from '@angular/compiler/src/aot/static_symbol'; import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata'; import * as o from '@angular/compiler/src/output/output_ast'; import {ImportResolver} from '@angular/compiler/src/output/path_util'; @@ -15,38 +16,42 @@ const someModuleUrl = 'somePackage/somePath'; const anotherModuleUrl = 'somePackage/someOtherPath'; const sameModuleIdentifier: CompileIdentifierMetadata = { - reference: {name: 'someLocalId', filePath: someModuleUrl} + reference: new StaticSymbol(someModuleUrl, 'someLocalId', []) }; const externalModuleIdentifier: CompileIdentifierMetadata = { - reference: {name: 'someExternalId', filePath: anotherModuleUrl} + reference: new StaticSymbol(anotherModuleUrl, 'someExternalId', []) }; class SimpleJsImportGenerator implements ImportResolver { fileNameToModuleName(importedUrlStr: string, moduleUrlStr: string): string { return importedUrlStr; } + getImportAs(symbol: StaticSymbol): StaticSymbol { return null; } } export function main() { - // Note supported features of our OutputAsti n TS: + // Not supported features of our OutputAst in TS: // - real `const` like in Dart // - final fields describe('TypeScriptEmitter', () => { + let importResolver: ImportResolver; let emitter: TypeScriptEmitter; let someVar: o.ReadVarExpr; beforeEach(() => { - emitter = new TypeScriptEmitter(new SimpleJsImportGenerator()); + importResolver = new SimpleJsImportGenerator(); + emitter = new TypeScriptEmitter(importResolver); someVar = o.variable('someVar'); }); - function emitStmt(stmt: o.Statement, exportedVars: string[] = null): string { + function emitStmt(stmt: o.Statement | o.Statement[], exportedVars: string[] = null): string { if (!exportedVars) { exportedVars = []; } - return emitter.emitStatements(someModuleUrl, [stmt], exportedVars); + const stmts = Array.isArray(stmt) ? stmt : [stmt]; + return emitter.emitStatements(someModuleUrl, stmts, exportedVars); } it('should declare variables', () => { @@ -59,6 +64,79 @@ export function main() { .toEqual(`var someVar:number = 1;`); }); + describe('declare variables with ExternExpressions as values', () => { + it('should create no reexport if the identifier is in the same module', () => { + // identifier is in the same module -> no reexport + expect(emitStmt(someVar.set(o.importExpr(sameModuleIdentifier)).toDeclStmt(), ['someVar'])) + .toEqual('export var someVar:any = someLocalId;'); + }); + + it('should create no reexport if the variable is not exported', () => { + expect(emitStmt(someVar.set(o.importExpr(externalModuleIdentifier)).toDeclStmt())).toEqual([ + `import * as import0 from 'somePackage/someOtherPath';`, + `var someVar:any = import0.someExternalId;` + ].join('\n')); + }); + + it('should create no reexport if the variable is typed', () => { + expect(emitStmt( + someVar.set(o.importExpr(externalModuleIdentifier)).toDeclStmt(o.DYNAMIC_TYPE), + ['someVar'])) + .toEqual([ + `import * as import0 from 'somePackage/someOtherPath';`, + `export var someVar:any = import0.someExternalId;` + ].join('\n')); + }); + + it('should create no reexport if the identifier has members', () => { + const externalModuleIdentifierWithMembers: CompileIdentifierMetadata = { + reference: new StaticSymbol(anotherModuleUrl, 'someExternalId', ['a']) + }; + expect(emitStmt( + someVar.set(o.importExpr(externalModuleIdentifierWithMembers)).toDeclStmt(), + ['someVar'])) + .toEqual([ + `import * as import0 from 'somePackage/someOtherPath';`, + `export var someVar:any = import0.someExternalId.a;` + ].join('\n')); + }); + + it('should create a reexport', () => { + expect( + emitStmt(someVar.set(o.importExpr(externalModuleIdentifier)).toDeclStmt(), ['someVar'])) + .toEqual([ + `export {someExternalId as someVar} from 'somePackage/someOtherPath';`, `` + ].join('\n')); + }); + + it('should create multiple reexports from the same file', () => { + const someVar2 = o.variable('someVar2'); + const externalModuleIdentifier2: CompileIdentifierMetadata = { + reference: new StaticSymbol(anotherModuleUrl, 'someExternalId2', []) + }; + expect(emitStmt( + [ + someVar.set(o.importExpr(externalModuleIdentifier)).toDeclStmt(), + someVar2.set(o.importExpr(externalModuleIdentifier2)).toDeclStmt() + ], + ['someVar', 'someVar2'])) + .toEqual([ + `export {someExternalId as someVar,someExternalId2 as someVar2} from 'somePackage/someOtherPath';`, + `` + ].join('\n')); + }); + + it('should use `importAs` for reexports', () => { + spyOn(importResolver, 'getImportAs') + .and.returnValue(new StaticSymbol('somePackage/importAsModule', 'importAsName', [])); + expect( + emitStmt(someVar.set(o.importExpr(externalModuleIdentifier)).toDeclStmt(), ['someVar'])) + .toEqual([ + `export {importAsName as someVar} from 'somePackage/importAsModule';`, `` + ].join('\n')); + }); + }); + it('should read and write variables', () => { expect(emitStmt(someVar.toStmt())).toEqual(`someVar;`); expect(emitStmt(someVar.set(o.literal(1)).toStmt())).toEqual(`someVar = 1;`); @@ -134,6 +212,14 @@ export function main() { ].join('\n')); }); + it('should support `importAs` for external identifiers', () => { + spyOn(importResolver, 'getImportAs') + .and.returnValue(new StaticSymbol('somePackage/importAsModule', 'importAsName', [])); + expect(emitStmt(o.importExpr(externalModuleIdentifier).toStmt())).toEqual([ + `import * as import0 from 'somePackage/importAsModule';`, `import0.importAsName;` + ].join('\n')); + }); + it('should support operators', () => { const lhs = o.variable('lhs'); const rhs = o.variable('rhs'); @@ -332,6 +418,16 @@ export function main() { ].join('\n')); }); + it('should support `importAs` for external types', () => { + spyOn(importResolver, 'getImportAs') + .and.returnValue(new StaticSymbol('somePackage/importAsModule', 'importAsName', [])); + const writeVarExpr = o.variable('a').set(o.NULL_EXPR); + expect(emitStmt(writeVarExpr.toDeclStmt(o.importType(externalModuleIdentifier)))).toEqual([ + `import * as import0 from 'somePackage/importAsModule';`, + `var a:import0.importAsName = (null as any);` + ].join('\n')); + }); + it('should support expression types', () => { expect(emitStmt(o.variable('a') .set(o.NULL_EXPR) diff --git a/modules/@angular/core/src/di/opaque_token.ts b/modules/@angular/core/src/di/opaque_token.ts index 2d728b13a7..40ef480138 100644 --- a/modules/@angular/core/src/di/opaque_token.ts +++ b/modules/@angular/core/src/di/opaque_token.ts @@ -6,8 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {Injectable} from './metadata'; - /** * Creates a token that can be used in a DI Provider. * @@ -30,7 +28,6 @@ import {Injectable} from './metadata'; * error messages. * @stable */ -@Injectable() // so that metadata is gathered for this class export class OpaqueToken { constructor(private _desc: string) {} diff --git a/modules/@angular/core/src/linker/component_factory.ts b/modules/@angular/core/src/linker/component_factory.ts index 03f7834577..d1b044fd58 100644 --- a/modules/@angular/core/src/linker/component_factory.ts +++ b/modules/@angular/core/src/linker/component_factory.ts @@ -95,9 +95,12 @@ const EMPTY_CONTEXT = new Object(); * @stable */ export class ComponentFactory { + /** @internal */ + _viewClass: Type>; constructor( - public selector: string, private _viewClass: Type>, - private _componentType: Type) {} + public selector: string, _viewClass: Type>, private _componentType: Type) { + this._viewClass = _viewClass; + } get componentType(): Type { return this._componentType; } diff --git a/modules/@angular/core/src/linker/view_utils.ts b/modules/@angular/core/src/linker/view_utils.ts index 1dffff2e6f..6258704396 100644 --- a/modules/@angular/core/src/linker/view_utils.ts +++ b/modules/@angular/core/src/linker/view_utils.ts @@ -14,9 +14,11 @@ import {isPresent, looseIdentical} from '../facade/lang'; import {ViewEncapsulation} from '../metadata/view'; import {RenderComponentType, RenderDebugInfo, Renderer, RootRenderer} from '../render/api'; import {Sanitizer} from '../security'; +import {Type} from '../type'; import {VERSION} from '../version'; import {NgZone} from '../zone/ng_zone'; +import {ComponentFactory} from './component_factory'; import {ExpressionChangedAfterItHasBeenCheckedError} from './errors'; import {AppView} from './view'; @@ -652,3 +654,10 @@ export class InlineArrayDynamic implements InlineArray { } export const EMPTY_INLINE_ARRAY: InlineArray = new InlineArray0(); + +/** + * This is a private API only used by the compiler to read the view class. + */ +export function getComponentFactoryViewClass(componentFactory: ComponentFactory): Type { + return componentFactory._viewClass; +} diff --git a/modules/@angular/language-service/src/typescript_host.ts b/modules/@angular/language-service/src/typescript_host.ts index 4e9c94cc02..7bc7b72165 100644 --- a/modules/@angular/language-service/src/typescript_host.ts +++ b/modules/@angular/language-service/src/typescript_host.ts @@ -122,7 +122,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost { result = this._resolver = new CompileMetadataResolver( moduleResolver, directiveResolver, pipeResolver, new SummaryResolver(), - elementSchemaRegistry, directiveNormalizer, this.reflector, + elementSchemaRegistry, directiveNormalizer, this._staticSymbolCache, this.reflector, (error, type) => this.collectError(error, type && type.filePath)); } return result; @@ -397,7 +397,8 @@ export class TypeScriptServiceHost implements LanguageServiceHost { const summaryResolver = new AotSummaryResolver( { loadSummary(filePath: string) { return null; }, - isSourceFile(sourceFilePath: string) { return true; } + isSourceFile(sourceFilePath: string) { return true; }, + getOutputFileName(sourceFilePath: string) { return null; } }, this._staticSymbolCache); result = this._staticSymbolResolver = new StaticSymbolResolver(