diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index 1b0a579180270..9e159a356c7c7 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -45,7 +45,7 @@ namespace ts { } /** Adds `isExernalLibraryImport` to a Resolved to get a ResolvedModule. */ - function resolvedModuleFromResolved({ path, extension }: Resolved, isExternalLibraryImport: boolean): ResolvedModule { + function resolvedModuleFromResolved({ path, extension }: Resolved, isExternalLibraryImport: boolean): ResolvedModuleFull { return { resolvedFileName: path, extension, isExternalLibraryImport }; } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 6b886a620696b..ec48e9471cdd0 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -329,16 +329,16 @@ namespace ts { // Map storing if there is emit blocking diagnostics for given input const hasEmitBlockingDiagnostics = createFileMap(getCanonicalFileName); - let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string) => ResolvedModule[]; + let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string) => ResolvedModuleFull[]; if (host.resolveModuleNames) { resolveModuleNamesWorker = (moduleNames, containingFile) => host.resolveModuleNames(moduleNames, containingFile).map(resolved => { // An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName. - if (!resolved || resolved.extension !== undefined) { - return resolved; + if (!resolved || (resolved as ResolvedModuleFull).extension !== undefined) { + return resolved as ResolvedModuleFull; } - resolved = clone(resolved); - resolved.extension = extensionFromPath(resolved.resolvedFileName); - return resolved; + const withExtension = clone(resolved) as ResolvedModuleFull; + withExtension.extension = extensionFromPath(resolved.resolvedFileName); + return withExtension; }); } else { @@ -1294,7 +1294,7 @@ namespace ts { function processImportedModules(file: SourceFile) { collectExternalModuleReferences(file); if (file.imports.length || file.moduleAugmentations.length) { - file.resolvedModules = createMap(); + file.resolvedModules = createMap(); const moduleNames = map(concatenate(file.imports, file.moduleAugmentations), getTextOfLiteral); const resolutions = resolveModuleNamesWorker(moduleNames, getNormalizedAbsolutePath(file.fileName, currentDirectory)); Debug.assert(resolutions.length === moduleNames.length); @@ -1573,7 +1573,7 @@ namespace ts { * The DiagnosticMessage's parameters are the imported module name, and the filename it resolved to. * This returns a diagnostic even if the module will be an untyped module. */ - export function getResolutionDiagnostic(options: CompilerOptions, { extension }: ResolvedModule): DiagnosticMessage | undefined { + export function getResolutionDiagnostic(options: CompilerOptions, { extension }: ResolvedModuleFull): DiagnosticMessage | undefined { switch (extension) { case Extension.Ts: case Extension.Dts: diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 9034ca32e4e91..419d062bb7f9e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -423,7 +423,7 @@ namespace ts { ThisNodeHasError = 1 << 19, // If the parser encountered an error when parsing the code that created this node JavaScriptFile = 1 << 20, // If node was parsed in a JavaScript ThisNodeOrAnySubNodesHasError = 1 << 21, // If this node or any of its children had an error - HasAggregatedChildData = 1 << 22, // If we've computed data from children and cached it in this node + HasAggregatedChildData = 1 << 22, // If we've computed data from children and cached it in this node BlockScoped = Let | Const, @@ -2083,7 +2083,7 @@ namespace ts { // Stores a mapping 'external module reference text' -> 'resolved file name' | undefined // It is used to resolve module names in the checker. // Content of this field should never be used directly - use getResolvedModuleFileName/setResolvedModuleFileName functions instead - /* @internal */ resolvedModules: Map; + /* @internal */ resolvedModules: Map; /* @internal */ resolvedTypeReferenceDirectiveNames: Map; /* @internal */ imports: LiteralExpression[]; /* @internal */ moduleAugmentations: LiteralExpression[]; @@ -3352,14 +3352,11 @@ namespace ts { * Module resolution will pick up tsx/jsx/js files even if '--jsx' and '--allowJs' are turned off. * The Program will then filter results based on these flags. * - * At least one of `resolvedTsFileName` or `resolvedJsFileName` must be defined, - * else resolution should just return `undefined` instead of a ResolvedModule. + * Prefer to return a `ResolvedModuleFull` so that the file type does not have to be inferred. */ export interface ResolvedModule { /** Path of the file the module was resolved to. */ resolvedFileName: string; - /** Extension of resolvedFileName. This must match what's at the end of resolvedFileName. */ - extension: Extension; /** * Denotes if 'resolvedFileName' is isExternalLibraryImport and thus should be a proper external module: * - be a .d.ts file @@ -3369,6 +3366,18 @@ namespace ts { isExternalLibraryImport?: boolean; } + /** + * ResolvedModule with an explicitly provided `extension` property. + * Prefer this over `ResolvedModule`. + */ + export interface ResolvedModuleFull extends ResolvedModule { + /** + * Extension of resolvedFileName. This must match what's at the end of resolvedFileName. + * This is optional for backwards-compatibility, but will be added if not provided. + */ + extension: Extension; + } + export enum Extension { Ts, Tsx, @@ -3379,7 +3388,7 @@ namespace ts { } export interface ResolvedModuleWithFailedLookupLocations { - resolvedModule: ResolvedModule | undefined; + resolvedModule: ResolvedModuleFull | undefined; failedLookupLocations: string[]; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index bbffdd470d790..f86a87e19eef4 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -87,13 +87,13 @@ namespace ts { return !!(sourceFile && sourceFile.resolvedModules && sourceFile.resolvedModules[moduleNameText]); } - export function getResolvedModule(sourceFile: SourceFile, moduleNameText: string): ResolvedModule { + export function getResolvedModule(sourceFile: SourceFile, moduleNameText: string): ResolvedModuleFull { return hasResolvedModule(sourceFile, moduleNameText) ? sourceFile.resolvedModules[moduleNameText] : undefined; } - export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string, resolvedModule: ResolvedModule): void { + export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string, resolvedModule: ResolvedModuleFull): void { if (!sourceFile.resolvedModules) { - sourceFile.resolvedModules = createMap(); + sourceFile.resolvedModules = createMap(); } sourceFile.resolvedModules[moduleNameText] = resolvedModule; @@ -108,11 +108,7 @@ namespace ts { } /* @internal */ - /** - * Considers two ResolvedModules equal if they have the same `resolvedFileName`. - * Thus `{ ts: foo, js: bar }` is equal to `{ ts: foo, js: baz }` because `ts` is preferred. - */ - export function moduleResolutionIsEqualTo(oldResolution: ResolvedModule, newResolution: ResolvedModule): boolean { + export function moduleResolutionIsEqualTo(oldResolution: ResolvedModuleFull, newResolution: ResolvedModuleFull): boolean { return oldResolution.isExternalLibraryImport === newResolution.isExternalLibraryImport && oldResolution.extension === newResolution.extension && oldResolution.resolvedFileName === newResolution.resolvedFileName; diff --git a/src/harness/unittests/moduleResolution.ts b/src/harness/unittests/moduleResolution.ts index 0c1934bb01c24..104d9f9e394eb 100644 --- a/src/harness/unittests/moduleResolution.ts +++ b/src/harness/unittests/moduleResolution.ts @@ -1,7 +1,7 @@ /// namespace ts { - export function checkResolvedModule(expected: ResolvedModule, actual: ResolvedModule): boolean { + export function checkResolvedModule(expected: ResolvedModuleFull, actual: ResolvedModuleFull): boolean { if (!expected === !actual) { if (expected) { assert.isTrue(expected.resolvedFileName === actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`); @@ -13,13 +13,13 @@ namespace ts { return false; } - export function checkResolvedModuleWithFailedLookupLocations(actual: ResolvedModuleWithFailedLookupLocations, expectedResolvedModule: ResolvedModule, expectedFailedLookupLocations: string[]): void { + export function checkResolvedModuleWithFailedLookupLocations(actual: ResolvedModuleWithFailedLookupLocations, expectedResolvedModule: ResolvedModuleFull, expectedFailedLookupLocations: string[]): void { assert.isTrue(actual.resolvedModule !== undefined, "module should be resolved"); checkResolvedModule(actual.resolvedModule, expectedResolvedModule); assert.deepEqual(actual.failedLookupLocations, expectedFailedLookupLocations); } - export function createResolvedModule(resolvedFileName: string, isExternalLibraryImport = false): ResolvedModule { + export function createResolvedModule(resolvedFileName: string, isExternalLibraryImport = false): ResolvedModuleFull { return { resolvedFileName, extension: extensionFromPath(resolvedFileName), isExternalLibraryImport }; } diff --git a/src/server/lsHost.ts b/src/server/lsHost.ts index 641f8dbabe742..628de71bb7a91 100644 --- a/src/server/lsHost.ts +++ b/src/server/lsHost.ts @@ -151,7 +151,7 @@ namespace ts.server { m => m.resolvedTypeReferenceDirective, r => r.resolvedFileName, /*logChanges*/ false); } - resolveModuleNames(moduleNames: string[], containingFile: string): ResolvedModule[] { + resolveModuleNames(moduleNames: string[], containingFile: string): ResolvedModuleFull[] { return this.resolveNamesWithLocalCache(moduleNames, containingFile, this.resolvedModuleNames, this.resolveModuleName, m => m.resolvedModule, r => r.resolvedFileName, /*logChanges*/ true); } diff --git a/src/services/services.ts b/src/services/services.ts index 5669134d37e75..8c11c707e5d60 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -467,7 +467,7 @@ namespace ts { public languageVariant: LanguageVariant; public identifiers: Map; public nameTable: Map; - public resolvedModules: Map; + public resolvedModules: Map; public resolvedTypeReferenceDirectiveNames: Map; public imports: LiteralExpression[]; public moduleAugmentations: LiteralExpression[]; diff --git a/src/services/shims.ts b/src/services/shims.ts index a3b99f81158c9..b1fac14674c32 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -316,7 +316,7 @@ namespace ts { private loggingEnabled = false; private tracingEnabled = false; - public resolveModuleNames: (moduleName: string[], containingFile: string) => ResolvedModule[]; + public resolveModuleNames: (moduleName: string[], containingFile: string) => ResolvedModuleFull[]; public resolveTypeReferenceDirectives: (typeDirectiveNames: string[], containingFile: string) => ResolvedTypeReferenceDirective[]; public directoryExists: (directoryName: string) => boolean;