Skip to content

Commit d9b9390

Browse files
authored
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 735a46f commit d9b9390

19 files changed

Lines changed: 235 additions & 41 deletions

src/compiler/checker.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4084,7 +4084,8 @@ namespace ts {
40844084
// ambient module, just use declaration/symbol name (fallthrough)
40854085
}
40864086
else {
4087-
return `"${getResolvedExternalModuleName(context!.tracker.moduleResolverHost!, file, getSourceFileOfNode(getOriginalNode(context!.enclosingDeclaration)))}"`;
4087+
const contextFile = getSourceFileOfNode(getOriginalNode(context!.enclosingDeclaration))!;
4088+
return `"${file.moduleName || moduleSpecifiers.getModuleSpecifier(compilerOptions, contextFile, contextFile.path, file.path, context!.tracker.moduleResolverHost!)}"`;
40884089
}
40894090
}
40904091
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 {
@@ -256,14 +259,26 @@ namespace ts.moduleSpecifiers {
256259
const fullModulePathWithoutExtension = removeFileExtension(path);
257260

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

263267
return fullModulePathWithoutExtension;
264268
}
265269
}
266270

271+
function tryGetAnyFileFromPath(host: ModuleSpecifierResolutionHost, path: string) {
272+
// We check all js, `node` and `json` extensions in addition to TS, since node module resolution would also choose those over the directory
273+
const extensions = getSupportedExtensions({ allowJs: true }, [{ extension: "node", isMixedContent: false }, { extension: "json", isMixedContent: false, scriptKind: ScriptKind.JSON }]);
274+
for (const e of extensions) {
275+
const fullPath = path + e;
276+
if (host.fileExists!(fullPath)) { // TODO: GH#18217
277+
return fullPath;
278+
}
279+
}
280+
}
281+
267282
interface NodeModulePathParts {
268283
readonly topLevelNodeModulesIndex: number;
269284
readonly topLevelPackageNameIndex: number;

src/compiler/program.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,9 @@ namespace ts {
12071207
writeFile: writeFileCallback || (
12081208
(fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)),
12091209
isEmitBlocked,
1210+
readFile: f => host.readFile(f),
1211+
fileExists: f => host.fileExists(f),
1212+
...(host.directoryExists ? { directoryExists: f => host.directoryExists!(f) } : {}),
12101213
};
12111214
}
12121215

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
@@ -5026,8 +5026,9 @@ namespace ts {
50265026
}
50275027

50285028
/* @internal */
5029-
export interface EmitHost extends ScriptReferenceHost {
5029+
export interface EmitHost extends ScriptReferenceHost, ModuleSpecifierResolutionHost {
50305030
getSourceFiles(): ReadonlyArray<SourceFile>;
5031+
getCurrentDirectory(): string;
50315032

50325033
/* @internal */
50335034
isSourceFileFromExternalLibrary(file: SourceFile): boolean;
@@ -5289,11 +5290,15 @@ namespace ts {
52895290
isAtStartOfLine(): boolean;
52905291
}
52915292

5292-
/* @internal */
5293-
export interface ModuleNameResolverHost {
5294-
getCanonicalFileName(f: string): string;
5295-
getCommonSourceDirectory(): string;
5296-
getCurrentDirectory(): string;
5293+
export interface GetEffectiveTypeRootsHost {
5294+
directoryExists?(directoryName: string): boolean;
5295+
getCurrentDirectory?(): string;
5296+
}
5297+
/** @internal */
5298+
export interface ModuleSpecifierResolutionHost extends GetEffectiveTypeRootsHost {
5299+
useCaseSensitiveFileNames?(): boolean;
5300+
fileExists?(path: string): boolean;
5301+
readFile?(path: string): string | undefined;
52975302
}
52985303

52995304
/** @deprecated See comment on SymbolWriter */
@@ -5307,7 +5312,7 @@ namespace ts {
53075312
reportPrivateInBaseOfClassExpression?(propertyName: string): void;
53085313
reportInaccessibleUniqueSymbolError?(): void;
53095314
/* @internal */
5310-
moduleResolverHost?: ModuleNameResolverHost;
5315+
moduleResolverHost?: ModuleSpecifierResolutionHost;
53115316
/* @internal */
53125317
trackReferencedAmbientModule?(decl: ModuleDeclaration): void;
53135318
}

src/compiler/utilities.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2934,11 +2934,11 @@ namespace ts {
29342934
};
29352935
}
29362936

2937-
export function getResolvedExternalModuleName(host: ModuleNameResolverHost, file: SourceFile, referenceFile?: SourceFile): string {
2937+
export function getResolvedExternalModuleName(host: EmitHost, file: SourceFile, referenceFile?: SourceFile): string {
29382938
return file.moduleName || getExternalModuleNameFromPath(host, file.fileName, referenceFile && referenceFile.fileName);
29392939
}
29402940

2941-
export function getExternalModuleNameFromDeclaration(host: ModuleNameResolverHost, resolver: EmitResolver, declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode): string | undefined {
2941+
export function getExternalModuleNameFromDeclaration(host: EmitHost, resolver: EmitResolver, declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode): string | undefined {
29422942
const file = resolver.getExternalModuleFileFromDeclaration(declaration);
29432943
if (!file || file.isDeclarationFile) {
29442944
return undefined;
@@ -2949,7 +2949,7 @@ namespace ts {
29492949
/**
29502950
* Resolves a local path to a path which is absolute to the base of the emit
29512951
*/
2952-
export function getExternalModuleNameFromPath(host: ModuleNameResolverHost, fileName: string, referencePath?: string): string {
2952+
export function getExternalModuleNameFromPath(host: EmitHost, fileName: string, referencePath?: string): string {
29532953
const getCanonicalFileName = (f: string) => host.getCanonicalFileName(f);
29542954
const dir = toPath(referencePath ? getDirectoryPath(referencePath) : host.getCommonSourceDirectory(), host.getCurrentDirectory(), getCanonicalFileName);
29552955
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)