Skip to content

Commit 052d1a2

Browse files
committed
move functionality from services into compiler, fix with propert file/directory conflict handling
1 parent 19820ca commit 052d1a2

15 files changed

+63
-68
lines changed

src/compiler/checker.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -4085,7 +4085,8 @@ namespace ts {
40854085
// ambient module, just use declaration/symbol name (fallthrough)
40864086
}
40874087
else {
4088-
return `"${getResolvedExternalModuleNameForPossiblyExternalModule(context!.tracker.moduleResolverHost!, file, getSourceFileOfNode(getOriginalNode(context!.enclosingDeclaration)))}"`;
4088+
const contextFile = getSourceFileOfNode(getOriginalNode(context!.enclosingDeclaration))!;
4089+
return `"${moduleSpecifiers.getModuleSpecifier(compilerOptions, contextFile, contextFile.path, file.path, context!.tracker.moduleResolverHost!)}"`;
40894090
}
40904091
}
40914092
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 {
@@ -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)) {
277+
return fullPath;
278+
}
279+
}
280+
}
281+
267282
interface NodeModulePathParts {
268283
readonly topLevelNodeModulesIndex: number;
269284
readonly topLevelPackageNameIndex: number;

src/compiler/program.ts

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

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-8
Original file line numberDiff line numberDiff line change
@@ -5019,8 +5019,9 @@ namespace ts {
50195019
}
50205020

50215021
/* @internal */
5022-
export interface EmitHost extends ScriptReferenceHost {
5022+
export interface EmitHost extends ScriptReferenceHost, ModuleSpecifierResolutionHost {
50235023
getSourceFiles(): ReadonlyArray<SourceFile>;
5024+
getCurrentDirectory(): string;
50245025

50255026
/* @internal */
50265027
isSourceFileFromExternalLibrary(file: SourceFile): boolean;
@@ -5282,12 +5283,15 @@ namespace ts {
52825283
isAtStartOfLine(): boolean;
52835284
}
52845285

5285-
/* @internal */
5286-
export interface ModuleNameResolverHost {
5287-
getCanonicalFileName(f: string): string;
5288-
getCommonSourceDirectory(): string;
5289-
getCurrentDirectory(): string;
5290-
getCompilerOptions(): CompilerOptions;
5286+
export interface GetEffectiveTypeRootsHost {
5287+
directoryExists?(directoryName: string): boolean;
5288+
getCurrentDirectory?(): string;
5289+
}
5290+
/** @internal */
5291+
export interface ModuleSpecifierResolutionHost extends GetEffectiveTypeRootsHost {
5292+
useCaseSensitiveFileNames?(): boolean;
5293+
fileExists?(path: string): boolean;
5294+
readFile?(path: string): string | undefined;
52915295
}
52925296

52935297
/** @deprecated See comment on SymbolWriter */
@@ -5301,7 +5305,7 @@ namespace ts {
53015305
reportPrivateInBaseOfClassExpression?(propertyName: string): void;
53025306
reportInaccessibleUniqueSymbolError?(): void;
53035307
/* @internal */
5304-
moduleResolverHost?: ModuleNameResolverHost;
5308+
moduleResolverHost?: ModuleSpecifierResolutionHost;
53055309
/* @internal */
53065310
trackReferencedAmbientModule?(decl: ModuleDeclaration): void;
53075311
}

src/compiler/utilities.ts

+3-28
Original file line numberDiff line numberDiff line change
@@ -2934,36 +2934,11 @@ namespace ts {
29342934
};
29352935
}
29362936

2937-
/** @internal */
2938-
export function getResolvedExternalModuleNameForPossiblyExternalModule(host: ModuleNameResolverHost, file: SourceFile, referenceFile?: SourceFile) {
2939-
const result = getResolvedExternalModuleName(host, file, referenceFile);
2940-
const opts = host.getCompilerOptions();
2941-
if (getEmitModuleResolutionKind(opts) === ModuleResolutionKind.NodeJs) {
2942-
// Trim leading paths to `node_modules` to allow node module resolution to find the thing in the future without an exact path
2943-
// This simplification means if a package.json for `foo` directs us to `foo/lib/main` instead of `foo/index` we'll write `foo/lib/main` over `foo`, however
2944-
const parts = getPathComponents(result);
2945-
if (parts[0] !== "") {
2946-
return result; // rooted path, leave as is
2947-
}
2948-
else {
2949-
parts.shift();
2950-
}
2951-
let index = 0;
2952-
while (parts[index] && (parts[index] === "." || parts[index] === "..")) {
2953-
index++;
2954-
}
2955-
if (parts[index] && parts[index] === "node_modules") {
2956-
return parts.slice(index + 1).join(directorySeparator);
2957-
}
2958-
}
2959-
return result;
2960-
}
2961-
2962-
export function getResolvedExternalModuleName(host: ModuleNameResolverHost, file: SourceFile, referenceFile?: SourceFile): string {
2937+
export function getResolvedExternalModuleName(host: EmitHost, file: SourceFile, referenceFile?: SourceFile): string {
29632938
return file.moduleName || getExternalModuleNameFromPath(host, file.fileName, referenceFile && referenceFile.fileName);
29642939
}
29652940

2966-
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 {
29672942
const file = resolver.getExternalModuleFileFromDeclaration(declaration);
29682943
if (!file || file.isDeclarationFile) {
29692944
return undefined;
@@ -2974,7 +2949,7 @@ namespace ts {
29742949
/**
29752950
* Resolves a local path to a path which is absolute to the base of the emit
29762951
*/
2977-
export function getExternalModuleNameFromPath(host: ModuleNameResolverHost, fileName: string, referencePath?: string): string {
2952+
export function getExternalModuleNameFromPath(host: EmitHost, fileName: string, referencePath?: string): string {
29782953
const getCanonicalFileName = (f: string) => host.getCanonicalFileName(f);
29792954
const dir = toPath(referencePath ? getDirectoryPath(referencePath) : host.getCommonSourceDirectory(), host.getCurrentDirectory(), getCanonicalFileName);
29802955
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;
@@ -3467,10 +3471,6 @@ declare namespace ts {
34673471
function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile;
34683472
}
34693473
declare namespace ts {
3470-
interface GetEffectiveTypeRootsHost {
3471-
directoryExists?(directoryName: string): boolean;
3472-
getCurrentDirectory?(): string;
3473-
}
34743474
function getEffectiveTypeRoots(options: CompilerOptions, host: GetEffectiveTypeRootsHost): string[] | undefined;
34753475
/**
34763476
* @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;
@@ -3467,10 +3471,6 @@ declare namespace ts {
34673471
function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile;
34683472
}
34693473
declare namespace ts {
3470-
interface GetEffectiveTypeRootsHost {
3471-
directoryExists?(directoryName: string): boolean;
3472-
getCurrentDirectory?(): string;
3473-
}
34743474
function getEffectiveTypeRoots(options: CompilerOptions, host: GetEffectiveTypeRootsHost): string[] | undefined;
34753475
/**
34763476
* @param {string | undefined} containingFile - file that contains type reference directive, can be undefined if containing file is unknown.

tests/baselines/reference/declarationEmitCommonJsModuleReferencedType.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,5 @@ exports.y = root_1.bar();
3434

3535

3636
//// [entry.d.ts]
37-
export declare const x: [import("foo/index").SomeProps, import("foo/other").OtherProps, import("foo/other/index").OtherIndexProps, import("foo/node_modules/nested/index").NestedProps];
38-
export declare const y: import("root/index").RootProps;
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)