Skip to content

Commit 7fc1cb4

Browse files
authored
Use realpathSync.native on case-insensitive file systems (#44966)
* Make getSourceOfProjectReferenceRedirect take a Path * Add useCaseSensitiveFileNames to ModuleResolutionHost ...so that path comparisons can use it during module resolution. * Re-enable realpathSync.native for case-insensitive file systems
1 parent 983ddf5 commit 7fc1cb4

File tree

10 files changed

+39
-28
lines changed

10 files changed

+39
-28
lines changed

src/compiler/moduleNameResolver.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,11 @@ namespace ts {
280280
}
281281
const nodeModulesAtTypes = combinePaths("node_modules", "@types");
282282

283+
function arePathsEqual(path1: string, path2: string, host: ModuleResolutionHost): boolean {
284+
const useCaseSensitiveFileNames = typeof host.useCaseSensitiveFileNames === "function" ? host.useCaseSensitiveFileNames() : host.useCaseSensitiveFileNames;
285+
return comparePaths(path1, path2, !useCaseSensitiveFileNames) === Comparison.EqualTo;
286+
}
287+
283288
/**
284289
* @param {string | undefined} containingFile - file that contains type reference directive, can be undefined if containing file is unknown.
285290
* This is possible in case if resolution is performed for directives specified via 'types' parameter. In this case initial path for secondary lookups
@@ -343,7 +348,7 @@ namespace ts {
343348
resolvedTypeReferenceDirective = {
344349
primary,
345350
resolvedFileName,
346-
originalPath: fileName === resolvedFileName ? undefined : fileName,
351+
originalPath: arePathsEqual(fileName, resolvedFileName, host) ? undefined : fileName,
347352
packageId,
348353
isExternalLibraryImport: pathContainsNodeModules(fileName),
349354
};
@@ -1122,7 +1127,7 @@ namespace ts {
11221127
let resolvedValue = resolved.value;
11231128
if (!compilerOptions.preserveSymlinks && resolvedValue && !resolvedValue.originalPath) {
11241129
const path = realPath(resolvedValue.path, host, traceEnabled);
1125-
const originalPath = path === resolvedValue.path ? undefined : resolvedValue.path;
1130+
const originalPath = arePathsEqual(path, resolvedValue.path, host) ? undefined : resolvedValue.path;
11261131
resolvedValue = { ...resolvedValue, path, originalPath };
11271132
}
11281133
// For node_modules lookups, get the real path so that multiple accesses to an `npm link`-ed module do not create duplicate files.

src/compiler/program.ts

+20-22
Original file line numberDiff line numberDiff line change
@@ -1163,21 +1163,20 @@ namespace ts {
11631163

11641164
// The originalFileName could not be actual source file name if file found was d.ts from referecned project
11651165
// So in this case try to look up if this is output from referenced project, if it is use the redirected project in that case
1166-
const resultFromDts = getRedirectReferenceForResolutionFromSourceOfProject(file.originalFileName, file.path);
1166+
const resultFromDts = getRedirectReferenceForResolutionFromSourceOfProject(file.path);
11671167
if (resultFromDts) return resultFromDts;
11681168

11691169
// If preserveSymlinks is true, module resolution wont jump the symlink
11701170
// but the resolved real path may be the .d.ts from project reference
11711171
// Note:: Currently we try the real path only if the
11721172
// file is from node_modules to avoid having to run real path on all file paths
11731173
if (!host.realpath || !options.preserveSymlinks || !stringContains(file.originalFileName, nodeModulesPathPart)) return undefined;
1174-
const realDeclarationFileName = host.realpath(file.originalFileName);
1175-
const realDeclarationPath = toPath(realDeclarationFileName);
1176-
return realDeclarationPath === file.path ? undefined : getRedirectReferenceForResolutionFromSourceOfProject(realDeclarationFileName, realDeclarationPath);
1174+
const realDeclarationPath = toPath(host.realpath(file.originalFileName));
1175+
return realDeclarationPath === file.path ? undefined : getRedirectReferenceForResolutionFromSourceOfProject(realDeclarationPath);
11771176
}
11781177

1179-
function getRedirectReferenceForResolutionFromSourceOfProject(fileName: string, filePath: Path) {
1180-
const source = getSourceOfProjectReferenceRedirect(fileName);
1178+
function getRedirectReferenceForResolutionFromSourceOfProject(filePath: Path) {
1179+
const source = getSourceOfProjectReferenceRedirect(filePath);
11811180
if (isString(source)) return getResolvedProjectReferenceToRedirect(source);
11821181
if (!source) return undefined;
11831182
// Output of .d.ts file so return resolved ref that matches the out file name
@@ -2472,7 +2471,7 @@ namespace ts {
24722471
function processSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, packageId: PackageId | undefined, reason: FileIncludeReason): void {
24732472
getSourceFileFromReferenceWorker(
24742473
fileName,
2475-
fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, ignoreNoDefaultLib, reason, packageId), // TODO: GH#18217
2474+
fileName => findSourceFile(fileName, isDefaultLib, ignoreNoDefaultLib, reason, packageId), // TODO: GH#18217
24762475
(diagnostic, ...args) => addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, diagnostic, args),
24772476
reason
24782477
);
@@ -2514,20 +2513,21 @@ namespace ts {
25142513
}
25152514

25162515
// Get source file from normalized fileName
2517-
function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined {
2516+
function findSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined {
25182517
tracing?.push(tracing.Phase.Program, "findSourceFile", {
25192518
fileName,
25202519
isDefaultLib: isDefaultLib || undefined,
25212520
fileIncludeKind: (FileIncludeKind as any)[reason.kind],
25222521
});
2523-
const result = findSourceFileWorker(fileName, path, isDefaultLib, ignoreNoDefaultLib, reason, packageId);
2522+
const result = findSourceFileWorker(fileName, isDefaultLib, ignoreNoDefaultLib, reason, packageId);
25242523
tracing?.pop();
25252524
return result;
25262525
}
25272526

2528-
function findSourceFileWorker(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined {
2527+
function findSourceFileWorker(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined {
2528+
const path = toPath(fileName);
25292529
if (useSourceOfProjectReferenceRedirect) {
2530-
let source = getSourceOfProjectReferenceRedirect(fileName);
2530+
let source = getSourceOfProjectReferenceRedirect(path);
25312531
// If preserveSymlinks is true, module resolution wont jump the symlink
25322532
// but the resolved real path may be the .d.ts from project reference
25332533
// Note:: Currently we try the real path only if the
@@ -2537,12 +2537,12 @@ namespace ts {
25372537
options.preserveSymlinks &&
25382538
isDeclarationFileName(fileName) &&
25392539
stringContains(fileName, nodeModulesPathPart)) {
2540-
const realPath = host.realpath(fileName);
2541-
if (realPath !== fileName) source = getSourceOfProjectReferenceRedirect(realPath);
2540+
const realPath = toPath(host.realpath(fileName));
2541+
if (realPath !== path) source = getSourceOfProjectReferenceRedirect(realPath);
25422542
}
25432543
if (source) {
25442544
const file = isString(source) ?
2545-
findSourceFile(source, toPath(source), isDefaultLib, ignoreNoDefaultLib, reason, packageId) :
2545+
findSourceFile(source, isDefaultLib, ignoreNoDefaultLib, reason, packageId) :
25462546
undefined;
25472547
if (file) addFileToFilesByName(file, path, /*redirectedPath*/ undefined);
25482548
return file;
@@ -2750,8 +2750,8 @@ namespace ts {
27502750
return ts.forEachResolvedProjectReference(resolvedProjectReferences, cb);
27512751
}
27522752

2753-
function getSourceOfProjectReferenceRedirect(file: string) {
2754-
if (!isDeclarationFileName(file)) return undefined;
2753+
function getSourceOfProjectReferenceRedirect(path: Path) {
2754+
if (!isDeclarationFileName(path)) return undefined;
27552755
if (mapFromToProjectReferenceRedirectSource === undefined) {
27562756
mapFromToProjectReferenceRedirectSource = new Map();
27572757
forEachResolvedProjectReference(resolvedRef => {
@@ -2772,7 +2772,7 @@ namespace ts {
27722772
}
27732773
});
27742774
}
2775-
return mapFromToProjectReferenceRedirectSource.get(toPath(file));
2775+
return mapFromToProjectReferenceRedirectSource.get(path);
27762776
}
27772777

27782778
function isSourceOfProjectReferenceRedirect(fileName: string) {
@@ -2954,10 +2954,8 @@ namespace ts {
29542954
modulesWithElidedImports.set(file.path, true);
29552955
}
29562956
else if (shouldAddFile) {
2957-
const path = toPath(resolvedFileName);
29582957
findSourceFile(
29592958
resolvedFileName,
2960-
path,
29612959
/*isDefaultLib*/ false,
29622960
/*ignoreNoDefaultLib*/ false,
29632961
{ kind: FileIncludeKind.Import, file: file.path, index, },
@@ -3691,7 +3689,7 @@ namespace ts {
36913689
useSourceOfProjectReferenceRedirect: boolean;
36923690
toPath(fileName: string): Path;
36933691
getResolvedProjectReferences(): readonly (ResolvedProjectReference | undefined)[] | undefined;
3694-
getSourceOfProjectReferenceRedirect(fileName: string): SourceOfProjectReferenceRedirect | undefined;
3692+
getSourceOfProjectReferenceRedirect(path: Path): SourceOfProjectReferenceRedirect | undefined;
36953693
forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined): T | undefined;
36963694
}
36973695

@@ -3778,9 +3776,9 @@ namespace ts {
37783776
}
37793777

37803778
function fileExistsIfProjectReferenceDts(file: string) {
3781-
const source = host.getSourceOfProjectReferenceRedirect(file);
3779+
const source = host.getSourceOfProjectReferenceRedirect(host.toPath(file));
37823780
return source !== undefined ?
3783-
isString(source) ? originalFileExists.call(host.compilerHost, source) : true :
3781+
isString(source) ? originalFileExists.call(host.compilerHost, source) as boolean : true :
37843782
undefined;
37853783
}
37863784

src/compiler/sys.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1279,7 +1279,7 @@ namespace ts {
12791279

12801280
const platform: string = _os.platform();
12811281
const useCaseSensitiveFileNames = isFileSystemCaseSensitive();
1282-
const realpathSync = useCaseSensitiveFileNames ? (_fs.realpathSync.native ?? _fs.realpathSync) : _fs.realpathSync;
1282+
const realpathSync = _fs.realpathSync.native ?? _fs.realpathSync;
12831283

12841284
const fsSupportsRecursiveFsWatch = isNode4OrLater && (process.platform === "win32" || process.platform === "darwin");
12851285
const getCurrentDirectory = memoize(() => process.cwd());

src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -6461,6 +6461,7 @@ namespace ts {
64616461
realpath?(path: string): string;
64626462
getCurrentDirectory?(): string;
64636463
getDirectories?(path: string): string[];
6464+
useCaseSensitiveFileNames?: boolean | (() => boolean);
64646465
}
64656466

64666467
/**

src/compiler/utilities.ts

+2
Original file line numberDiff line numberDiff line change
@@ -6210,7 +6210,9 @@ namespace ts {
62106210
}
62116211

62126212
export interface SymlinkedDirectory {
6213+
/** Matches the casing returned by `realpath`. Used to compute the `realpath` of children. */
62136214
real: string;
6215+
/** toPath(real). Stored to avoid repeated recomputation. */
62146216
realPath: Path;
62156217
}
62166218

src/harness/harnessLanguageService.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,8 @@ namespace Harness.LanguageService {
325325
readFile: fileName => {
326326
const scriptInfo = this.getScriptInfo(fileName);
327327
return scriptInfo && scriptInfo.content;
328-
}
328+
},
329+
useCaseSensitiveFileNames: this.useCaseSensitiveFileNames()
329330
};
330331
this.getModuleResolutionsForFile = (fileName) => {
331332
const scriptInfo = this.getScriptInfo(fileName)!;

src/server/project.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1706,6 +1706,7 @@ namespace ts.server {
17061706
readFile: this.projectService.host.readFile.bind(this.projectService.host),
17071707
getDirectories: this.projectService.host.getDirectories.bind(this.projectService.host),
17081708
trace: this.projectService.host.trace?.bind(this.projectService.host),
1709+
useCaseSensitiveFileNames: this.program.useCaseSensitiveFileNames(),
17091710
};
17101711
}
17111712
return this.projectService.host;

src/testRunner/unittests/moduleResolution.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,12 @@ namespace ts {
6565
fileExists: path => {
6666
assert.isTrue(directories.has(getDirectoryPath(path)), `'fileExists' '${path}' request in non-existing directory`);
6767
return map.has(path);
68-
}
68+
},
69+
useCaseSensitiveFileNames: true
6970
};
7071
}
7172
else {
72-
return { readFile, realpath, fileExists: path => map.has(path) };
73+
return { readFile, realpath, fileExists: path => map.has(path), useCaseSensitiveFileNames: true };
7374
}
7475
function readFile(path: string): string | undefined {
7576
const file = map.get(path);

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

+1
Original file line numberDiff line numberDiff line change
@@ -3059,6 +3059,7 @@ declare namespace ts {
30593059
realpath?(path: string): string;
30603060
getCurrentDirectory?(): string;
30613061
getDirectories?(path: string): string[];
3062+
useCaseSensitiveFileNames?: boolean | (() => boolean);
30623063
}
30633064
/**
30643065
* Represents the result of module resolution.

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

+1
Original file line numberDiff line numberDiff line change
@@ -3059,6 +3059,7 @@ declare namespace ts {
30593059
realpath?(path: string): string;
30603060
getCurrentDirectory?(): string;
30613061
getDirectories?(path: string): string[];
3062+
useCaseSensitiveFileNames?: boolean | (() => boolean);
30623063
}
30633064
/**
30643065
* Represents the result of module resolution.

0 commit comments

Comments
 (0)