From 76f53f929cbbd3b4ff9bdc2c436ed8eece76b2aa Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Wed, 23 Nov 2016 12:09:09 -0800 Subject: [PATCH] Revert "refactor(compiler): move `findDeclaration` into the `StaticReflector`" This reverts commit e7025c9423cd19c7008a85ab06019750ae9ef992. --- modules/@angular/compiler-cli/index.ts | 4 +- modules/@angular/compiler-cli/src/codegen.ts | 38 +- .../@angular/compiler-cli/src/extractor.ts | 14 +- ..._host.ts => path_mapped_reflector_host.ts} | 19 +- .../compiler-cli/src/private_import_core.ts | 6 + .../src/{ng_host.ts => reflector_host.ts} | 158 ++++++++- modules/@angular/compiler-cli/test/mocks.ts | 4 +- .../compiler-cli/test/ng_host_spec.ts | 204 ----------- .../compiler-cli/test/reflector_host_spec.ts | 329 ++++++++++++++++++ modules/@angular/compiler/index.ts | 1 - .../compiler/src/aot/compiler_host.ts | 31 -- .../compiler/src/aot/static_reflector.ts | 248 +++++-------- .../test/aot/static_reflector_spec.ts | 260 ++++++-------- 13 files changed, 704 insertions(+), 612 deletions(-) rename modules/@angular/compiler-cli/src/{path_mapped_ng_host.ts => path_mapped_reflector_host.ts} (87%) rename modules/@angular/compiler-cli/src/{ng_host.ts => reflector_host.ts} (58%) delete mode 100644 modules/@angular/compiler-cli/test/ng_host_spec.ts create mode 100644 modules/@angular/compiler-cli/test/reflector_host_spec.ts delete mode 100644 modules/@angular/compiler/src/aot/compiler_host.ts diff --git a/modules/@angular/compiler-cli/index.ts b/modules/@angular/compiler-cli/index.ts index 590350abcf..19b10e6cfc 100644 --- a/modules/@angular/compiler-cli/index.ts +++ b/modules/@angular/compiler-cli/index.ts @@ -6,9 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -export {AotCompilerHost, AotCompilerHost as StaticReflectorHost, StaticReflector, StaticSymbol} from '@angular/compiler'; +export {StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler'; export {CodeGenerator} from './src/codegen'; export {Extractor} from './src/extractor'; -export {NgHost, NgHostContext, NodeNgHostContext} from './src/ng_host'; +export {NodeReflectorHostContext, ReflectorHost, ReflectorHostContext} from './src/reflector_host'; export * from '@angular/tsc-wrapped'; diff --git a/modules/@angular/compiler-cli/src/codegen.ts b/modules/@angular/compiler-cli/src/codegen.ts index 03255bd92a..5c64c1955e 100644 --- a/modules/@angular/compiler-cli/src/codegen.ts +++ b/modules/@angular/compiler-cli/src/codegen.ts @@ -17,9 +17,9 @@ import {readFileSync} from 'fs'; import * as path from 'path'; import * as ts from 'typescript'; -import {NgHost, NgHostContext} from './ng_host'; -import {PathMappedNgHost} from './path_mapped_ng_host'; +import {PathMappedReflectorHost} from './path_mapped_reflector_host'; import {Console} from './private_import_core'; +import {ReflectorHost, ReflectorHostContext} from './reflector_host'; const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/; const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/; @@ -37,7 +37,8 @@ export class CodeGenerator { constructor( private options: AngularCompilerOptions, private program: ts.Program, public host: ts.CompilerHost, private staticReflector: compiler.StaticReflector, - private compiler: compiler.AotCompiler, private ngHost: NgHost) {} + private compiler: compiler.AotCompiler, private reflectorHost: compiler.StaticReflectorHost) { + } // Write codegen in a directory structure matching the sources. private calculateEmitPath(filePath: string): string { @@ -64,7 +65,7 @@ export class CodeGenerator { codegen(options: {transitiveModules: boolean}): Promise { const staticSymbols = - extractProgramSymbols(this.program, this.staticReflector, this.ngHost, this.options); + extractProgramSymbols(this.program, this.staticReflector, this.reflectorHost, this.options); return this.compiler.compileModules(staticSymbols, options).then(generatedModules => { generatedModules.forEach(generatedModule => { @@ -78,8 +79,8 @@ export class CodeGenerator { static create( options: AngularCompilerOptions, cliOptions: NgcCliOptions, program: ts.Program, - compilerHost: ts.CompilerHost, ngHostContext?: NgHostContext, - resourceLoader?: compiler.ResourceLoader, ngHost?: NgHost): CodeGenerator { + compilerHost: ts.CompilerHost, reflectorHostContext?: ReflectorHostContext, + resourceLoader?: compiler.ResourceLoader, reflectorHost?: ReflectorHost): CodeGenerator { resourceLoader = resourceLoader || { get: (s: string) => { if (!compilerHost.fileExists(s)) { @@ -101,13 +102,13 @@ export class CodeGenerator { } const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver(); - if (!ngHost) { + if (!reflectorHost) { const usePathMapping = !!options.rootDirs && options.rootDirs.length > 0; - ngHost = usePathMapping ? - new PathMappedNgHost(program, compilerHost, options, ngHostContext) : - new NgHost(program, compilerHost, options, ngHostContext); + reflectorHost = usePathMapping ? + new PathMappedReflectorHost(program, compilerHost, options, reflectorHostContext) : + new ReflectorHost(program, compilerHost, options, reflectorHostContext); } - const staticReflector = new compiler.StaticReflector(ngHost); + const staticReflector = new compiler.StaticReflector(reflectorHost); compiler.StaticAndDynamicReflectionCapabilities.install(staticReflector); const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser(), transContent, cliOptions.i18nFormat); @@ -134,15 +135,18 @@ export class CodeGenerator { new compiler.ViewCompiler(config, elementSchemaRegistry), new compiler.DirectiveWrapperCompiler( config, expressionParser, elementSchemaRegistry, console), - new compiler.NgModuleCompiler(), new compiler.TypeScriptEmitter(ngHost), cliOptions.locale, - cliOptions.i18nFormat, new compiler.AnimationParser(elementSchemaRegistry)); + new compiler.NgModuleCompiler(), new compiler.TypeScriptEmitter(reflectorHost), + cliOptions.locale, cliOptions.i18nFormat, + new compiler.AnimationParser(elementSchemaRegistry)); - return new CodeGenerator(options, program, compilerHost, staticReflector, aotCompiler, ngHost); + return new CodeGenerator( + options, program, compilerHost, staticReflector, aotCompiler, reflectorHost); } } export function extractProgramSymbols( - program: ts.Program, staticReflector: compiler.StaticReflector, ngHost: NgHost, + program: ts.Program, staticReflector: compiler.StaticReflector, + reflectorHost: compiler.StaticReflectorHost, options: AngularCompilerOptions): compiler.StaticSymbol[] { // Compare with false since the default should be true const skipFileNames = @@ -153,7 +157,7 @@ export function extractProgramSymbols( program.getSourceFiles() .filter(sourceFile => !skipFileNames.test(sourceFile.fileName)) .forEach(sourceFile => { - const absSrcPath = ngHost.getCanonicalFileName(sourceFile.fileName); + const absSrcPath = reflectorHost.getCanonicalFileName(sourceFile.fileName); const moduleMetadata = staticReflector.getModuleMetadata(absSrcPath); if (!moduleMetadata) { @@ -172,7 +176,7 @@ export function extractProgramSymbols( // Ignore symbols that are only included to record error information. continue; } - staticSymbols.push(staticReflector.findDeclaration(absSrcPath, symbol, absSrcPath)); + staticSymbols.push(reflectorHost.findDeclaration(absSrcPath, symbol, absSrcPath)); } }); diff --git a/modules/@angular/compiler-cli/src/extractor.ts b/modules/@angular/compiler-cli/src/extractor.ts index a16a388bad..c956b60231 100644 --- a/modules/@angular/compiler-cli/src/extractor.ts +++ b/modules/@angular/compiler-cli/src/extractor.ts @@ -19,18 +19,18 @@ import * as tsc from '@angular/tsc-wrapped'; import * as ts from 'typescript'; import {extractProgramSymbols} from './codegen'; -import {NgHost} from './ng_host'; +import {ReflectorHost} from './reflector_host'; export class Extractor { constructor( private options: tsc.AngularCompilerOptions, private program: ts.Program, public host: ts.CompilerHost, private staticReflector: compiler.StaticReflector, - private messageBundle: compiler.MessageBundle, private ngHost: NgHost, + private messageBundle: compiler.MessageBundle, private reflectorHost: ReflectorHost, private metadataResolver: compiler.CompileMetadataResolver) {} extract(): Promise { const programSymbols: compiler.StaticSymbol[] = - extractProgramSymbols(this.program, this.staticReflector, this.ngHost, this.options); + extractProgramSymbols(this.program, this.staticReflector, this.reflectorHost, this.options); const {ngModules, files} = compiler.analyzeAndValidateNgModules( programSymbols, {transitiveModules: true}, this.metadataResolver); @@ -65,12 +65,12 @@ export class Extractor { static create( options: tsc.AngularCompilerOptions, translationsFormat: string, program: ts.Program, compilerHost: ts.CompilerHost, resourceLoader: compiler.ResourceLoader, - ngHost?: NgHost): Extractor { + reflectorHost?: ReflectorHost): Extractor { const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser()); const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver(); - if (!ngHost) ngHost = new NgHost(program, compilerHost, options); - const staticReflector = new compiler.StaticReflector(ngHost); + if (!reflectorHost) reflectorHost = new ReflectorHost(program, compilerHost, options); + const staticReflector = new compiler.StaticReflector(reflectorHost); compiler.StaticAndDynamicReflectionCapabilities.install(staticReflector); const config = new compiler.CompilerConfig({ @@ -92,6 +92,6 @@ export class Extractor { const messageBundle = new compiler.MessageBundle(htmlParser, [], {}); return new Extractor( - options, program, compilerHost, staticReflector, messageBundle, ngHost, resolver); + options, program, compilerHost, staticReflector, messageBundle, reflectorHost, resolver); } } \ No newline at end of file diff --git a/modules/@angular/compiler-cli/src/path_mapped_ng_host.ts b/modules/@angular/compiler-cli/src/path_mapped_reflector_host.ts similarity index 87% rename from modules/@angular/compiler-cli/src/path_mapped_ng_host.ts rename to modules/@angular/compiler-cli/src/path_mapped_reflector_host.ts index 863952cf7d..e3d1e26ec4 100644 --- a/modules/@angular/compiler-cli/src/path_mapped_ng_host.ts +++ b/modules/@angular/compiler-cli/src/path_mapped_reflector_host.ts @@ -12,22 +12,22 @@ import * as fs from 'fs'; import * as path from 'path'; import * as ts from 'typescript'; -import {NgHost, NgHostContext} from './ng_host'; +import {ReflectorHost, ReflectorHostContext} from './reflector_host'; const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/; const DTS = /\.d\.ts$/; /** - * This version of the AotCompilerHost expects that the program will be compiled + * This version of the reflector host expects that the program will be compiled * and executed with a "path mapped" directory structure, where generated files * are in a parallel tree with the sources, and imported using a `./` relative * import. This requires using TS `rootDirs` option and also teaching the module * loader what to do. */ -export class PathMappedNgHost extends NgHost { +export class PathMappedReflectorHost extends ReflectorHost { constructor( program: ts.Program, compilerHost: ts.CompilerHost, options: AngularCompilerOptions, - context?: NgHostContext) { + context?: ReflectorHostContext) { super(program, compilerHost, options, context); } @@ -42,14 +42,7 @@ export class PathMappedNgHost extends NgHost { return fileName; } - resolveImportToFile(m: string, containingFile: string) { - if (!containingFile || !containingFile.length) { - if (m.indexOf('.') === 0) { - throw new Error('Resolution of relative paths requires a containing file.'); - } - // Any containing file gives the same result for absolute imports - containingFile = path.join(this.basePath, 'index.ts'); - } + protected resolve(m: string, containingFile: string) { for (const root of this.options.rootDirs || ['']) { const rootedContainingFile = path.join(root, containingFile); const resolved = @@ -89,7 +82,7 @@ export class PathMappedNgHost extends NgHost { } const resolvable = (candidate: string) => { - const resolved = this.getCanonicalFileName(this.resolveImportToFile(candidate, importedFile)); + const resolved = this.getCanonicalFileName(this.resolve(candidate, importedFile)); return resolved && resolved.replace(EXT, '') === importedFile.replace(EXT, ''); }; diff --git a/modules/@angular/compiler-cli/src/private_import_core.ts b/modules/@angular/compiler-cli/src/private_import_core.ts index 3cd53cdaf8..5030d3d23f 100644 --- a/modules/@angular/compiler-cli/src/private_import_core.ts +++ b/modules/@angular/compiler-cli/src/private_import_core.ts @@ -16,3 +16,9 @@ export var ReflectionCapabilities: typeof r.ReflectionCapabilities = r.Reflectio export type Console = typeof r._Console; export var Console: typeof r.Console = r.Console; + +export var reflector: typeof r.reflector = r.reflector; + +export type SetterFn = typeof r._SetterFn; +export type GetterFn = typeof r._GetterFn; +export type MethodFn = typeof r._MethodFn; diff --git a/modules/@angular/compiler-cli/src/ng_host.ts b/modules/@angular/compiler-cli/src/reflector_host.ts similarity index 58% rename from modules/@angular/compiler-cli/src/ng_host.ts rename to modules/@angular/compiler-cli/src/reflector_host.ts index 50c437bb04..7c0ee729be 100644 --- a/modules/@angular/compiler-cli/src/ng_host.ts +++ b/modules/@angular/compiler-cli/src/reflector_host.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AotCompilerHost, AssetUrl, StaticSymbol} from '@angular/compiler'; +import {AssetUrl, ImportGenerator, StaticReflectorHost, StaticSymbol} from '@angular/compiler'; import {AngularCompilerOptions, MetadataCollector, ModuleMetadata} from '@angular/tsc-wrapped'; import * as fs from 'fs'; import * as path from 'path'; @@ -17,42 +17,46 @@ const DTS = /\.d\.ts$/; const NODE_MODULES = '/node_modules/'; const IS_GENERATED = /\.(ngfactory|css(\.shim)?)$/; -export interface NgHostContext { +export interface ReflectorHostContext { fileExists(fileName: string): boolean; directoryExists(directoryName: string): boolean; readFile(fileName: string): string; assumeFileExists(fileName: string): void; } -export class NgHost implements AotCompilerHost { +export class ReflectorHost implements StaticReflectorHost, ImportGenerator { protected metadataCollector = new MetadataCollector(); - protected context: NgHostContext; + protected context: ReflectorHostContext; private isGenDirChildOfRootDir: boolean; protected basePath: string; private genDir: string; constructor( protected program: ts.Program, protected compilerHost: ts.CompilerHost, - protected options: AngularCompilerOptions, context?: NgHostContext) { + protected options: AngularCompilerOptions, context?: ReflectorHostContext) { // normalize the path so that it never ends with '/'. this.basePath = path.normalize(path.join(this.options.basePath, '.')).replace(/\\/g, '/'); this.genDir = path.normalize(path.join(this.options.genDir, '.')).replace(/\\/g, '/'); - this.context = context || new NodeNgHostContext(compilerHost); + this.context = context || new NodeReflectorHostContext(compilerHost); const genPath: string = path.relative(this.basePath, this.genDir); this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..'); } + angularImportLocations() { + return { + coreDecorators: '@angular/core/src/metadata', + diDecorators: '@angular/core/src/di/metadata', + diMetadata: '@angular/core/src/di/metadata', + diOpaqueToken: '@angular/core/src/di/opaque_token', + animationMetadata: '@angular/core/src/animation/metadata', + provider: '@angular/core/src/di/provider' + }; + } + // We use absolute paths on disk as canonical. getCanonicalFileName(fileName: string): string { return fileName; } - resolveImportToFile(m: string, containingFile: string) { - if (!containingFile || !containingFile.length) { - if (m.indexOf('.') === 0) { - throw new Error('Resolution of relative paths requires a containing file.'); - } - // Any containing file gives the same result for absolute imports - containingFile = path.join(this.basePath, 'index.ts'); - } + protected resolve(m: string, containingFile: string) { m = m.replace(EXT, ''); const resolved = ts.resolveModuleName(m, containingFile.replace(/\\/g, '/'), this.options, this.context) @@ -69,7 +73,7 @@ export class NgHost implements AotCompilerHost { protected resolveAssetUrl(url: string, containingFile: string): string { const assetUrl = this.normalizeAssetUrl(url); if (assetUrl) { - return this.getCanonicalFileName(this.resolveImportToFile(assetUrl, containingFile)); + return this.getCanonicalFileName(this.resolve(assetUrl, containingFile)); } return url; } @@ -154,8 +158,80 @@ export class NgHost implements AotCompilerHost { } } + findDeclaration( + module: string, symbolName: string, containingFile: string, + containingModule?: string): StaticSymbol { + if (!containingFile || !containingFile.length) { + if (module.indexOf('.') === 0) { + throw new Error('Resolution of relative paths requires a containing file.'); + } + // Any containing file gives the same result for absolute imports + containingFile = path.join(this.basePath, 'index.ts'); + } + + try { + const assetUrl = this.normalizeAssetUrl(module); + if (assetUrl) { + module = assetUrl; + } + const filePath = this.resolve(module, containingFile); + + if (!filePath) { + // If the file cannot be found the module is probably referencing a declared module + // for which there is no disambiguating file and we also don't need to track + // re-exports. Just use the module name. + return this.getStaticSymbol(module, symbolName); + } + + const tc = this.program.getTypeChecker(); + const sf = this.program.getSourceFile(filePath); + if (!sf || !(sf).symbol) { + // The source file was not needed in the compile but we do need the values from + // the corresponding .ts files stored in the .metadata.json file. Check the file + // for exports to see if the file is exported. + return this.resolveExportedSymbol(filePath, symbolName) || + this.getStaticSymbol(filePath, symbolName); + } + + let symbol = tc.getExportsOfModule((sf).symbol).find(m => m.name === symbolName); + if (!symbol) { + throw new Error(`can't find symbol ${symbolName} exported from module ${filePath}`); + } + if (symbol && + symbol.flags & ts.SymbolFlags.Alias) { // This is an alias, follow what it aliases + symbol = tc.getAliasedSymbol(symbol); + } + const declaration = symbol.getDeclarations()[0]; + const declarationFile = this.getCanonicalFileName(declaration.getSourceFile().fileName); + + return this.getStaticSymbol(declarationFile, symbol.getName()); + } catch (e) { + console.error(`can't resolve module ${module} from ${containingFile}`); + throw e; + } + } + + private typeCache = new Map(); private resolverCache = new Map(); + /** + * getStaticSymbol produces a Type whose metadata is known but whose implementation is not loaded. + * All types passed to the StaticResolver should be pseudo-types returned by this method. + * + * @param declarationFile the absolute path of the file where the symbol is declared + * @param name the name of the type. + */ + getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol { + const memberSuffix = members ? `.${ members.join('.')}` : ''; + const key = `"${declarationFile}".${name}${memberSuffix}`; + let result = this.typeCache.get(key); + if (!result) { + result = new StaticSymbol(declarationFile, name, members); + this.typeCache.set(key, result); + } + return result; + } + getMetadataFor(filePath: string): ModuleMetadata { if (!this.context.fileExists(filePath)) { // If the file doesn't exists then we cannot return metadata for the file. @@ -201,9 +277,59 @@ export class NgHost implements AotCompilerHost { } return metadata; } + + protected resolveExportedSymbol(filePath: string, symbolName: string): StaticSymbol { + const resolveModule = (moduleName: string): string => { + const resolvedModulePath = this.getCanonicalFileName(this.resolve(moduleName, filePath)); + if (!resolvedModulePath) { + throw new Error(`Could not resolve module '${moduleName}' relative to file ${filePath}`); + } + return resolvedModulePath; + }; + const metadata = this.getResolverMetadata(filePath); + if (metadata) { + // If we have metadata for the symbol, this is the original exporting location. + if (metadata.metadata[symbolName]) { + return this.getStaticSymbol(filePath, symbolName); + } + + // If no, try to find the symbol in one of the re-export location + if (metadata.exports) { + // Try and find the symbol in the list of explicitly re-exported symbols. + for (const moduleExport of metadata.exports) { + if (moduleExport.export) { + const exportSymbol = moduleExport.export.find(symbol => { + if (typeof symbol === 'string') { + return symbol == symbolName; + } else { + return symbol.as == symbolName; + } + }); + if (exportSymbol) { + let symName = symbolName; + if (typeof exportSymbol !== 'string') { + symName = exportSymbol.name; + } + return this.resolveExportedSymbol(resolveModule(moduleExport.from), symName); + } + } + } + + // Try to find the symbol via export * directives. + for (const moduleExport of metadata.exports) { + if (!moduleExport.export) { + const resolvedModule = resolveModule(moduleExport.from); + const candidateSymbol = this.resolveExportedSymbol(resolvedModule, symbolName); + if (candidateSymbol) return candidateSymbol; + } + } + } + } + return null; + } } -export class NodeNgHostContext implements NgHostContext { +export class NodeReflectorHostContext implements ReflectorHostContext { constructor(private host: ts.CompilerHost) {} private assumedExists: {[fileName: string]: boolean} = {}; diff --git a/modules/@angular/compiler-cli/test/mocks.ts b/modules/@angular/compiler-cli/test/mocks.ts index fddc69c866..7573f7fce5 100644 --- a/modules/@angular/compiler-cli/test/mocks.ts +++ b/modules/@angular/compiler-cli/test/mocks.ts @@ -6,14 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import {NgHostContext} from '@angular/compiler-cli/src/ng_host'; +import {ReflectorHostContext} from '@angular/compiler-cli/src/reflector_host'; import * as ts from 'typescript'; export type Entry = string | Directory; export interface Directory { [name: string]: Entry; } -export class MockContext implements NgHostContext { +export class MockContext implements ReflectorHostContext { constructor(public currentDirectory: string, private files: Entry) {} fileExists(fileName: string): boolean { return typeof this.getEntry(fileName) === 'string'; } diff --git a/modules/@angular/compiler-cli/test/ng_host_spec.ts b/modules/@angular/compiler-cli/test/ng_host_spec.ts deleted file mode 100644 index e2776b5cf8..0000000000 --- a/modules/@angular/compiler-cli/test/ng_host_spec.ts +++ /dev/null @@ -1,204 +0,0 @@ -/** - * @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 - */ - -import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; -import * as ts from 'typescript'; - -import {NgHost} from '../src/ng_host'; - -import {Directory, Entry, MockCompilerHost, MockContext} from './mocks'; - -describe('NgHost', () => { - let context: MockContext; - let host: ts.CompilerHost; - let program: ts.Program; - let hostNestedGenDir: NgHost; - let hostSiblingGenDir: NgHost; - - beforeEach(() => { - context = new MockContext('/tmp/src', clone(FILES)); - host = new MockCompilerHost(context); - program = ts.createProgram( - ['main.ts'], { - module: ts.ModuleKind.CommonJS, - }, - host); - // Force a typecheck - const errors = program.getSemanticDiagnostics(); - if (errors && errors.length) { - throw new Error('Expected no errors'); - } - hostNestedGenDir = new NgHost( - program, host, { - genDir: '/tmp/project/src/gen/', - basePath: '/tmp/project/src', - skipMetadataEmit: false, - strictMetadataEmit: false, - skipTemplateCodegen: false, - trace: false - }, - context); - hostSiblingGenDir = new NgHost( - program, host, { - genDir: '/tmp/project/gen', - basePath: '/tmp/project/src/', - skipMetadataEmit: false, - strictMetadataEmit: false, - skipTemplateCodegen: false, - trace: false - }, - context); - }); - - describe('nestedGenDir', () => { - it('should import node_module from factory', () => { - expect(hostNestedGenDir.getImportPath( - '/tmp/project/src/gen/my.ngfactory.ts', - '/tmp/project/node_modules/@angular/core.d.ts')) - .toEqual('@angular/core'); - }); - - it('should import factory from factory', () => { - expect(hostNestedGenDir.getImportPath( - '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/my.other.ngfactory.ts')) - .toEqual('./my.other.ngfactory'); - expect(hostNestedGenDir.getImportPath( - '/tmp/project/src/a/my.ngfactory.ts', '/tmp/project/src/my.other.css.ts')) - .toEqual('../my.other.css'); - expect(hostNestedGenDir.getImportPath( - '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/a/my.other.css.shim.ts')) - .toEqual('./a/my.other.css.shim'); - }); - - it('should import application from factory', () => { - expect(hostNestedGenDir.getImportPath( - '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/my.other.ts')) - .toEqual('../my.other'); - expect(hostNestedGenDir.getImportPath( - '/tmp/project/src/a/my.ngfactory.ts', '/tmp/project/src/my.other.ts')) - .toEqual('../../my.other'); - expect(hostNestedGenDir.getImportPath( - '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/a/my.other.ts')) - .toEqual('../a/my.other'); - }); - }); - - describe('nestedGenDir', () => { - it('should import node_module from factory', () => { - expect(hostSiblingGenDir.getImportPath( - '/tmp/project/src/gen/my.ngfactory.ts', - '/tmp/project/node_modules/@angular/core.d.ts')) - .toEqual('@angular/core'); - }); - - it('should import factory from factory', () => { - expect(hostSiblingGenDir.getImportPath( - '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/my.other.ngfactory.ts')) - .toEqual('./my.other.ngfactory'); - expect(hostSiblingGenDir.getImportPath( - '/tmp/project/src/a/my.ngfactory.ts', '/tmp/project/src/my.other.css.ts')) - .toEqual('../my.other.css'); - expect(hostSiblingGenDir.getImportPath( - '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/a/my.other.css.shim.ts')) - .toEqual('./a/my.other.css.shim'); - }); - - it('should import application from factory', () => { - expect(hostSiblingGenDir.getImportPath( - '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/my.other.ts')) - .toEqual('./my.other'); - expect(hostSiblingGenDir.getImportPath( - '/tmp/project/src/a/my.ngfactory.ts', '/tmp/project/src/my.other.ts')) - .toEqual('../my.other'); - expect(hostSiblingGenDir.getImportPath( - '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/a/my.other.ts')) - .toEqual('./a/my.other'); - }); - }); - - it('should be able to produce an import from main @angular/core', () => { - expect(hostNestedGenDir.getImportPath( - '/tmp/project/src/main.ts', '/tmp/project/node_modules/@angular/core.d.ts')) - .toEqual('@angular/core'); - }); - - it('should be able to produce an import from main to a sub-directory', () => { - expect(hostNestedGenDir.getImportPath('main.ts', 'lib/utils.ts')).toEqual('./lib/utils'); - }); - - it('should be able to produce an import from to a peer file', () => { - expect(hostNestedGenDir.getImportPath('lib/utils.ts', 'lib/collections.ts')) - .toEqual('./collections'); - }); - - it('should be able to produce an import from to a sibling directory', () => { - expect(hostNestedGenDir.getImportPath('lib2/utils2.ts', 'lib/utils.ts')) - .toEqual('../lib/utils'); - }); - - it('should be able to read a metadata file', () => { - expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/core.d.ts')) - .toEqual({__symbolic: 'module', version: 1, metadata: {foo: {__symbolic: 'class'}}}); - }); - - it('should be able to read metadata from an otherwise unused .d.ts file ', () => { - expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/unused.d.ts')).toBeUndefined(); - }); - - it('should be able to read empty metadata ', () => { - expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/empty.d.ts')).toBeUndefined(); - }); - - it('should return undefined for missing modules', () => { - expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/missing.d.ts')).toBeUndefined(); - }); -}); - -const dummyModule = 'export let foo: any[];'; - -const FILES: Entry = { - 'tmp': { - 'src': { - 'main.ts': ` - import * as c from '@angular/core'; - import * as r from '@angular/router'; - import * as u from './lib/utils'; - import * as cs from './lib/collections'; - import * as u2 from './lib2/utils2'; - `, - 'lib': { - 'utils.ts': dummyModule, - 'collections.ts': dummyModule, - }, - 'lib2': {'utils2.ts': dummyModule}, - 'node_modules': { - '@angular': { - 'core.d.ts': dummyModule, - 'core.metadata.json': - `{"__symbolic":"module", "version": 1, "metadata": {"foo": {"__symbolic": "class"}}}`, - 'router': {'index.d.ts': dummyModule, 'src': {'providers.d.ts': dummyModule}}, - 'unused.d.ts': dummyModule, - 'empty.d.ts': 'export declare var a: string;', - 'empty.metadata.json': '[]', - } - } - } - } -}; - -function clone(entry: Entry): Entry { - if (typeof entry === 'string') { - return entry; - } else { - const result: Directory = {}; - for (const name in entry) { - result[name] = clone(entry[name]); - } - return result; - } -} diff --git a/modules/@angular/compiler-cli/test/reflector_host_spec.ts b/modules/@angular/compiler-cli/test/reflector_host_spec.ts new file mode 100644 index 0000000000..d47b89d05f --- /dev/null +++ b/modules/@angular/compiler-cli/test/reflector_host_spec.ts @@ -0,0 +1,329 @@ +/** + * @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 + */ + +import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; +import * as ts from 'typescript'; + +import {ReflectorHost} from '../src/reflector_host'; + +import {Directory, Entry, MockCompilerHost, MockContext} from './mocks'; + +describe('reflector_host', () => { + let context: MockContext; + let host: ts.CompilerHost; + let program: ts.Program; + let reflectorNestedGenDir: ReflectorHost; + let reflectorSiblingGenDir: ReflectorHost; + + beforeEach(() => { + context = new MockContext('/tmp/src', clone(FILES)); + host = new MockCompilerHost(context); + program = ts.createProgram( + ['main.ts'], { + module: ts.ModuleKind.CommonJS, + }, + host); + // Force a typecheck + const errors = program.getSemanticDiagnostics(); + if (errors && errors.length) { + throw new Error('Expected no errors'); + } + reflectorNestedGenDir = new ReflectorHost( + program, host, { + genDir: '/tmp/project/src/gen/', + basePath: '/tmp/project/src', + skipMetadataEmit: false, + strictMetadataEmit: false, + skipTemplateCodegen: false, + trace: false + }, + context); + reflectorSiblingGenDir = new ReflectorHost( + program, host, { + genDir: '/tmp/project/gen', + basePath: '/tmp/project/src/', + skipMetadataEmit: false, + strictMetadataEmit: false, + skipTemplateCodegen: false, + trace: false + }, + context); + }); + + describe('nestedGenDir', () => { + it('should import node_module from factory', () => { + expect(reflectorNestedGenDir.getImportPath( + '/tmp/project/src/gen/my.ngfactory.ts', + '/tmp/project/node_modules/@angular/core.d.ts')) + .toEqual('@angular/core'); + }); + + it('should import factory from factory', () => { + expect(reflectorNestedGenDir.getImportPath( + '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/my.other.ngfactory.ts')) + .toEqual('./my.other.ngfactory'); + expect(reflectorNestedGenDir.getImportPath( + '/tmp/project/src/a/my.ngfactory.ts', '/tmp/project/src/my.other.css.ts')) + .toEqual('../my.other.css'); + expect(reflectorNestedGenDir.getImportPath( + '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/a/my.other.css.shim.ts')) + .toEqual('./a/my.other.css.shim'); + }); + + it('should import application from factory', () => { + expect(reflectorNestedGenDir.getImportPath( + '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/my.other.ts')) + .toEqual('../my.other'); + expect(reflectorNestedGenDir.getImportPath( + '/tmp/project/src/a/my.ngfactory.ts', '/tmp/project/src/my.other.ts')) + .toEqual('../../my.other'); + expect(reflectorNestedGenDir.getImportPath( + '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/a/my.other.ts')) + .toEqual('../a/my.other'); + }); + }); + + describe('nestedGenDir', () => { + it('should import node_module from factory', () => { + expect(reflectorSiblingGenDir.getImportPath( + '/tmp/project/src/gen/my.ngfactory.ts', + '/tmp/project/node_modules/@angular/core.d.ts')) + .toEqual('@angular/core'); + }); + + it('should import factory from factory', () => { + expect(reflectorSiblingGenDir.getImportPath( + '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/my.other.ngfactory.ts')) + .toEqual('./my.other.ngfactory'); + expect(reflectorSiblingGenDir.getImportPath( + '/tmp/project/src/a/my.ngfactory.ts', '/tmp/project/src/my.other.css.ts')) + .toEqual('../my.other.css'); + expect(reflectorSiblingGenDir.getImportPath( + '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/a/my.other.css.shim.ts')) + .toEqual('./a/my.other.css.shim'); + }); + + it('should import application from factory', () => { + expect(reflectorSiblingGenDir.getImportPath( + '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/my.other.ts')) + .toEqual('./my.other'); + expect(reflectorSiblingGenDir.getImportPath( + '/tmp/project/src/a/my.ngfactory.ts', '/tmp/project/src/my.other.ts')) + .toEqual('../my.other'); + expect(reflectorSiblingGenDir.getImportPath( + '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/a/my.other.ts')) + .toEqual('./a/my.other'); + }); + }); + + it('should provide the import locations for angular', () => { + const {coreDecorators, diDecorators, diMetadata, animationMetadata, provider} = + reflectorNestedGenDir.angularImportLocations(); + expect(coreDecorators).toEqual('@angular/core/src/metadata'); + expect(diDecorators).toEqual('@angular/core/src/di/metadata'); + expect(diMetadata).toEqual('@angular/core/src/di/metadata'); + expect(animationMetadata).toEqual('@angular/core/src/animation/metadata'); + expect(provider).toEqual('@angular/core/src/di/provider'); + }); + + it('should be able to produce an import from main @angular/core', () => { + expect(reflectorNestedGenDir.getImportPath( + '/tmp/project/src/main.ts', '/tmp/project/node_modules/@angular/core.d.ts')) + .toEqual('@angular/core'); + }); + + it('should be able to produce an import from main to a sub-directory', () => { + expect(reflectorNestedGenDir.getImportPath('main.ts', 'lib/utils.ts')).toEqual('./lib/utils'); + }); + + it('should be able to produce an import from to a peer file', () => { + expect(reflectorNestedGenDir.getImportPath('lib/utils.ts', 'lib/collections.ts')) + .toEqual('./collections'); + }); + + it('should be able to produce an import from to a sibling directory', () => { + expect(reflectorNestedGenDir.getImportPath('lib2/utils2.ts', 'lib/utils.ts')) + .toEqual('../lib/utils'); + }); + + it('should be able to produce a symbol for an exported symbol', () => { + expect(reflectorNestedGenDir.findDeclaration('@angular/router', 'foo', 'main.ts')) + .toBeDefined(); + }); + + it('should be able to produce a symbol for values space only reference', () => { + expect(reflectorNestedGenDir.findDeclaration('@angular/router/src/providers', 'foo', 'main.ts')) + .toBeDefined(); + }); + + + it('should be produce the same symbol if asked twice', () => { + const foo1 = reflectorNestedGenDir.getStaticSymbol('main.ts', 'foo'); + const foo2 = reflectorNestedGenDir.getStaticSymbol('main.ts', 'foo'); + expect(foo1).toBe(foo2); + }); + + it('should be able to produce a symbol for a module with no file', () => { + expect(reflectorNestedGenDir.getStaticSymbol('angularjs', 'SomeAngularSymbol')).toBeDefined(); + }); + + it('should be able to read a metadata file', () => { + expect(reflectorNestedGenDir.getMetadataFor('node_modules/@angular/core.d.ts')) + .toEqual({__symbolic: 'module', version: 1, metadata: {foo: {__symbolic: 'class'}}}); + }); + + it('should be able to read metadata from an otherwise unused .d.ts file ', () => { + expect(reflectorNestedGenDir.getMetadataFor('node_modules/@angular/unused.d.ts')) + .toBeUndefined(); + }); + + it('should be able to read empty metadata ', () => { + expect(reflectorNestedGenDir.getMetadataFor('node_modules/@angular/empty.d.ts')) + .toBeUndefined(); + }); + + it('should return undefined for missing modules', () => { + expect(reflectorNestedGenDir.getMetadataFor('node_modules/@angular/missing.d.ts')) + .toBeUndefined(); + }); + + it('should be able to trace a named export', () => { + const symbol = reflectorNestedGenDir.findDeclaration( + './reexport/reexport.d.ts', 'One', '/tmp/src/main.ts'); + expect(symbol.name).toEqual('One'); + expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts'); + }); + + it('should be able to trace a renamed export', () => { + const symbol = reflectorNestedGenDir.findDeclaration( + './reexport/reexport.d.ts', 'Four', '/tmp/src/main.ts'); + expect(symbol.name).toEqual('Three'); + expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts'); + }); + + it('should be able to trace an export * export', () => { + const symbol = reflectorNestedGenDir.findDeclaration( + './reexport/reexport.d.ts', 'Five', '/tmp/src/main.ts'); + expect(symbol.name).toEqual('Five'); + expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin5.d.ts'); + }); + + it('should be able to trace a multi-level re-export', () => { + const symbol = reflectorNestedGenDir.findDeclaration( + './reexport/reexport.d.ts', 'Thirty', '/tmp/src/main.ts'); + expect(symbol.name).toEqual('Thirty'); + expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin30.d.ts'); + }); +}); + +const dummyModule = 'export let foo: any[];'; + +const FILES: Entry = { + 'tmp': { + 'src': { + 'main.ts': ` + import * as c from '@angular/core'; + import * as r from '@angular/router'; + import * as u from './lib/utils'; + import * as cs from './lib/collections'; + import * as u2 from './lib2/utils2'; + `, + 'lib': { + 'utils.ts': dummyModule, + 'collections.ts': dummyModule, + }, + 'lib2': {'utils2.ts': dummyModule}, + 'reexport': { + 'reexport.d.ts': ` + import * as c from '@angular/core'; + `, + 'reexport.metadata.json': JSON.stringify({ + __symbolic: 'module', + version: 1, + metadata: {}, + exports: [ + {from: './src/origin1', export: ['One', 'Two', {name: 'Three', as: 'Four'}]}, + {from: './src/origin5'}, {from: './src/reexport2'} + ] + }), + 'src': { + 'origin1.d.ts': ` + export class One {} + export class Two {} + export class Three {} + `, + 'origin1.metadata.json': JSON.stringify({ + __symbolic: 'module', + version: 1, + metadata: { + One: {__symbolic: 'class'}, + Two: {__symbolic: 'class'}, + Three: {__symbolic: 'class'}, + }, + }), + 'origin5.d.ts': ` + export class Five {} + `, + 'origin5.metadata.json': JSON.stringify({ + __symbolic: 'module', + version: 1, + metadata: { + Five: {__symbolic: 'class'}, + }, + }), + 'origin30.d.ts': ` + export class Thirty {} + `, + 'origin30.metadata.json': JSON.stringify({ + __symbolic: 'module', + version: 1, + metadata: { + Thirty: {__symbolic: 'class'}, + }, + }), + 'originNone.d.ts': dummyModule, + 'originNone.metadata.json': JSON.stringify({ + __symbolic: 'module', + version: 1, + metadata: {}, + }), + 'reexport2.d.ts': dummyModule, + 'reexport2.metadata.json': JSON.stringify({ + __symbolic: 'module', + version: 1, + metadata: {}, + exports: [{from: './originNone'}, {from: './origin30'}] + }) + } + }, + 'node_modules': { + '@angular': { + 'core.d.ts': dummyModule, + 'core.metadata.json': + `{"__symbolic":"module", "version": 1, "metadata": {"foo": {"__symbolic": "class"}}}`, + 'router': {'index.d.ts': dummyModule, 'src': {'providers.d.ts': dummyModule}}, + 'unused.d.ts': dummyModule, + 'empty.d.ts': 'export declare var a: string;', + 'empty.metadata.json': '[]', + } + } + } + } +}; + +function clone(entry: Entry): Entry { + if (typeof entry === 'string') { + return entry; + } else { + const result: Directory = {}; + for (const name in entry) { + result[name] = clone(entry[name]); + } + return result; + } +} diff --git a/modules/@angular/compiler/index.ts b/modules/@angular/compiler/index.ts index 8b37b1e698..d1ae9ef26f 100644 --- a/modules/@angular/compiler/index.ts +++ b/modules/@angular/compiler/index.ts @@ -26,7 +26,6 @@ export {TEMPLATE_TRANSFORMS} from './src/template_parser/template_parser'; export {CompilerConfig, RenderTypes} from './src/config'; export * from './src/compile_metadata'; export * from './src/aot/compiler'; -export * from './src/aot/compiler_host'; export * from './src/aot/static_reflector'; export * from './src/aot/static_reflection_capabilities'; export * from './src/aot/static_symbol'; diff --git a/modules/@angular/compiler/src/aot/compiler_host.ts b/modules/@angular/compiler/src/aot/compiler_host.ts deleted file mode 100644 index 6c3fdf8001..0000000000 --- a/modules/@angular/compiler/src/aot/compiler_host.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @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 - */ - -import {StaticSymbol} from './static_symbol'; - -/** - * The host of the AotCompiler disconnects the implementation from TypeScript / other language - * services and from underlying file systems. - */ -export interface AotCompilerHost { - /** - * Return a ModuleMetadata for the given module. - * Angular 2 CLI will produce this metadata for a module whenever a .d.ts files is - * produced and the module has exported variables or classes with decorators. Module metadata can - * also be produced directly from TypeScript sources by using MetadataCollector in tools/metadata. - * - * @param modulePath is a string identifier for a module as an absolute path. - * @returns the metadata for the given module. - */ - getMetadataFor(modulePath: string): {[key: string]: any}|{[key: string]: any}[]; - - /** - * Converts a module name into a file path. - */ - resolveImportToFile(moduleName: string, containingFile: string): string; -} \ No newline at end of file diff --git a/modules/@angular/compiler/src/aot/static_reflector.ts b/modules/@angular/compiler/src/aot/static_reflector.ts index 2efca94244..f2c5b4614e 100644 --- a/modules/@angular/compiler/src/aot/static_reflector.ts +++ b/modules/@angular/compiler/src/aot/static_reflector.ts @@ -7,49 +7,75 @@ */ import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core'; -import {AssetUrl} from '../output/path_util'; + import {ReflectorReader} from '../private_import_core'; -import {AotCompilerHost} from './compiler_host'; + import {StaticSymbol} from './static_symbol'; const SUPPORTED_SCHEMA_VERSION = 1; -const ANGULAR_IMPORT_LOCATIONS = { - coreDecorators: '@angular/core/src/metadata', - diDecorators: '@angular/core/src/di/metadata', - diMetadata: '@angular/core/src/di/metadata', - diOpaqueToken: '@angular/core/src/di/opaque_token', - animationMetadata: '@angular/core/src/animation/metadata', - provider: '@angular/core/src/di/provider' -}; + +/** + * The host of the static resolver is expected to be able to provide module metadata in the form of + * ModuleMetadata. Angular 2 CLI will produce this metadata for a module whenever a .d.ts files is + * produced and the module has exported variables or classes with decorators. Module metadata can + * also be produced directly from TypeScript sources by using MetadataCollector in tools/metadata. + */ +export interface StaticReflectorHost { + /** + * Return a ModuleMetadata for the given module. + * + * @param modulePath is a string identifier for a module as an absolute path. + * @returns the metadata for the given module. + */ + getMetadataFor(modulePath: string): {[key: string]: any}|{[key: string]: any}[]; + + /** + * Resolve a symbol from an import statement form, to the file where it is declared. + * @param module the location imported from + * @param containingFile for relative imports, the path of the file containing the import + */ + findDeclaration(modulePath: string, symbolName: string, containingFile?: string): StaticSymbol; + + getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol; + + angularImportLocations(): { + coreDecorators: string, + diDecorators: string, + diMetadata: string, + diOpaqueToken: string, + animationMetadata: string, + provider: string + }; + + getCanonicalFileName(fileName: string): string; +} /** * A static reflector implements enough of the Reflector API that is necessary to compile * templates statically. */ export class StaticReflector implements ReflectorReader { - private typeCache = new Map(); private annotationCache = new Map(); private propertyCache = new Map(); private parameterCache = new Map(); private metadataCache = new Map(); private conversionMap = new Map any>(); - private declarationMap = new Map(); private opaqueToken: StaticSymbol; - constructor(private host: AotCompilerHost) { this.initializeConversionMap(); } + constructor(private host: StaticReflectorHost) { this.initializeConversionMap(); } importUri(typeOrFunc: StaticSymbol): string { - const staticSymbol = this.findDeclaration(typeOrFunc.filePath, typeOrFunc.name, ''); + const staticSymbol = this.host.findDeclaration(typeOrFunc.filePath, typeOrFunc.name, ''); return staticSymbol ? staticSymbol.filePath : null; } resolveIdentifier(name: string, moduleUrl: string, runtime: any): any { - return this.findDeclaration(moduleUrl, name, ''); + return this.host.findDeclaration(moduleUrl, name, ''); } resolveEnum(enumIdentifier: any, name: string): any { const staticSymbol: StaticSymbol = enumIdentifier; - return this.getStaticSymbol(staticSymbol.filePath, staticSymbol.name, [name]); + return this.host.getStaticSymbol(staticSymbol.filePath, staticSymbol.name, [name]); } public annotations(type: StaticSymbol): any[] { @@ -146,156 +172,59 @@ export class StaticReflector implements ReflectorReader { private initializeConversionMap(): void { const {coreDecorators, diDecorators, diMetadata, diOpaqueToken, animationMetadata, provider} = - ANGULAR_IMPORT_LOCATIONS; - this.opaqueToken = this.findDeclaration(diOpaqueToken, 'OpaqueToken'); + this.host.angularImportLocations(); + this.opaqueToken = this.host.findDeclaration(diOpaqueToken, 'OpaqueToken'); - this.registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Host'), Host); + this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Host'), Host); this.registerDecoratorOrConstructor( - this.findDeclaration(diDecorators, 'Injectable'), Injectable); - this.registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Self'), Self); - this.registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'SkipSelf'), SkipSelf); - this.registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Inject'), Inject); - this.registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Optional'), Optional); + this.host.findDeclaration(diDecorators, 'Injectable'), Injectable); + this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Self'), Self); this.registerDecoratorOrConstructor( - this.findDeclaration(coreDecorators, 'Attribute'), Attribute); + this.host.findDeclaration(diDecorators, 'SkipSelf'), SkipSelf); + this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Inject'), Inject); this.registerDecoratorOrConstructor( - this.findDeclaration(coreDecorators, 'ContentChild'), ContentChild); + this.host.findDeclaration(diDecorators, 'Optional'), Optional); this.registerDecoratorOrConstructor( - this.findDeclaration(coreDecorators, 'ContentChildren'), ContentChildren); + this.host.findDeclaration(coreDecorators, 'Attribute'), Attribute); this.registerDecoratorOrConstructor( - this.findDeclaration(coreDecorators, 'ViewChild'), ViewChild); + this.host.findDeclaration(coreDecorators, 'ContentChild'), ContentChild); this.registerDecoratorOrConstructor( - this.findDeclaration(coreDecorators, 'ViewChildren'), ViewChildren); - this.registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Input'), Input); - this.registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Output'), Output); - this.registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Pipe'), Pipe); + this.host.findDeclaration(coreDecorators, 'ContentChildren'), ContentChildren); this.registerDecoratorOrConstructor( - this.findDeclaration(coreDecorators, 'HostBinding'), HostBinding); + this.host.findDeclaration(coreDecorators, 'ViewChild'), ViewChild); this.registerDecoratorOrConstructor( - this.findDeclaration(coreDecorators, 'HostListener'), HostListener); + this.host.findDeclaration(coreDecorators, 'ViewChildren'), ViewChildren); + this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'Input'), Input); this.registerDecoratorOrConstructor( - this.findDeclaration(coreDecorators, 'Directive'), Directive); + this.host.findDeclaration(coreDecorators, 'Output'), Output); + this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'Pipe'), Pipe); this.registerDecoratorOrConstructor( - this.findDeclaration(coreDecorators, 'Component'), Component); - this.registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'NgModule'), NgModule); + this.host.findDeclaration(coreDecorators, 'HostBinding'), HostBinding); + this.registerDecoratorOrConstructor( + this.host.findDeclaration(coreDecorators, 'HostListener'), HostListener); + this.registerDecoratorOrConstructor( + this.host.findDeclaration(coreDecorators, 'Directive'), Directive); + this.registerDecoratorOrConstructor( + this.host.findDeclaration(coreDecorators, 'Component'), Component); + this.registerDecoratorOrConstructor( + this.host.findDeclaration(coreDecorators, 'NgModule'), NgModule); // Note: Some metadata classes can be used directly with Provider.deps. - this.registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Host'), Host); - this.registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Self'), Self); - this.registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'SkipSelf'), SkipSelf); - this.registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Optional'), Optional); + this.registerDecoratorOrConstructor(this.host.findDeclaration(diMetadata, 'Host'), Host); + this.registerDecoratorOrConstructor(this.host.findDeclaration(diMetadata, 'Self'), Self); + this.registerDecoratorOrConstructor( + this.host.findDeclaration(diMetadata, 'SkipSelf'), SkipSelf); + this.registerDecoratorOrConstructor( + this.host.findDeclaration(diMetadata, 'Optional'), Optional); - this.registerFunction(this.findDeclaration(animationMetadata, 'trigger'), trigger); - this.registerFunction(this.findDeclaration(animationMetadata, 'state'), state); - this.registerFunction(this.findDeclaration(animationMetadata, 'transition'), transition); - this.registerFunction(this.findDeclaration(animationMetadata, 'style'), style); - this.registerFunction(this.findDeclaration(animationMetadata, 'animate'), animate); - this.registerFunction(this.findDeclaration(animationMetadata, 'keyframes'), keyframes); - this.registerFunction(this.findDeclaration(animationMetadata, 'sequence'), sequence); - this.registerFunction(this.findDeclaration(animationMetadata, 'group'), group); - } - - /** - * getStaticSymbol produces a Type whose metadata is known but whose implementation is not loaded. - * All types passed to the StaticResolver should be pseudo-types returned by this method. - * - * @param declarationFile the absolute path of the file where the symbol is declared - * @param name the name of the type. - */ - getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol { - const memberSuffix = members ? `.${ members.join('.')}` : ''; - const key = `"${declarationFile}".${name}${memberSuffix}`; - let result = this.typeCache.get(key); - if (!result) { - result = new StaticSymbol(declarationFile, name, members); - this.typeCache.set(key, result); - } - return result; - } - - private normalizeAssetUrl(url: string): string { - const assetUrl = AssetUrl.parse(url); - return assetUrl ? `${assetUrl.packageName}@${assetUrl.modulePath}` : null; - } - - private resolveExportedSymbol(filePath: string, symbolName: string): StaticSymbol { - const resolveModule = (moduleName: string): string => { - const resolvedModulePath = this.host.resolveImportToFile(moduleName, filePath); - if (!resolvedModulePath) { - throw new Error(`Could not resolve module '${moduleName}' relative to file ${filePath}`); - } - return resolvedModulePath; - }; - const metadata = this.getModuleMetadata(filePath); - if (metadata) { - // If we have metadata for the symbol, this is the original exporting location. - if (metadata['metadata'][symbolName]) { - return this.getStaticSymbol(filePath, symbolName); - } - - // If no, try to find the symbol in one of the re-export location - if (metadata['exports']) { - // Try and find the symbol in the list of explicitly re-exported symbols. - for (const moduleExport of metadata['exports']) { - if (moduleExport.export) { - const exportSymbol = moduleExport.export.find((symbol: any) => { - if (typeof symbol === 'string') { - return symbol == symbolName; - } else { - return symbol.as == symbolName; - } - }); - if (exportSymbol) { - let symName = symbolName; - if (typeof exportSymbol !== 'string') { - symName = exportSymbol.name; - } - return this.resolveExportedSymbol(resolveModule(moduleExport.from), symName); - } - } - } - - // Try to find the symbol via export * directives. - for (const moduleExport of metadata['exports']) { - if (!moduleExport.export) { - const resolvedModule = resolveModule(moduleExport.from); - const candidateSymbol = this.resolveExportedSymbol(resolvedModule, symbolName); - if (candidateSymbol) return candidateSymbol; - } - } - } - } - return null; - } - - findDeclaration(module: string, symbolName: string, containingFile?: string): StaticSymbol { - const cacheKey = `${module}|${symbolName}|${containingFile}`; - let symbol = this.declarationMap.get(cacheKey); - if (symbol) { - return symbol; - } - try { - const assetUrl = this.normalizeAssetUrl(module); - if (assetUrl) { - module = assetUrl; - } - const filePath = this.host.resolveImportToFile(module, containingFile); - - if (!filePath) { - // If the file cannot be found the module is probably referencing a declared module - // for which there is no disambiguating file and we also don't need to track - // re-exports. Just use the module name. - return this.getStaticSymbol(module, symbolName); - } - - let symbol = this.resolveExportedSymbol(filePath, symbolName) || - this.getStaticSymbol(filePath, symbolName); - this.declarationMap.set(cacheKey, symbol); - return symbol; - } catch (e) { - console.error(`can't resolve module ${module} from ${containingFile}`); - throw e; - } + this.registerFunction(this.host.findDeclaration(animationMetadata, 'trigger'), trigger); + this.registerFunction(this.host.findDeclaration(animationMetadata, 'state'), state); + this.registerFunction(this.host.findDeclaration(animationMetadata, 'transition'), transition); + this.registerFunction(this.host.findDeclaration(animationMetadata, 'style'), style); + this.registerFunction(this.host.findDeclaration(animationMetadata, 'animate'), animate); + this.registerFunction(this.host.findDeclaration(animationMetadata, 'keyframes'), keyframes); + this.registerFunction(this.host.findDeclaration(animationMetadata, 'sequence'), sequence); + this.registerFunction(this.host.findDeclaration(animationMetadata, 'group'), group); } /** @internal */ @@ -308,10 +237,10 @@ export class StaticReflector implements ReflectorReader { function resolveReference(context: StaticSymbol, expression: any): StaticSymbol { let staticSymbol: StaticSymbol; if (expression['module']) { - staticSymbol = - _this.findDeclaration(expression['module'], expression['name'], context.filePath); + staticSymbol = _this.host.findDeclaration( + expression['module'], expression['name'], context.filePath); } else { - staticSymbol = _this.getStaticSymbol(context.filePath, expression['name']); + staticSymbol = _this.host.getStaticSymbol(context.filePath, expression['name']); } return staticSymbol; } @@ -520,7 +449,8 @@ export class StaticReflector implements ReflectorReader { const members = selectTarget.members ? (selectTarget.members as string[]).concat(member) : [member]; - return _this.getStaticSymbol(selectTarget.filePath, selectTarget.name, members); + return _this.host.getStaticSymbol( + selectTarget.filePath, selectTarget.name, members); } } const member = simplify(expression['member']); @@ -555,10 +485,10 @@ export class StaticReflector implements ReflectorReader { // Determine if the function is a built-in conversion let target = expression['expression']; if (target['module']) { - staticSymbol = - _this.findDeclaration(target['module'], target['name'], context.filePath); + staticSymbol = _this.host.findDeclaration( + target['module'], target['name'], context.filePath); } else { - staticSymbol = _this.getStaticSymbol(context.filePath, target['name']); + staticSymbol = _this.host.getStaticSymbol(context.filePath, target['name']); } let converter = _this.conversionMap.get(staticSymbol); if (converter) { diff --git a/modules/@angular/compiler/test/aot/static_reflector_spec.ts b/modules/@angular/compiler/test/aot/static_reflector_spec.ts index 219519caac..81d99f3866 100644 --- a/modules/@angular/compiler/test/aot/static_reflector_spec.ts +++ b/modules/@angular/compiler/test/aot/static_reflector_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AotCompilerHost, StaticReflector, StaticSymbol} from '@angular/compiler'; +import {StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler'; import {HostListener, Inject, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core'; import {MetadataCollector} from '@angular/tsc-wrapped'; import * as ts from 'typescript'; @@ -18,11 +18,11 @@ const TS_EXT = /(^.|(?!\.d)..)\.ts$/; describe('StaticReflector', () => { const noContext = new StaticSymbol('', ''); - let host: AotCompilerHost; + let host: StaticReflectorHost; let reflector: StaticReflector; beforeEach(() => { - host = new MockAotCompilerHost(); + host = new MockReflectorHost(); reflector = new StaticReflector(host); }); @@ -31,7 +31,7 @@ describe('StaticReflector', () => { } it('should get annotations for NgFor', () => { - const NgFor = reflector.findDeclaration('@angular/common/src/directives/ng_for', 'NgFor'); + const NgFor = host.findDeclaration('angular2/src/common/directives/ng_for', 'NgFor'); const annotations = reflector.annotations(NgFor); expect(annotations.length).toEqual(1); const annotation = annotations[0]; @@ -40,15 +40,15 @@ describe('StaticReflector', () => { }); it('should get constructor for NgFor', () => { - const NgFor = reflector.findDeclaration('@angular/common/src/directives/ng_for', 'NgFor'); - const ViewContainerRef = reflector.findDeclaration( - '@angular/core/src/linker/view_container_ref', 'ViewContainerRef'); + const NgFor = host.findDeclaration('angular2/src/common/directives/ng_for', 'NgFor'); + const ViewContainerRef = + host.findDeclaration('angular2/src/core/linker/view_container_ref', 'ViewContainerRef'); const TemplateRef = - reflector.findDeclaration('@angular/core/src/linker/template_ref', 'TemplateRef'); - const IterableDiffers = reflector.findDeclaration( - '@angular/core/src/change_detection/differs/iterable_differs', 'IterableDiffers'); - const ChangeDetectorRef = reflector.findDeclaration( - '@angular/core/src/change_detection/change_detector_ref', 'ChangeDetectorRef'); + host.findDeclaration('angular2/src/core/linker/template_ref', 'TemplateRef'); + const IterableDiffers = host.findDeclaration( + 'angular2/src/core/change_detection/differs/iterable_differs', 'IterableDiffers'); + const ChangeDetectorRef = host.findDeclaration( + 'angular2/src/core/change_detection/change_detector_ref', 'ChangeDetectorRef'); const parameters = reflector.parameters(NgFor); expect(parameters).toEqual([ @@ -58,7 +58,7 @@ describe('StaticReflector', () => { it('should get annotations for HeroDetailComponent', () => { const HeroDetailComponent = - reflector.findDeclaration('src/app/hero-detail.component', 'HeroDetailComponent'); + host.findDeclaration('src/app/hero-detail.component', 'HeroDetailComponent'); const annotations = reflector.annotations(HeroDetailComponent); expect(annotations.length).toEqual(1); const annotation = annotations[0]; @@ -74,39 +74,40 @@ describe('StaticReflector', () => { }); it('should throw and exception for unsupported metadata versions', () => { - expect(() => reflector.findDeclaration('src/version-error', 'e')) + const e = host.findDeclaration('src/version-error', 'e'); + expect(() => reflector.annotations(e)) .toThrow(new Error( 'Metadata version mismatch for module /tmp/src/version-error.d.ts, found version 100, expected 1')); }); it('should get and empty annotation list for an unknown class', () => { - const UnknownClass = reflector.findDeclaration('src/app/app.component', 'UnknownClass'); + const UnknownClass = host.findDeclaration('src/app/app.component', 'UnknownClass'); const annotations = reflector.annotations(UnknownClass); expect(annotations).toEqual([]); }); it('should get propMetadata for HeroDetailComponent', () => { const HeroDetailComponent = - reflector.findDeclaration('src/app/hero-detail.component', 'HeroDetailComponent'); + host.findDeclaration('src/app/hero-detail.component', 'HeroDetailComponent'); const props = reflector.propMetadata(HeroDetailComponent); expect(props['hero']).toBeTruthy(); expect(props['onMouseOver']).toEqual([new HostListener('mouseover', ['$event'])]); }); it('should get an empty object from propMetadata for an unknown class', () => { - const UnknownClass = reflector.findDeclaration('src/app/app.component', 'UnknownClass'); + const UnknownClass = host.findDeclaration('src/app/app.component', 'UnknownClass'); const properties = reflector.propMetadata(UnknownClass); expect(properties).toEqual({}); }); it('should get empty parameters list for an unknown class ', () => { - const UnknownClass = reflector.findDeclaration('src/app/app.component', 'UnknownClass'); + const UnknownClass = host.findDeclaration('src/app/app.component', 'UnknownClass'); const parameters = reflector.parameters(UnknownClass); expect(parameters).toEqual([]); }); it('should provide context for errors reported by the collector', () => { - const SomeClass = reflector.findDeclaration('src/error-reporting', 'SomeClass'); + const SomeClass = host.findDeclaration('src/error-reporting', 'SomeClass'); expect(() => reflector.annotations(SomeClass)) .toThrow(new Error( 'Error encountered resolving symbol values statically. A reasonable error message (position 13:34 in the original .ts file), resolving symbol ErrorSym in /tmp/src/error-references.d.ts, resolving symbol Link2 in /tmp/src/error-references.d.ts, resolving symbol Link1 in /tmp/src/error-references.d.ts, resolving symbol SomeClass in /tmp/src/error-reporting.d.ts, resolving symbol SomeClass in /tmp/src/error-reporting.d.ts')); @@ -307,14 +308,14 @@ describe('StaticReflector', () => { expect(simplify( new StaticSymbol('/src/cases', ''), ({__symbolic: 'reference', module: './extern', name: 'nonExisting'}))) - .toEqual(reflector.getStaticSymbol('/src/extern.d.ts', 'nonExisting')); + .toEqual(host.getStaticSymbol('/src/extern.d.ts', 'nonExisting')); }); it('should simplify a function reference as a static symbol', () => { expect(simplify( new StaticSymbol('/src/cases', 'myFunction'), ({__symbolic: 'function', parameters: ['a'], value: []}))) - .toEqual(reflector.getStaticSymbol('/src/cases', 'myFunction')); + .toEqual(host.getStaticSymbol('/src/cases', 'myFunction')); }); it('should simplify values initialized with a function call', () => { @@ -405,35 +406,35 @@ describe('StaticReflector', () => { it('should be able to get metadata for a class containing a custom decorator', () => { const props = reflector.propMetadata( - reflector.getStaticSymbol('/tmp/src/custom-decorator-reference.ts', 'Foo')); + host.getStaticSymbol('/tmp/src/custom-decorator-reference.ts', 'Foo')); expect(props).toEqual({foo: []}); }); it('should read ctor parameters with forwardRef', () => { const src = '/tmp/src/forward-ref.ts'; - const dep = reflector.getStaticSymbol(src, 'Dep'); - const props = reflector.parameters(reflector.getStaticSymbol(src, 'Forward')); + const dep = host.getStaticSymbol(src, 'Dep'); + const props = reflector.parameters(host.getStaticSymbol(src, 'Forward')); expect(props).toEqual([[dep, new Inject(dep)]]); }); it('should report an error for invalid function calls', () => { expect( - () => reflector.annotations( - reflector.getStaticSymbol('/tmp/src/invalid-calls.ts', 'MyComponent'))) + () => + reflector.annotations(host.getStaticSymbol('/tmp/src/invalid-calls.ts', 'MyComponent'))) .toThrow(new Error( `Error encountered resolving symbol values statically. Calling function 'someFunction', function calls are not supported. Consider replacing the function or lambda with a reference to an exported function, resolving symbol MyComponent in /tmp/src/invalid-calls.ts, resolving symbol MyComponent in /tmp/src/invalid-calls.ts`)); }); it('should be able to get metadata for a class containing a static method call', () => { const annotations = reflector.annotations( - reflector.getStaticSymbol('/tmp/src/static-method-call.ts', 'MyComponent')); + host.getStaticSymbol('/tmp/src/static-method-call.ts', 'MyComponent')); expect(annotations.length).toBe(1); expect(annotations[0].providers).toEqual({provider: 'a', useValue: 100}); }); it('should be able to get metadata for a class containing a static field reference', () => { - const annotations = reflector.annotations( - reflector.getStaticSymbol('/tmp/src/static-field-reference.ts', 'Foo')); + const annotations = + reflector.annotations(host.getStaticSymbol('/tmp/src/static-field-reference.ts', 'Foo')); expect(annotations.length).toBe(1); expect(annotations[0].providers).toEqual([{provider: 'a', useValue: 'Some string'}]); }); @@ -441,7 +442,7 @@ describe('StaticReflector', () => { it('should be able to get the metadata for a class calling a method with a conditional expression', () => { const annotations = reflector.annotations( - reflector.getStaticSymbol('/tmp/src/static-method-call.ts', 'MyCondComponent')); + host.getStaticSymbol('/tmp/src/static-method-call.ts', 'MyCondComponent')); expect(annotations.length).toBe(1); expect(annotations[0].providers).toEqual([ [{provider: 'a', useValue: '1'}], [{provider: 'a', useValue: '2'}] @@ -451,68 +452,50 @@ describe('StaticReflector', () => { it('should be able to get the metadata for a class calling a method with default parameters', () => { const annotations = reflector.annotations( - reflector.getStaticSymbol('/tmp/src/static-method-call.ts', 'MyDefaultsComponent')); + host.getStaticSymbol('/tmp/src/static-method-call.ts', 'MyDefaultsComponent')); expect(annotations.length).toBe(1); expect(annotations[0].providers).toEqual([['a', true, false]]); }); it('should be able to get metadata with a reference to a static method', () => { const annotations = reflector.annotations( - reflector.getStaticSymbol('/tmp/src/static-method-ref.ts', 'MethodReference')); + host.getStaticSymbol('/tmp/src/static-method-ref.ts', 'MethodReference')); expect(annotations.length).toBe(1); expect(annotations[0].providers[0].useValue.members[0]).toEqual('staticMethod'); }); - - it('should be able to produce a symbol for an exported symbol', () => { - expect(reflector.findDeclaration('@angular/router', 'foo', 'main.ts')).toBeDefined(); - }); - - it('should be able to produce a symbol for values space only reference', () => { - expect(reflector.findDeclaration('@angular/router/src/providers', 'foo', 'main.ts')) - .toBeDefined(); - }); - - it('should be produce the same symbol if asked twice', () => { - const foo1 = reflector.getStaticSymbol('main.ts', 'foo'); - const foo2 = reflector.getStaticSymbol('main.ts', 'foo'); - expect(foo1).toBe(foo2); - }); - - it('should be able to produce a symbol for a module with no file', - () => { expect(reflector.getStaticSymbol('angularjs', 'SomeAngularSymbol')).toBeDefined(); }); - - it('should be able to trace a named export', () => { - const symbol = reflector.findDeclaration('./reexport/reexport', 'One', '/tmp/src/main.ts'); - expect(symbol.name).toEqual('One'); - expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts'); - }); - - it('should be able to trace a renamed export', () => { - const symbol = reflector.findDeclaration('./reexport/reexport', 'Four', '/tmp/src/main.ts'); - expect(symbol.name).toEqual('Three'); - expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts'); - }); - - it('should be able to trace an export * export', () => { - const symbol = reflector.findDeclaration('./reexport/reexport', 'Five', '/tmp/src/main.ts'); - expect(symbol.name).toEqual('Five'); - expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin5.d.ts'); - }); - - it('should be able to trace a multi-level re-export', () => { - const symbol = reflector.findDeclaration('./reexport/reexport', 'Thirty', '/tmp/src/main.ts'); - expect(symbol.name).toEqual('Thirty'); - expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin30.d.ts'); - }); }); -class MockAotCompilerHost implements AotCompilerHost { +class MockReflectorHost implements StaticReflectorHost { + private staticTypeCache = new Map(); private collector = new MetadataCollector(); constructor() {} + angularImportLocations() { + return { + coreDecorators: 'angular2/src/core/metadata', + diDecorators: 'angular2/src/core/di/metadata', + diMetadata: 'angular2/src/core/di/metadata', + diOpaqueToken: 'angular2/src/core/di/opaque_token', + animationMetadata: 'angular2/src/core/animation/metadata', + provider: 'angular2/src/core/di/provider' + }; + } + + getCanonicalFileName(fileName: string): string { return fileName; } + + getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol { + const cacheKey = `${declarationFile}:${name}${members?'.'+members.join('.'):''}`; + let result = this.staticTypeCache.get(cacheKey); + if (!result) { + result = new StaticSymbol(declarationFile, name, members); + this.staticTypeCache.set(cacheKey, result); + } + return result; + } + // In tests, assume that symbols are not re-exported - resolveImportToFile(modulePath: string, containingFile?: string): string { + findDeclaration(modulePath: string, symbolName: string, containingFile?: string): StaticSymbol { function splitPath(path: string): string[] { return path.split(/\/|\\/g); } function resolvePath(pathParts: string[]): string { @@ -547,16 +530,16 @@ class MockAotCompilerHost implements AotCompilerHost { const baseName = pathTo(containingFile, modulePath); const tsName = baseName + '.ts'; if (this.getMetadataFor(tsName)) { - return tsName; + return this.getStaticSymbol(tsName, symbolName); } - return baseName + '.d.ts'; + return this.getStaticSymbol(baseName + '.d.ts', symbolName); } - return '/tmp/' + modulePath + '.d.ts'; + return this.getStaticSymbol('/tmp/' + modulePath + '.d.ts', symbolName); } getMetadataFor(moduleId: string): any { const data: {[key: string]: any} = { - '/tmp/@angular/common/src/forms-deprecated/directives.d.ts': [{ + '/tmp/angular2/src/common/forms-deprecated/directives.d.ts': [{ '__symbolic': 'module', 'version': 1, 'metadata': { @@ -564,12 +547,12 @@ class MockAotCompilerHost implements AotCompilerHost { { '__symbolic': 'reference', 'name': 'NgFor', - 'module': '@angular/common/src/directives/ng_for' + 'module': 'angular2/src/common/directives/ng_for' } ] } }], - '/tmp/@angular/common/src/directives/ng_for.d.ts': { + '/tmp/angular2/src/common/directives/ng_for.d.ts': { '__symbolic': 'module', 'version': 1, 'metadata': { @@ -581,7 +564,7 @@ class MockAotCompilerHost implements AotCompilerHost { 'expression': { '__symbolic': 'reference', 'name': 'Directive', - 'module': '@angular/core/src/metadata' + 'module': '../../core/metadata' }, 'arguments': [ { @@ -598,22 +581,22 @@ class MockAotCompilerHost implements AotCompilerHost { 'parameters': [ { '__symbolic': 'reference', - 'module': '@angular/core/src/linker/view_container_ref', + 'module': '../../core/linker/view_container_ref', 'name': 'ViewContainerRef' }, { '__symbolic': 'reference', - 'module': '@angular/core/src/linker/template_ref', + 'module': '../../core/linker/template_ref', 'name': 'TemplateRef' }, { '__symbolic': 'reference', - 'module': '@angular/core/src/change_detection/differs/iterable_differs', + 'module': '../../core/change_detection/differs/iterable_differs', 'name': 'IterableDiffers' }, { '__symbolic': 'reference', - 'module': '@angular/core/src/change_detection/change_detector_ref', + 'module': '../../core/change_detection/change_detector_ref', 'name': 'ChangeDetectorRef' } ] @@ -623,13 +606,13 @@ class MockAotCompilerHost implements AotCompilerHost { } } }, - '/tmp/@angular/core/src/linker/view_container_ref.d.ts': + '/tmp/angular2/src/core/linker/view_container_ref.d.ts': {version: 1, 'metadata': {'ViewContainerRef': {'__symbolic': 'class'}}}, - '/tmp/@angular/core/src/linker/template_ref.d.ts': + '/tmp/angular2/src/core/linker/template_ref.d.ts': {version: 1, 'module': './template_ref', 'metadata': {'TemplateRef': {'__symbolic': 'class'}}}, - '/tmp/@angular/core/src/change_detection/differs/iterable_differs.d.ts': + '/tmp/angular2/src/core/change_detection/differs/iterable_differs.d.ts': {version: 1, 'metadata': {'IterableDiffers': {'__symbolic': 'class'}}}, - '/tmp/@angular/core/src/change_detection/change_detector_ref.d.ts': + '/tmp/angular2/src/core/change_detection/change_detector_ref.d.ts': {version: 1, 'metadata': {'ChangeDetectorRef': {'__symbolic': 'class'}}}, '/tmp/src/app/hero-detail.component.d.ts': { '__symbolic': 'module', @@ -643,7 +626,7 @@ class MockAotCompilerHost implements AotCompilerHost { 'expression': { '__symbolic': 'reference', 'name': 'Component', - 'module': '@angular/core/src/metadata' + 'module': 'angular2/src/core/metadata' }, 'arguments': [ { @@ -655,7 +638,7 @@ class MockAotCompilerHost implements AotCompilerHost { 'expression': { '__symbolic': 'reference', 'name': 'trigger', - 'module': '@angular/core/src/animation/metadata' + 'module': 'angular2/src/core/animation/metadata' }, 'arguments': [ 'myAnimation', @@ -663,7 +646,7 @@ class MockAotCompilerHost implements AotCompilerHost { 'expression': { '__symbolic': 'reference', 'name': 'state', - 'module': '@angular/core/src/animation/metadata' + 'module': 'angular2/src/core/animation/metadata' }, 'arguments': [ 'state1', @@ -671,7 +654,7 @@ class MockAotCompilerHost implements AotCompilerHost { 'expression': { '__symbolic': 'reference', 'name': 'style', - 'module': '@angular/core/src/animation/metadata' + 'module': 'angular2/src/core/animation/metadata' }, 'arguments': [ { 'background':'white' } @@ -683,7 +666,7 @@ class MockAotCompilerHost implements AotCompilerHost { 'expression': { '__symbolic':'reference', 'name':'transition', - 'module': '@angular/core/src/animation/metadata' + 'module': 'angular2/src/core/animation/metadata' }, 'arguments': [ '* => *', @@ -692,20 +675,20 @@ class MockAotCompilerHost implements AotCompilerHost { 'expression':{ '__symbolic':'reference', 'name':'sequence', - 'module': '@angular/core/src/animation/metadata' + 'module': 'angular2/src/core/animation/metadata' }, 'arguments':[[{ '__symbolic': 'call', 'expression': { '__symbolic':'reference', 'name':'group', - 'module': '@angular/core/src/animation/metadata' + 'module': 'angular2/src/core/animation/metadata' }, 'arguments':[[{ '__symbolic': 'call', 'expression': { '__symbolic':'reference', 'name':'animate', - 'module': '@angular/core/src/animation/metadata' + 'module': 'angular2/src/core/animation/metadata' }, 'arguments':[ '1s 0.5s', @@ -713,13 +696,13 @@ class MockAotCompilerHost implements AotCompilerHost { 'expression': { '__symbolic':'reference', 'name':'keyframes', - 'module': '@angular/core/src/animation/metadata' + 'module': 'angular2/src/core/animation/metadata' }, 'arguments':[[{ '__symbolic': 'call', 'expression': { '__symbolic':'reference', 'name':'style', - 'module': '@angular/core/src/animation/metadata' + 'module': 'angular2/src/core/animation/metadata' }, 'arguments':[ { 'background': 'blue'} ] }, { @@ -727,7 +710,7 @@ class MockAotCompilerHost implements AotCompilerHost { 'expression': { '__symbolic':'reference', 'name':'style', - 'module': '@angular/core/src/animation/metadata' + 'module': 'angular2/src/core/animation/metadata' }, 'arguments':[ { 'background': 'red'} ] }]] @@ -753,7 +736,7 @@ class MockAotCompilerHost implements AotCompilerHost { 'expression': { '__symbolic': 'reference', 'name': 'Input', - 'module': '@angular/core/src/metadata' + 'module': 'angular2/src/core/metadata' } } ] @@ -767,7 +750,7 @@ class MockAotCompilerHost implements AotCompilerHost { '__symbolic': 'call', 'expression': { '__symbolic': 'reference', - 'module': '@angular/core/src/metadata', + 'module': 'angular2/src/core/metadata', 'name': 'HostListener' }, 'arguments': [ @@ -798,7 +781,7 @@ class MockAotCompilerHost implements AotCompilerHost { expression: { __symbolic: 'reference', name: 'Component', - module: '@angular/core/src/metadata' + module: 'angular2/src/core/metadata' }, arguments: [ { @@ -999,8 +982,8 @@ class MockAotCompilerHost implements AotCompilerHost { `, '/tmp/src/invalid-calls.ts': ` import {someFunction} from './nvalid-calll-definitions.ts'; - import {Component} from '@angular/core/src/metadata'; - import {NgIf} from '@angular/common'; + import {Component} from 'angular2/src/core/metadata'; + import {NgIf} from 'angular2/common'; @Component({ selector: 'my-component', @@ -1016,7 +999,7 @@ class MockAotCompilerHost implements AotCompilerHost { export class MyOtherComponent { } `, '/tmp/src/static-method.ts': ` - import {Component} from '@angular/core/src/metadata'; + import {Component} from 'angular2/src/core/metadata'; @Component({ selector: 'stub' @@ -1034,7 +1017,7 @@ class MockAotCompilerHost implements AotCompilerHost { } `, '/tmp/src/static-method-call.ts': ` - import {Component} from '@angular/core/src/metadata'; + import {Component} from 'angular2/src/core/metadata'; import {MyModule} from './static-method'; @Component({ @@ -1053,7 +1036,7 @@ class MockAotCompilerHost implements AotCompilerHost { export class MyDefaultsComponent { } `, '/tmp/src/static-field.ts': ` - import {Injectable} from '@angular/core'; + import {Injectable} from 'angular2/core'; @Injectable() export class MyModule { @@ -1061,7 +1044,7 @@ class MockAotCompilerHost implements AotCompilerHost { } `, '/tmp/src/static-field-reference.ts': ` - import {Component} from '@angular/core/src/metadata'; + import {Component} from 'angular2/src/core/metadata'; import {MyModule} from './static-field'; @Component({ @@ -1075,7 +1058,7 @@ class MockAotCompilerHost implements AotCompilerHost { } `, '/tmp/src/static-method-ref.ts': ` - import {Component} from '@angular/core/src/metadata'; + import {Component} from 'angular2/src/core/metadata'; import {ClassWithStatics} from './static-method-def'; @Component({ @@ -1086,7 +1069,7 @@ class MockAotCompilerHost implements AotCompilerHost { } `, '/tmp/src/invalid-metadata.ts': ` - import {Component} from '@angular/core/src/metadata'; + import {Component} from 'angular2/src/core/metadata'; @Component({ providers: [ { provider: 'a', useValue: (() => 1)() }] @@ -1094,9 +1077,9 @@ class MockAotCompilerHost implements AotCompilerHost { export class InvalidMetadata {} `, '/tmp/src/forward-ref.ts': ` - import {forwardRef} from '@angular/core'; - import {Component} from '@angular/core/src/metadata'; - import {Inject} from '@angular/core/src/di/metadata'; + import {forwardRef} from 'angular2/core'; + import {Component} from 'angular2/src/core/metadata'; + import {Inject} from 'angular2/src/core/di/metadata'; @Component({}) export class Forward { constructor(@Inject(forwardRef(() => Dep)) d: Dep) {} @@ -1104,50 +1087,7 @@ class MockAotCompilerHost implements AotCompilerHost { export class Dep { @Input f: Forward; } - `, - '/tmp/src/reexport/reexport.d.ts': { - __symbolic: 'module', - version: 1, - metadata: {}, - exports: [ - {from: './src/origin1', export: ['One', 'Two', {name: 'Three', as: 'Four'}]}, - {from: './src/origin5'}, {from: './src/reexport2'} - ] - }, - '/tmp/src/reexport/src/origin1.d.ts': { - __symbolic: 'module', - version: 1, - metadata: { - One: {__symbolic: 'class'}, - Two: {__symbolic: 'class'}, - Three: {__symbolic: 'class'}, - }, - }, - '/tmp/src/reexport/src/origin5.d.ts': { - __symbolic: 'module', - version: 1, - metadata: { - Five: {__symbolic: 'class'}, - }, - }, - '/tmp/src/reexport/src/origin30.d.ts': { - __symbolic: 'module', - version: 1, - metadata: { - Thirty: {__symbolic: 'class'}, - }, - }, - '/tmp/src/reexport/src/originNone.d.ts': { - __symbolic: 'module', - version: 1, - metadata: {}, - }, - '/tmp/src/reexport/src/reexport2.d.ts': { - __symbolic: 'module', - version: 1, - metadata: {}, - exports: [{from: './originNone'}, {from: './origin30'}] - } + ` };