Skip to content

Commit 14da064

Browse files
committed
Use more nodelike paths for import types when possible (microsoft#24610)
* Use more nodelike paths for import types when possible * move functionality from services into compiler, fix with propert file/directory conflict handling * mark suspect cast
1 parent 0db07e5 commit 14da064

19 files changed

+235
-41
lines changed

src/compiler/checker.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -4092,7 +4092,8 @@ namespace ts {
40924092
// ambient module, just use declaration/symbol name (fallthrough)
40934093
}
40944094
else {
4095-
return `"${getResolvedExternalModuleName(context.tracker.moduleResolverHost, file, getSourceFileOfNode(getOriginalNode(context.enclosingDeclaration)))}"`;
4095+
const contextFile = getSourceFileOfNode(getOriginalNode(context!.enclosingDeclaration))!;
4096+
return `"${file.moduleName || moduleSpecifiers.getModuleSpecifier(compilerOptions, contextFile, contextFile.path, file.path, context!.tracker.moduleResolverHost!)}"`;
40964097
}
40974098
}
40984099
const declaration = symbol.declarations[0];

src/compiler/moduleNameResolver.ts

-4
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,6 @@ namespace ts {
132132
}
133133
}
134134

135-
export interface GetEffectiveTypeRootsHost {
136-
directoryExists?(directoryName: string): boolean;
137-
getCurrentDirectory?(): string;
138-
}
139135
export function getEffectiveTypeRoots(options: CompilerOptions, host: GetEffectiveTypeRootsHost): string[] | undefined {
140136
if (options.typeRoots) {
141137
return options.typeRoots;

src/services/codefixes/moduleSpecifiers.ts renamed to src/compiler/moduleSpecifiers.ts

+27-12
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
// Used by importFixes to synthesize import module specifiers.
22
/* @internal */
33
namespace ts.moduleSpecifiers {
4+
export interface ModuleSpecifierPreferences {
5+
importModuleSpecifierPreference?: "relative" | "non-relative";
6+
}
7+
48
// Note: fromSourceFile is just for usesJsExtensionOnImports
5-
export function getModuleSpecifier(program: Program, fromSourceFile: SourceFile, fromSourceFileName: string, toFileName: string, host: LanguageServiceHost, preferences: UserPreferences) {
6-
const info = getInfo(program.getCompilerOptions(), fromSourceFile, fromSourceFileName, host);
7-
const compilerOptions = program.getCompilerOptions();
9+
export function getModuleSpecifier(compilerOptions: CompilerOptions, fromSourceFile: SourceFile, fromSourceFileName: string, toFileName: string, host: ModuleSpecifierResolutionHost, preferences: ModuleSpecifierPreferences = {}) {
10+
const info = getInfo(compilerOptions, fromSourceFile, fromSourceFileName, host);
811
return getGlobalModuleSpecifier(toFileName, info, host, compilerOptions) ||
912
first(getLocalModuleSpecifiers(toFileName, info, compilerOptions, preferences));
1013
}
@@ -14,15 +17,15 @@ namespace ts.moduleSpecifiers {
1417
moduleSymbol: Symbol,
1518
program: Program,
1619
importingSourceFile: SourceFile,
17-
host: LanguageServiceHost,
18-
preferences: UserPreferences,
20+
host: ModuleSpecifierResolutionHost,
21+
preferences: ModuleSpecifierPreferences,
1922
): ReadonlyArray<ReadonlyArray<string>> {
2023
const ambient = tryGetModuleNameFromAmbientModule(moduleSymbol);
2124
if (ambient) return [[ambient]];
2225

2326
const compilerOptions = program.getCompilerOptions();
2427
const info = getInfo(compilerOptions, importingSourceFile, importingSourceFile.fileName, host);
25-
const modulePaths = getAllModulePaths(program, moduleSymbol.valueDeclaration.getSourceFile());
28+
const modulePaths = getAllModulePaths(program, getSourceFileOfNode(moduleSymbol.valueDeclaration));
2629

2730
const global = mapDefined(modulePaths, moduleFileName => getGlobalModuleSpecifier(moduleFileName, info, host, compilerOptions));
2831
return global.length ? global.map(g => [g]) : modulePaths.map(moduleFileName =>
@@ -36,18 +39,18 @@ namespace ts.moduleSpecifiers {
3639
readonly sourceDirectory: string;
3740
}
3841
// importingSourceFileName is separate because getEditsForFileRename may need to specify an updated path
39-
function getInfo(compilerOptions: CompilerOptions, importingSourceFile: SourceFile, importingSourceFileName: string, host: LanguageServiceHost): Info {
42+
function getInfo(compilerOptions: CompilerOptions, importingSourceFile: SourceFile, importingSourceFileName: string, host: ModuleSpecifierResolutionHost): Info {
4043
const moduleResolutionKind = getEmitModuleResolutionKind(compilerOptions);
4144
const addJsExtension = usesJsExtensionOnImports(importingSourceFile);
42-
const getCanonicalFileName = hostGetCanonicalFileName(host);
45+
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames ? host.useCaseSensitiveFileNames() : true);
4346
const sourceDirectory = getDirectoryPath(importingSourceFileName);
4447
return { moduleResolutionKind, addJsExtension, getCanonicalFileName, sourceDirectory };
4548
}
4649

4750
function getGlobalModuleSpecifier(
4851
moduleFileName: string,
4952
{ addJsExtension, getCanonicalFileName, sourceDirectory }: Info,
50-
host: LanguageServiceHost,
53+
host: ModuleSpecifierResolutionHost,
5154
compilerOptions: CompilerOptions,
5255
) {
5356
return tryGetModuleNameFromTypeRoots(compilerOptions, host, getCanonicalFileName, moduleFileName, addJsExtension)
@@ -59,7 +62,7 @@ namespace ts.moduleSpecifiers {
5962
moduleFileName: string,
6063
{ moduleResolutionKind, addJsExtension, getCanonicalFileName, sourceDirectory }: Info,
6164
compilerOptions: CompilerOptions,
62-
preferences: UserPreferences,
65+
preferences: ModuleSpecifierPreferences,
6366
) {
6467
const { baseUrl, paths } = compilerOptions;
6568

@@ -210,7 +213,7 @@ namespace ts.moduleSpecifiers {
210213
function tryGetModuleNameAsNodeModule(
211214
options: CompilerOptions,
212215
moduleFileName: string,
213-
host: LanguageServiceHost,
216+
host: ModuleSpecifierResolutionHost,
214217
getCanonicalFileName: (file: string) => string,
215218
sourceDirectory: string,
216219
): string | undefined {
@@ -255,7 +258,8 @@ namespace ts.moduleSpecifiers {
255258
const fullModulePathWithoutExtension = removeFileExtension(path);
256259

257260
// If the file is /index, it can be imported by its directory name
258-
if (getCanonicalFileName(fullModulePathWithoutExtension.substring(parts.fileNameIndex)) === "/index") {
261+
// IFF there is not _also_ a file by the same name
262+
if (getCanonicalFileName(fullModulePathWithoutExtension.substring(parts.fileNameIndex)) === "/index" && !tryGetAnyFileFromPath(host, fullModulePathWithoutExtension.substring(0, parts.fileNameIndex))) {
259263
return fullModulePathWithoutExtension.substring(0, parts.fileNameIndex);
260264
}
261265

@@ -274,6 +278,17 @@ namespace ts.moduleSpecifiers {
274278
}
275279
}
276280

281+
function tryGetAnyFileFromPath(host: ModuleSpecifierResolutionHost, path: string) {
282+
// We check all js, `node` and `json` extensions in addition to TS, since node module resolution would also choose those over the directory
283+
const extensions = getSupportedExtensions({ allowJs: true }, [{ extension: "node", isMixedContent: false }, { extension: "json", isMixedContent: false, scriptKind: ScriptKind.JSON }]);
284+
for (const e of extensions) {
285+
const fullPath = path + e;
286+
if (host.fileExists!(fullPath)) { // TODO: GH#18217
287+
return fullPath;
288+
}
289+
}
290+
}
291+
277292
function getNodeModulePathParts(fullPath: string) {
278293
// If fullPath can't be valid module file within node_modules, returns undefined.
279294
// Example of expected pattern: /base/path/node_modules/[@scope/otherpackage/@otherscope/node_modules/]package/[subdirectory/]file.js

src/compiler/program.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1168,6 +1168,9 @@ namespace ts {
11681168
writeFile: writeFileCallback || (
11691169
(fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)),
11701170
isEmitBlocked,
1171+
readFile: f => host.readFile(f),
1172+
fileExists: f => host.fileExists(f),
1173+
...(host.directoryExists ? { directoryExists: f => host.directoryExists!(f) } : {}),
11711174
};
11721175
}
11731176

src/compiler/tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"builderState.ts",
4545
"builder.ts",
4646
"resolutionCache.ts",
47+
"moduleSpecifiers.ts",
4748
"watch.ts",
4849
"commandLineParser.ts",
4950
"tsc.ts"

src/compiler/types.ts

+12-7
Original file line numberDiff line numberDiff line change
@@ -5011,8 +5011,9 @@ namespace ts {
50115011
}
50125012

50135013
/* @internal */
5014-
export interface EmitHost extends ScriptReferenceHost {
5014+
export interface EmitHost extends ScriptReferenceHost, ModuleSpecifierResolutionHost {
50155015
getSourceFiles(): ReadonlyArray<SourceFile>;
5016+
getCurrentDirectory(): string;
50165017

50175018
/* @internal */
50185019
isSourceFileFromExternalLibrary(file: SourceFile): boolean;
@@ -5274,11 +5275,15 @@ namespace ts {
52745275
isAtStartOfLine(): boolean;
52755276
}
52765277

5277-
/* @internal */
5278-
export interface ModuleNameResolverHost {
5279-
getCanonicalFileName(f: string): string;
5280-
getCommonSourceDirectory(): string;
5281-
getCurrentDirectory(): string;
5278+
export interface GetEffectiveTypeRootsHost {
5279+
directoryExists?(directoryName: string): boolean;
5280+
getCurrentDirectory?(): string;
5281+
}
5282+
/** @internal */
5283+
export interface ModuleSpecifierResolutionHost extends GetEffectiveTypeRootsHost {
5284+
useCaseSensitiveFileNames?(): boolean;
5285+
fileExists?(path: string): boolean;
5286+
readFile?(path: string): string | undefined;
52825287
}
52835288

52845289
/** @deprecated See comment on SymbolWriter */
@@ -5292,7 +5297,7 @@ namespace ts {
52925297
reportPrivateInBaseOfClassExpression?(propertyName: string): void;
52935298
reportInaccessibleUniqueSymbolError?(): void;
52945299
/* @internal */
5295-
moduleResolverHost?: ModuleNameResolverHost;
5300+
moduleResolverHost?: ModuleSpecifierResolutionHost;
52965301
/* @internal */
52975302
trackReferencedAmbientModule?(decl: ModuleDeclaration): void;
52985303
}

src/compiler/utilities.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -2893,11 +2893,11 @@ namespace ts {
28932893
};
28942894
}
28952895

2896-
export function getResolvedExternalModuleName(host: ModuleNameResolverHost, file: SourceFile, referenceFile?: SourceFile): string {
2896+
export function getResolvedExternalModuleName(host: EmitHost, file: SourceFile, referenceFile?: SourceFile): string {
28972897
return file.moduleName || getExternalModuleNameFromPath(host, file.fileName, referenceFile && referenceFile.fileName);
28982898
}
28992899

2900-
export function getExternalModuleNameFromDeclaration(host: ModuleNameResolverHost, resolver: EmitResolver, declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode): string {
2900+
export function getExternalModuleNameFromDeclaration(host: EmitHost, resolver: EmitResolver, declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode): string | undefined {
29012901
const file = resolver.getExternalModuleFileFromDeclaration(declaration);
29022902
if (!file || file.isDeclarationFile) {
29032903
return undefined;
@@ -2908,7 +2908,7 @@ namespace ts {
29082908
/**
29092909
* Resolves a local path to a path which is absolute to the base of the emit
29102910
*/
2911-
export function getExternalModuleNameFromPath(host: ModuleNameResolverHost, fileName: string, referencePath?: string): string {
2911+
export function getExternalModuleNameFromPath(host: EmitHost, fileName: string, referencePath?: string): string {
29122912
const getCanonicalFileName = (f: string) => host.getCanonicalFileName(f);
29132913
const dir = toPath(referencePath ? getDirectoryPath(referencePath) : host.getCommonSourceDirectory(), host.getCurrentDirectory(), getCanonicalFileName);
29142914
const filePath = getNormalizedAbsolutePath(fileName, host.getCurrentDirectory());

src/harness/tsconfig.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"../compiler/builderState.ts",
5252
"../compiler/builder.ts",
5353
"../compiler/resolutionCache.ts",
54+
"../compiler/moduleSpecifiers.ts",
5455
"../compiler/watch.ts",
5556
"../compiler/commandLineParser.ts",
5657

@@ -115,7 +116,6 @@
115116
"../services/codefixes/inferFromUsage.ts",
116117
"../services/codefixes/fixInvalidImportSyntax.ts",
117118
"../services/codefixes/fixStrictClassInitialization.ts",
118-
"../services/codefixes/moduleSpecifiers.ts",
119119
"../services/codefixes/requireInTs.ts",
120120
"../services/codefixes/useDefaultImport.ts",
121121
"../services/codefixes/fixAddModuleReferTypeMissingTypeof.ts",

src/server/tsconfig.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"../compiler/builderState.ts",
4848
"../compiler/builder.ts",
4949
"../compiler/resolutionCache.ts",
50+
"../compiler/moduleSpecifiers.ts",
5051
"../compiler/watch.ts",
5152
"../compiler/commandLineParser.ts",
5253

@@ -111,7 +112,6 @@
111112
"../services/codefixes/inferFromUsage.ts",
112113
"../services/codefixes/fixInvalidImportSyntax.ts",
113114
"../services/codefixes/fixStrictClassInitialization.ts",
114-
"../services/codefixes/moduleSpecifiers.ts",
115115
"../services/codefixes/requireInTs.ts",
116116
"../services/codefixes/useDefaultImport.ts",
117117
"../services/codefixes/fixAddModuleReferTypeMissingTypeof.ts",

src/server/tsconfig.library.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"../compiler/builderState.ts",
5454
"../compiler/builder.ts",
5555
"../compiler/resolutionCache.ts",
56+
"../compiler/moduleSpecifiers.ts",
5657
"../compiler/watch.ts",
5758
"../compiler/commandLineParser.ts",
5859

@@ -117,7 +118,6 @@
117118
"../services/codefixes/inferFromUsage.ts",
118119
"../services/codefixes/fixInvalidImportSyntax.ts",
119120
"../services/codefixes/fixStrictClassInitialization.ts",
120-
"../services/codefixes/moduleSpecifiers.ts",
121121
"../services/codefixes/requireInTs.ts",
122122
"../services/codefixes/useDefaultImport.ts",
123123
"../services/codefixes/fixAddModuleReferTypeMissingTypeof.ts",

src/services/getEditsForFileRename.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ namespace ts {
123123
// TODO:GH#18217
124124
? getSourceFileToImportFromResolved(resolveModuleName(importLiteral.text, oldImportFromPath, program.getCompilerOptions(), host as ModuleResolutionHost), oldToNew, program)
125125
: getSourceFileToImport(importLiteral, sourceFile, program, host, oldToNew);
126-
return toImport === undefined ? undefined : moduleSpecifiers.getModuleSpecifier(program, sourceFile, newImportFromPath, toImport, host, preferences);
126+
return toImport === undefined ? undefined : moduleSpecifiers.getModuleSpecifier(program.getCompilerOptions(), sourceFile, newImportFromPath, toImport, host, preferences);
127127
});
128128
}
129129
}

src/services/tsconfig.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"../compiler/builderState.ts",
4545
"../compiler/builder.ts",
4646
"../compiler/resolutionCache.ts",
47+
"../compiler/moduleSpecifiers.ts",
4748
"../compiler/watch.ts",
4849
"../compiler/commandLineParser.ts",
4950

@@ -108,7 +109,6 @@
108109
"codefixes/inferFromUsage.ts",
109110
"codefixes/fixInvalidImportSyntax.ts",
110111
"codefixes/fixStrictClassInitialization.ts",
111-
"codefixes/moduleSpecifiers.ts",
112112
"codefixes/requireInTs.ts",
113113
"codefixes/useDefaultImport.ts",
114114
"codefixes/fixAddModuleReferTypeMissingTypeof.ts",

tests/baselines/reference/api/tsserverlibrary.d.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -2876,6 +2876,10 @@ declare namespace ts {
28762876
omitTrailingSemicolon?: boolean;
28772877
noEmitHelpers?: boolean;
28782878
}
2879+
interface GetEffectiveTypeRootsHost {
2880+
directoryExists?(directoryName: string): boolean;
2881+
getCurrentDirectory?(): string;
2882+
}
28792883
/** @deprecated See comment on SymbolWriter */
28802884
interface SymbolTracker {
28812885
trackSymbol?(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): void;
@@ -3463,10 +3467,6 @@ declare namespace ts {
34633467
function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile;
34643468
}
34653469
declare namespace ts {
3466-
interface GetEffectiveTypeRootsHost {
3467-
directoryExists?(directoryName: string): boolean;
3468-
getCurrentDirectory?(): string;
3469-
}
34703470
function getEffectiveTypeRoots(options: CompilerOptions, host: GetEffectiveTypeRootsHost): string[] | undefined;
34713471
/**
34723472
* @param {string | undefined} containingFile - file that contains type reference directive, can be undefined if containing file is unknown.

tests/baselines/reference/api/typescript.d.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -2876,6 +2876,10 @@ declare namespace ts {
28762876
omitTrailingSemicolon?: boolean;
28772877
noEmitHelpers?: boolean;
28782878
}
2879+
interface GetEffectiveTypeRootsHost {
2880+
directoryExists?(directoryName: string): boolean;
2881+
getCurrentDirectory?(): string;
2882+
}
28792883
/** @deprecated See comment on SymbolWriter */
28802884
interface SymbolTracker {
28812885
trackSymbol?(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): void;
@@ -3463,10 +3467,6 @@ declare namespace ts {
34633467
function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile;
34643468
}
34653469
declare namespace ts {
3466-
interface GetEffectiveTypeRootsHost {
3467-
directoryExists?(directoryName: string): boolean;
3468-
getCurrentDirectory?(): string;
3469-
}
34703470
function getEffectiveTypeRoots(options: CompilerOptions, host: GetEffectiveTypeRootsHost): string[] | undefined;
34713471
/**
34723472
* @param {string | undefined} containingFile - file that contains type reference directive, can be undefined if containing file is unknown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//// [tests/cases/compiler/declarationEmitCommonJsModuleReferencedType.ts] ////
2+
3+
//// [index.d.ts]
4+
export interface NestedProps {}
5+
//// [index.d.ts]
6+
export interface OtherIndexProps {}
7+
//// [other.d.ts]
8+
export interface OtherProps {}
9+
//// [index.d.ts]
10+
import { OtherProps } from "./other";
11+
import { OtherIndexProps } from "./other/index";
12+
import { NestedProps } from "nested";
13+
export interface SomeProps {}
14+
15+
export function foo(): [SomeProps, OtherProps, OtherIndexProps, NestedProps];
16+
//// [index.d.ts]
17+
export interface RootProps {}
18+
19+
export function bar(): RootProps;
20+
//// [entry.ts]
21+
import { foo } from "foo";
22+
import { bar } from "root";
23+
export const x = foo();
24+
export const y = bar();
25+
26+
27+
//// [entry.js]
28+
"use strict";
29+
exports.__esModule = true;
30+
var foo_1 = require("foo");
31+
var root_1 = require("root");
32+
exports.x = foo_1.foo();
33+
exports.y = root_1.bar();
34+
35+
36+
//// [entry.d.ts]
37+
export declare const x: [import("foo").SomeProps, import("foo/other").OtherProps, import("foo/other/index").OtherIndexProps, import("foo/node_modules/nested").NestedProps];
38+
export declare const y: import("root").RootProps;

0 commit comments

Comments
 (0)