diff --git a/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts b/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts index ceb5fffe7a..db7b8c2c7f 100644 --- a/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts +++ b/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts @@ -59,6 +59,8 @@ export class DecorationAnalyzer { * Map of NgModule declarations to the re-exports for that NgModule. */ private reexportMap = new Map>(); + moduleResolver = + new ModuleResolver(this.program, this.options, this.host, /* moduleResolutionCache */ null); resourceManager = new NgccResourceLoader(this.fs); metaRegistry = new LocalMetadataRegistry(); dtsMetaReader = new DtsMetadataReader(this.typeChecker, this.reflectionHost); @@ -66,7 +68,7 @@ export class DecorationAnalyzer { refEmitter = new ReferenceEmitter([ new LocalIdentifierStrategy(), new AbsoluteModuleStrategy( - this.program, this.typeChecker, this.options, this.host, this.reflectionHost), + this.program, this.typeChecker, this.moduleResolver, 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. @@ -81,7 +83,6 @@ export class DecorationAnalyzer { fullRegistry = new CompoundMetadataRegistry([this.metaRegistry, this.scopeRegistry]); evaluator = new PartialEvaluator(this.reflectionHost, this.typeChecker, /* dependencyTracker */ null); - moduleResolver = new ModuleResolver(this.program, this.options, this.host); importGraph = new ImportGraph(this.moduleResolver); cycleAnalyzer = new CycleAnalyzer(this.importGraph); handlers: DecoratorHandler[] = [ diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts index f31c698b8a..d1420f8e92 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts @@ -757,7 +757,7 @@ export class ComponentDecoratorHandler implements } // Figure out what file is being imported. - return this.moduleResolver.resolveModuleName(expr.value.moduleName !, origin); + return this.moduleResolver.resolveModule(expr.value.moduleName !, origin.fileName); } private _isCyclicImport(expr: Expression, origin: ts.SourceFile): boolean { diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts index 6cd4723003..09e9908179 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts @@ -48,7 +48,8 @@ runInEachFileSystem(() => { const checker = program.getTypeChecker(); const reflectionHost = new TypeScriptReflectionHost(checker); const evaluator = new PartialEvaluator(reflectionHost, checker, /* dependencyTracker */ null); - const moduleResolver = new ModuleResolver(program, options, host); + const moduleResolver = + new ModuleResolver(program, options, host, /* moduleResolutionCache */ null); const importGraph = new ImportGraph(moduleResolver); const cycleAnalyzer = new CycleAnalyzer(importGraph); const metaRegistry = new LocalMetadataRegistry(); diff --git a/packages/compiler-cli/src/ngtsc/cycles/src/imports.ts b/packages/compiler-cli/src/ngtsc/cycles/src/imports.ts index 053c1dd277..37d876c1ba 100644 --- a/packages/compiler-cli/src/ngtsc/cycles/src/imports.ts +++ b/packages/compiler-cli/src/ngtsc/cycles/src/imports.ts @@ -68,7 +68,7 @@ export class ImportGraph { stmt.moduleSpecifier !== undefined && ts.isStringLiteral(stmt.moduleSpecifier)) { // Resolve the module to a file, and check whether that file is in the ts.Program. const moduleName = stmt.moduleSpecifier.text; - const moduleFile = this.resolver.resolveModuleName(moduleName, sf); + const moduleFile = this.resolver.resolveModule(moduleName, sf.fileName); if (moduleFile !== null && isLocalFile(moduleFile)) { // Record this local import. imports.add(moduleFile); diff --git a/packages/compiler-cli/src/ngtsc/cycles/test/analyzer_spec.ts b/packages/compiler-cli/src/ngtsc/cycles/test/analyzer_spec.ts index 3f11d7c418..b6e9d1df08 100644 --- a/packages/compiler-cli/src/ngtsc/cycles/test/analyzer_spec.ts +++ b/packages/compiler-cli/src/ngtsc/cycles/test/analyzer_spec.ts @@ -62,9 +62,11 @@ runInEachFileSystem(() => { function makeAnalyzer(graph: string): {program: ts.Program, analyzer: CycleAnalyzer} { const {program, options, host} = makeProgramFromGraph(getFileSystem(), graph); + const moduleResolver = + new ModuleResolver(program, options, host, /* moduleResolutionCache */ null); return { program, - analyzer: new CycleAnalyzer(new ImportGraph(new ModuleResolver(program, options, host))), + analyzer: new CycleAnalyzer(new ImportGraph(moduleResolver)), }; } }); diff --git a/packages/compiler-cli/src/ngtsc/cycles/test/imports_spec.ts b/packages/compiler-cli/src/ngtsc/cycles/test/imports_spec.ts index 3a0fe67815..7b3e7b5ef7 100644 --- a/packages/compiler-cli/src/ngtsc/cycles/test/imports_spec.ts +++ b/packages/compiler-cli/src/ngtsc/cycles/test/imports_spec.ts @@ -55,9 +55,11 @@ runInEachFileSystem(() => { function makeImportGraph(graph: string): {program: ts.Program, graph: ImportGraph} { const {program, options, host} = makeProgramFromGraph(getFileSystem(), graph); + const moduleResolver = + new ModuleResolver(program, options, host, /* moduleResolutionCache */ null); return { program, - graph: new ImportGraph(new ModuleResolver(program, options, host)), + graph: new ImportGraph(moduleResolver), }; } diff --git a/packages/compiler-cli/src/ngtsc/imports/src/emitter.ts b/packages/compiler-cli/src/ngtsc/imports/src/emitter.ts index 4fb3c5880b..4606d49ec9 100644 --- a/packages/compiler-cli/src/ngtsc/imports/src/emitter.ts +++ b/packages/compiler-cli/src/ngtsc/imports/src/emitter.ts @@ -8,13 +8,14 @@ import {Expression, ExternalExpr, ExternalReference, WrappedNodeExpr} from '@angular/compiler'; import * as ts from 'typescript'; -import {LogicalFileSystem, LogicalProjectPath, PathSegment, absoluteFrom, absoluteFromSourceFile, basename, dirname, relative, resolve} from '../../file_system'; +import {LogicalFileSystem, LogicalProjectPath, PathSegment, absoluteFromSourceFile, dirname, relative} from '../../file_system'; import {stripExtension} from '../../file_system/src/util'; import {ReflectionHost} from '../../reflection'; -import {getSourceFile, getSourceFileOrNull, isDeclaration, nodeNameForError, resolveModuleName} from '../../util/src/typescript'; +import {getSourceFile, isDeclaration, nodeNameForError} from '../../util/src/typescript'; import {findExportedNameOfNode} from './find_export'; import {ImportMode, Reference} from './references'; +import {ModuleResolver} from './resolver'; @@ -118,8 +119,7 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy { constructor( protected program: ts.Program, protected checker: ts.TypeChecker, - protected options: ts.CompilerOptions, protected host: ts.CompilerHost, - private reflectionHost: ReflectionHost) {} + protected moduleResolver: ModuleResolver, private reflectionHost: ReflectionHost) {} emit(ref: Reference, context: ts.SourceFile, importMode: ImportMode): Expression|null { if (ref.bestGuessOwningModule === null) { @@ -165,13 +165,7 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy { 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); - if (resolvedModule === undefined) { - return null; - } - - const entryPointFile = - getSourceFileOrNull(this.program, absoluteFrom(resolvedModule.resolvedFileName)); + const entryPointFile = this.moduleResolver.resolveModule(specifier, fromFile); if (entryPointFile === null) { return null; } diff --git a/packages/compiler-cli/src/ngtsc/imports/src/resolver.ts b/packages/compiler-cli/src/ngtsc/imports/src/resolver.ts index 7393f25798..61d713337a 100644 --- a/packages/compiler-cli/src/ngtsc/imports/src/resolver.ts +++ b/packages/compiler-cli/src/ngtsc/imports/src/resolver.ts @@ -8,12 +8,6 @@ import * as ts from 'typescript'; import {absoluteFrom} from '../../file_system'; import {getSourceFileOrNull, resolveModuleName} from '../../util/src/typescript'; -import {Reference} from './references'; - -export interface ReferenceResolver { - resolve(decl: ts.Declaration, importFromHint: string|null, fromFile: string): - Reference; -} /** * Used by `RouterEntryPointManager` and `NgModuleRouteAnalyzer` (which is in turn is used by @@ -24,11 +18,12 @@ export interface ReferenceResolver { export class ModuleResolver { constructor( private program: ts.Program, private compilerOptions: ts.CompilerOptions, - private host: ts.CompilerHost) {} + private host: ts.CompilerHost, private moduleResolutionCache: ts.ModuleResolutionCache|null) { + } - resolveModuleName(module: string, containingFile: ts.SourceFile): ts.SourceFile|null { - const resolved = - resolveModuleName(module, containingFile.fileName, this.compilerOptions, this.host); + resolveModule(moduleName: string, containingFile: string): ts.SourceFile|null { + const resolved = resolveModuleName( + moduleName, containingFile, this.compilerOptions, this.host, this.moduleResolutionCache); if (resolved === undefined) { return null; } diff --git a/packages/compiler-cli/src/ngtsc/program.ts b/packages/compiler-cli/src/ngtsc/program.ts index a890ed3775..42ba27f535 100644 --- a/packages/compiler-cli/src/ngtsc/program.ts +++ b/packages/compiler-cli/src/ngtsc/program.ts @@ -177,7 +177,10 @@ export class NgtscProgram implements api.Program { this.reuseTsProgram = this.tsProgram; this.entryPoint = entryPoint !== null ? getSourceFileOrNull(this.tsProgram, entryPoint) : null; - this.moduleResolver = new ModuleResolver(this.tsProgram, options, this.host); + const moduleResolutionCache = ts.createModuleResolutionCache( + this.host.getCurrentDirectory(), fileName => this.host.getCanonicalFileName(fileName)); + this.moduleResolver = + new ModuleResolver(this.tsProgram, options, this.host, moduleResolutionCache); this.cycleAnalyzer = new CycleAnalyzer(new ImportGraph(this.moduleResolver)); this.defaultImportTracker = new DefaultImportTracker(); if (oldProgram === undefined) { @@ -287,7 +290,8 @@ export class NgtscProgram implements api.Program { // of the root files. const containingFile = this.tsProgram.getRootFileNames()[0]; const [entryPath, moduleName] = entryRoute.split('#'); - const resolvedModule = resolveModuleName(entryPath, containingFile, this.options, this.host); + const resolvedModule = + resolveModuleName(entryPath, containingFile, this.options, this.host, null); if (resolvedModule) { entryRoute = entryPointKeyFor(resolvedModule.resolvedFileName, moduleName); @@ -587,8 +591,7 @@ 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, this.reflector), + new AbsoluteModuleStrategy(this.tsProgram, checker, this.moduleResolver, this.reflector), // Finally, check if the reference is being written into a file within the project's .ts // sources, and use a relative import if so. If this fails, ReferenceEmitter will throw // an error. diff --git a/packages/compiler-cli/src/ngtsc/routing/src/route.ts b/packages/compiler-cli/src/ngtsc/routing/src/route.ts index 8c8a5bb7af..2764b09de3 100644 --- a/packages/compiler-cli/src/ngtsc/routing/src/route.ts +++ b/packages/compiler-cli/src/ngtsc/routing/src/route.ts @@ -39,7 +39,7 @@ export class RouterEntryPointManager { if (moduleName === undefined) { return null; } - const resolvedSf = this.moduleResolver.resolveModuleName(relativeFile, context); + const resolvedSf = this.moduleResolver.resolveModule(relativeFile, context.fileName); if (resolvedSf === null) { return null; } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts index 003914282d..f52b16becb 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts @@ -11,7 +11,7 @@ import * as ts from 'typescript'; import {AbsoluteFsPath, LogicalFileSystem, absoluteFrom} from '../../file_system'; import {TestFile} from '../../file_system/testing'; -import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, Reference, ReferenceEmitter} from '../../imports'; +import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, Reference, ReferenceEmitter} from '../../imports'; import {ClassDeclaration, TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection'; import {makeProgram} from '../../testing'; import {getRootDirs} from '../../util/src/typescript'; @@ -29,7 +29,7 @@ export function typescriptLibDts(): TestFile { type Partial = { [P in keyof T]?: T[P]; }; type Pick = { [P in K]: T[P]; }; type NonNullable = T extends null | undefined ? never : T; - + // The following native type declarations are required for proper type inference declare interface Function { call(...args: any[]): any; @@ -40,7 +40,7 @@ export function typescriptLibDts(): TestFile { declare interface String { length: number; } - + declare interface Event { preventDefault(): void; } @@ -48,7 +48,7 @@ export function typescriptLibDts(): TestFile { readonly x: number; readonly y: number; } - + declare interface HTMLElementEventMap { "click": MouseEvent; } @@ -88,7 +88,7 @@ export function angularCoreDts(): TestFile { abstract readonly elementRef: unknown; abstract createEmbeddedView(context: C): unknown; } - + export declare class EventEmitter { subscribe(generatorOrNext?: any, error?: any, complete?: any): unknown; } @@ -131,7 +131,7 @@ export function ngForDts(): TestFile { export interface TrackByFunction { (index: number, item: T): any; } - + export declare class NgForOfContext { $implicit: T; index: number; @@ -249,10 +249,12 @@ export function typecheck( const checker = program.getTypeChecker(); const logicalFs = new LogicalFileSystem(getRootDirs(host, options)); const reflectionHost = new TypeScriptReflectionHost(checker); + const moduleResolver = + new ModuleResolver(program, options, host, /* moduleResolutionCache */ null); const emitter = new ReferenceEmitter([ new LocalIdentifierStrategy(), new AbsoluteModuleStrategy( - program, checker, options, host, new TypeScriptReflectionHost(checker)), + program, checker, moduleResolver, new TypeScriptReflectionHost(checker)), new LogicalProjectStrategy(reflectionHost, logicalFs), ]); const ctx = new TypeCheckContext({...ALL_ENABLED_CONFIG, ...config}, emitter, typeCheckFilePath); 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 4f5e9b0795..e164342bd5 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 @@ -8,7 +8,7 @@ import * as ts from 'typescript'; import {LogicalFileSystem, absoluteFrom, getSourceFileOrError} from '../../file_system'; import {TestFile, runInEachFileSystem} from '../../file_system/testing'; -import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, Reference, ReferenceEmitter} from '../../imports'; +import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, Reference, ReferenceEmitter} from '../../imports'; import {TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection'; import {getDeclaration, makeProgram} from '../../testing'; import {getRootDirs} from '../../util/src/typescript'; @@ -64,9 +64,11 @@ TestClass.ngTypeCtor({value: 'test'}); const checker = program.getTypeChecker(); const reflectionHost = new TypeScriptReflectionHost(checker); const logicalFs = new LogicalFileSystem(getRootDirs(host, options)); + const moduleResolver = + new ModuleResolver(program, options, host, /* moduleResolutionCache */ null); const emitter = new ReferenceEmitter([ new LocalIdentifierStrategy(), - new AbsoluteModuleStrategy(program, checker, options, host, reflectionHost), + new AbsoluteModuleStrategy(program, checker, moduleResolver, reflectionHost), new LogicalProjectStrategy(reflectionHost, logicalFs), ]); const ctx = new TypeCheckContext(ALL_ENABLED_CONFIG, emitter, _('/_typecheck_.ts')); @@ -97,9 +99,11 @@ TestClass.ngTypeCtor({value: 'test'}); const checker = program.getTypeChecker(); const reflectionHost = new TypeScriptReflectionHost(checker); const logicalFs = new LogicalFileSystem(getRootDirs(host, options)); + const moduleResolver = + new ModuleResolver(program, options, host, /* moduleResolutionCache */ null); const emitter = new ReferenceEmitter([ new LocalIdentifierStrategy(), - new AbsoluteModuleStrategy(program, checker, options, host, reflectionHost), + new AbsoluteModuleStrategy(program, checker, moduleResolver, reflectionHost), new LogicalProjectStrategy(reflectionHost, logicalFs), ]); const ctx = new TypeCheckContext(ALL_ENABLED_CONFIG, emitter, _('/_typecheck_.ts')); @@ -136,9 +140,11 @@ TestClass.ngTypeCtor({value: 'test'}); const checker = program.getTypeChecker(); const reflectionHost = new TypeScriptReflectionHost(checker); const logicalFs = new LogicalFileSystem(getRootDirs(host, options)); + const moduleResolver = + new ModuleResolver(program, options, host, /* moduleResolutionCache */ null); const emitter = new ReferenceEmitter([ new LocalIdentifierStrategy(), - new AbsoluteModuleStrategy(program, checker, options, host, reflectionHost), + new AbsoluteModuleStrategy(program, checker, moduleResolver, reflectionHost), new LogicalProjectStrategy(reflectionHost, logicalFs), ]); const ctx = new TypeCheckContext(ALL_ENABLED_CONFIG, emitter, _('/_typecheck_.ts')); diff --git a/packages/compiler-cli/src/ngtsc/util/src/typescript.ts b/packages/compiler-cli/src/ngtsc/util/src/typescript.ts index 67708ee461..0eedf3cfbc 100644 --- a/packages/compiler-cli/src/ngtsc/util/src/typescript.ts +++ b/packages/compiler-cli/src/ngtsc/util/src/typescript.ts @@ -111,14 +111,18 @@ export function nodeDebugInfo(node: ts.Node): string { */ export function resolveModuleName( moduleName: string, containingFile: string, compilerOptions: ts.CompilerOptions, - compilerHost: ts.CompilerHost): ts.ResolvedModule|undefined { + compilerHost: ts.CompilerHost, + moduleResolutionCache: ts.ModuleResolutionCache | null): ts.ResolvedModule|undefined { if (compilerHost.resolveModuleNames) { // FIXME: Additional parameters are required in TS3.6, but ignored in 3.5. // Remove the any cast once google3 is fully on TS3.6. return (compilerHost as any) .resolveModuleNames([moduleName], containingFile, undefined, undefined, compilerOptions)[0]; } else { - return ts.resolveModuleName(moduleName, containingFile, compilerOptions, compilerHost) + return ts + .resolveModuleName( + moduleName, containingFile, compilerOptions, compilerHost, + moduleResolutionCache !== null ? moduleResolutionCache : undefined) .resolvedModule; } }