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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 0 additions & 4 deletions
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

Lines changed: 27 additions & 12 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 12 additions & 7 deletions
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

Lines changed: 3 additions & 3 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 1 deletion
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",

0 commit comments

Comments
 (0)