diff --git a/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts b/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts index 1a24bfab42..602f8d4829 100644 --- a/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts +++ b/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts @@ -70,7 +70,8 @@ export class DecorationAnalyzer { fullMetaReader = new CompoundMetadataReader([this.metaRegistry, this.dtsMetaReader]); refEmitter = new ReferenceEmitter([ new LocalIdentifierStrategy(), - new AbsoluteModuleStrategy(this.program, this.typeChecker, this.options, this.host), + new AbsoluteModuleStrategy( + this.program, this.typeChecker, this.options, this.host, this.reflectionHost), // TODO(alxhub): there's no reason why ngcc needs the "logical file system" logic here, as ngcc // projects only ever have one rootDir. Instead, ngcc should just switch its emitted import // based on whether a bestGuessOwningModule is present in the Reference. diff --git a/packages/compiler-cli/src/ngtsc/imports/src/emitter.ts b/packages/compiler-cli/src/ngtsc/imports/src/emitter.ts index fcfb24edcd..3131558ee8 100644 --- a/packages/compiler-cli/src/ngtsc/imports/src/emitter.ts +++ b/packages/compiler-cli/src/ngtsc/imports/src/emitter.ts @@ -11,11 +11,13 @@ import {ExternalReference} from '@angular/compiler/src/compiler'; import * as ts from 'typescript'; import {LogicalFileSystem, LogicalProjectPath} from '../../path'; +import {ReflectionHost} from '../../reflection'; import {getSourceFile, isDeclaration, nodeNameForError, resolveModuleName} from '../../util/src/typescript'; import {findExportedNameOfNode} from './find_export'; import {ImportMode, Reference} from './references'; + /** * A host which supports an operation to convert a file name into a module name. * @@ -115,8 +117,9 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy { private moduleExportsCache = new Map|null>(); constructor( - private program: ts.Program, private checker: ts.TypeChecker, - private options: ts.CompilerOptions, private host: ts.CompilerHost) {} + protected program: ts.Program, protected checker: ts.TypeChecker, + protected options: ts.CompilerOptions, protected host: ts.CompilerHost, + private reflectionHost: ReflectionHost) {} emit(ref: Reference, context: ts.SourceFile, importMode: ImportMode): Expression|null { if (ref.bestGuessOwningModule === null) { @@ -159,7 +162,7 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy { return this.moduleExportsCache.get(moduleName) !; } - private enumerateExportsOfModule(specifier: string, fromFile: string): + protected enumerateExportsOfModule(specifier: string, fromFile: string): Map|null { // First, resolve the module specifier to its entry point, and get the ts.Symbol for it. const resolvedModule = resolveModuleName(specifier, fromFile, this.options, this.host); @@ -172,34 +175,12 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy { return null; } - const entryPointSymbol = this.checker.getSymbolAtLocation(entryPointFile); - if (entryPointSymbol === undefined) { + const exports = this.reflectionHost.getExportsOfModule(entryPointFile); + if (exports === null) { return null; } - - // Next, build a Map of all the ts.Declarations exported via the specifier and their exported - // names. const exportMap = new Map(); - - const exports = this.checker.getExportsOfModule(entryPointSymbol); - for (const expSymbol of exports) { - // Resolve export symbols to their actual declarations. - const declSymbol = expSymbol.flags & ts.SymbolFlags.Alias ? - this.checker.getAliasedSymbol(expSymbol) : - expSymbol; - - // At this point the valueDeclaration of the symbol should be defined. - const decl = declSymbol.valueDeclaration; - if (decl === undefined) { - continue; - } - - // Prefer importing the symbol via its declared name, but take any export of it otherwise. - if (declSymbol.name === expSymbol.name || !exportMap.has(decl)) { - exportMap.set(decl, expSymbol.name); - } - } - + exports.forEach((declaration, name) => { exportMap.set(declaration.node, name); }); return exportMap; } } diff --git a/packages/compiler-cli/src/ngtsc/program.ts b/packages/compiler-cli/src/ngtsc/program.ts index c3ab8ca2de..e16a415fbd 100644 --- a/packages/compiler-cli/src/ngtsc/program.ts +++ b/packages/compiler-cli/src/ngtsc/program.ts @@ -438,7 +438,8 @@ export class NgtscProgram implements api.Program { // First, try to use local identifiers if available. new LocalIdentifierStrategy(), // Next, attempt to use an absolute import. - new AbsoluteModuleStrategy(this.tsProgram, checker, this.options, this.host), + new AbsoluteModuleStrategy( + this.tsProgram, checker, this.options, this.host, this.reflector), // Finally, check if the reference is being written into a file within the project's logical // file system, and use a relative import if so. If this fails, ReferenceEmitter will throw // an error. diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_constructor_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_constructor_spec.ts index 695b5dac10..c08b50c3e8 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_constructor_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_constructor_spec.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, Reference, ReferenceEmitter} from '../../imports'; import {AbsoluteFsPath, LogicalFileSystem} from '../../path'; -import {isNamedClassDeclaration} from '../../reflection'; +import {TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection'; import {getDeclaration, makeProgram} from '../../testing/in_memory_typescript'; import {getRootDirs} from '../../util/src/typescript'; import {TypeCheckingConfig} from '../src/api'; @@ -54,7 +54,8 @@ TestClass.ngTypeCtor({value: 'test'}); const logicalFs = new LogicalFileSystem(getRootDirs(host, options)); const emitter = new ReferenceEmitter([ new LocalIdentifierStrategy(), - new AbsoluteModuleStrategy(program, checker, options, host), + new AbsoluteModuleStrategy( + program, checker, options, host, new TypeScriptReflectionHost(checker)), new LogicalProjectStrategy(checker, logicalFs), ]); const ctx = new TypeCheckContext( @@ -84,7 +85,8 @@ TestClass.ngTypeCtor({value: 'test'}); const logicalFs = new LogicalFileSystem(getRootDirs(host, options)); const emitter = new ReferenceEmitter([ new LocalIdentifierStrategy(), - new AbsoluteModuleStrategy(program, checker, options, host), + new AbsoluteModuleStrategy( + program, checker, options, host, new TypeScriptReflectionHost(checker)), new LogicalProjectStrategy(checker, logicalFs), ]); const ctx = new TypeCheckContext(