Skip to content

Commit

Permalink
Use realpathSync.native on case-insensitive file systems (#44966)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
amcasey committed Aug 26, 2021
1 parent 983ddf5 commit 7fc1cb4
Show file tree
Hide file tree
Showing 10 changed files with 39 additions and 28 deletions.
9 changes: 7 additions & 2 deletions src/compiler/moduleNameResolver.ts
Expand Up @@ -280,6 +280,11 @@ namespace ts {
}
const nodeModulesAtTypes = combinePaths("node_modules", "@types");

function arePathsEqual(path1: string, path2: string, host: ModuleResolutionHost): boolean {
const useCaseSensitiveFileNames = typeof host.useCaseSensitiveFileNames === "function" ? host.useCaseSensitiveFileNames() : host.useCaseSensitiveFileNames;
return comparePaths(path1, path2, !useCaseSensitiveFileNames) === Comparison.EqualTo;
}

/**
* @param {string | undefined} containingFile - file that contains type reference directive, can be undefined if containing file is unknown.
* This is possible in case if resolution is performed for directives specified via 'types' parameter. In this case initial path for secondary lookups
Expand Down Expand Up @@ -343,7 +348,7 @@ namespace ts {
resolvedTypeReferenceDirective = {
primary,
resolvedFileName,
originalPath: fileName === resolvedFileName ? undefined : fileName,
originalPath: arePathsEqual(fileName, resolvedFileName, host) ? undefined : fileName,
packageId,
isExternalLibraryImport: pathContainsNodeModules(fileName),
};
Expand Down Expand Up @@ -1122,7 +1127,7 @@ namespace ts {
let resolvedValue = resolved.value;
if (!compilerOptions.preserveSymlinks && resolvedValue && !resolvedValue.originalPath) {
const path = realPath(resolvedValue.path, host, traceEnabled);
const originalPath = path === resolvedValue.path ? undefined : resolvedValue.path;
const originalPath = arePathsEqual(path, resolvedValue.path, host) ? undefined : resolvedValue.path;
resolvedValue = { ...resolvedValue, path, originalPath };
}
// For node_modules lookups, get the real path so that multiple accesses to an `npm link`-ed module do not create duplicate files.
Expand Down
42 changes: 20 additions & 22 deletions src/compiler/program.ts
Expand Up @@ -1163,21 +1163,20 @@ namespace ts {

// The originalFileName could not be actual source file name if file found was d.ts from referecned project
// 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
const resultFromDts = getRedirectReferenceForResolutionFromSourceOfProject(file.originalFileName, file.path);
const resultFromDts = getRedirectReferenceForResolutionFromSourceOfProject(file.path);
if (resultFromDts) return resultFromDts;

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

function getRedirectReferenceForResolutionFromSourceOfProject(fileName: string, filePath: Path) {
const source = getSourceOfProjectReferenceRedirect(fileName);
function getRedirectReferenceForResolutionFromSourceOfProject(filePath: Path) {
const source = getSourceOfProjectReferenceRedirect(filePath);
if (isString(source)) return getResolvedProjectReferenceToRedirect(source);
if (!source) return undefined;
// Output of .d.ts file so return resolved ref that matches the out file name
Expand Down Expand Up @@ -2472,7 +2471,7 @@ namespace ts {
function processSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, packageId: PackageId | undefined, reason: FileIncludeReason): void {
getSourceFileFromReferenceWorker(
fileName,
fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, ignoreNoDefaultLib, reason, packageId), // TODO: GH#18217
fileName => findSourceFile(fileName, isDefaultLib, ignoreNoDefaultLib, reason, packageId), // TODO: GH#18217
(diagnostic, ...args) => addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, diagnostic, args),
reason
);
Expand Down Expand Up @@ -2514,20 +2513,21 @@ namespace ts {
}

// Get source file from normalized fileName
function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined {
function findSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined {
tracing?.push(tracing.Phase.Program, "findSourceFile", {
fileName,
isDefaultLib: isDefaultLib || undefined,
fileIncludeKind: (FileIncludeKind as any)[reason.kind],
});
const result = findSourceFileWorker(fileName, path, isDefaultLib, ignoreNoDefaultLib, reason, packageId);
const result = findSourceFileWorker(fileName, isDefaultLib, ignoreNoDefaultLib, reason, packageId);
tracing?.pop();
return result;
}

function findSourceFileWorker(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined {
function findSourceFileWorker(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined {
const path = toPath(fileName);
if (useSourceOfProjectReferenceRedirect) {
let source = getSourceOfProjectReferenceRedirect(fileName);
let source = getSourceOfProjectReferenceRedirect(path);
// If preserveSymlinks is true, module resolution wont jump the symlink
// but the resolved real path may be the .d.ts from project reference
// Note:: Currently we try the real path only if the
Expand All @@ -2537,12 +2537,12 @@ namespace ts {
options.preserveSymlinks &&
isDeclarationFileName(fileName) &&
stringContains(fileName, nodeModulesPathPart)) {
const realPath = host.realpath(fileName);
if (realPath !== fileName) source = getSourceOfProjectReferenceRedirect(realPath);
const realPath = toPath(host.realpath(fileName));
if (realPath !== path) source = getSourceOfProjectReferenceRedirect(realPath);
}
if (source) {
const file = isString(source) ?
findSourceFile(source, toPath(source), isDefaultLib, ignoreNoDefaultLib, reason, packageId) :
findSourceFile(source, isDefaultLib, ignoreNoDefaultLib, reason, packageId) :
undefined;
if (file) addFileToFilesByName(file, path, /*redirectedPath*/ undefined);
return file;
Expand Down Expand Up @@ -2750,8 +2750,8 @@ namespace ts {
return ts.forEachResolvedProjectReference(resolvedProjectReferences, cb);
}

function getSourceOfProjectReferenceRedirect(file: string) {
if (!isDeclarationFileName(file)) return undefined;
function getSourceOfProjectReferenceRedirect(path: Path) {
if (!isDeclarationFileName(path)) return undefined;
if (mapFromToProjectReferenceRedirectSource === undefined) {
mapFromToProjectReferenceRedirectSource = new Map();
forEachResolvedProjectReference(resolvedRef => {
Expand All @@ -2772,7 +2772,7 @@ namespace ts {
}
});
}
return mapFromToProjectReferenceRedirectSource.get(toPath(file));
return mapFromToProjectReferenceRedirectSource.get(path);
}

function isSourceOfProjectReferenceRedirect(fileName: string) {
Expand Down Expand Up @@ -2954,10 +2954,8 @@ namespace ts {
modulesWithElidedImports.set(file.path, true);
}
else if (shouldAddFile) {
const path = toPath(resolvedFileName);
findSourceFile(
resolvedFileName,
path,
/*isDefaultLib*/ false,
/*ignoreNoDefaultLib*/ false,
{ kind: FileIncludeKind.Import, file: file.path, index, },
Expand Down Expand Up @@ -3691,7 +3689,7 @@ namespace ts {
useSourceOfProjectReferenceRedirect: boolean;
toPath(fileName: string): Path;
getResolvedProjectReferences(): readonly (ResolvedProjectReference | undefined)[] | undefined;
getSourceOfProjectReferenceRedirect(fileName: string): SourceOfProjectReferenceRedirect | undefined;
getSourceOfProjectReferenceRedirect(path: Path): SourceOfProjectReferenceRedirect | undefined;
forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined): T | undefined;
}

Expand Down Expand Up @@ -3778,9 +3776,9 @@ namespace ts {
}

function fileExistsIfProjectReferenceDts(file: string) {
const source = host.getSourceOfProjectReferenceRedirect(file);
const source = host.getSourceOfProjectReferenceRedirect(host.toPath(file));
return source !== undefined ?
isString(source) ? originalFileExists.call(host.compilerHost, source) : true :
isString(source) ? originalFileExists.call(host.compilerHost, source) as boolean : true :
undefined;
}

Expand Down
2 changes: 1 addition & 1 deletion src/compiler/sys.ts
Expand Up @@ -1279,7 +1279,7 @@ namespace ts {

const platform: string = _os.platform();
const useCaseSensitiveFileNames = isFileSystemCaseSensitive();
const realpathSync = useCaseSensitiveFileNames ? (_fs.realpathSync.native ?? _fs.realpathSync) : _fs.realpathSync;
const realpathSync = _fs.realpathSync.native ?? _fs.realpathSync;

const fsSupportsRecursiveFsWatch = isNode4OrLater && (process.platform === "win32" || process.platform === "darwin");
const getCurrentDirectory = memoize(() => process.cwd());
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Expand Up @@ -6461,6 +6461,7 @@ namespace ts {
realpath?(path: string): string;
getCurrentDirectory?(): string;
getDirectories?(path: string): string[];
useCaseSensitiveFileNames?: boolean | (() => boolean);
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/utilities.ts
Expand Up @@ -6210,7 +6210,9 @@ namespace ts {
}

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

Expand Down
3 changes: 2 additions & 1 deletion src/harness/harnessLanguageService.ts
Expand Up @@ -325,7 +325,8 @@ namespace Harness.LanguageService {
readFile: fileName => {
const scriptInfo = this.getScriptInfo(fileName);
return scriptInfo && scriptInfo.content;
}
},
useCaseSensitiveFileNames: this.useCaseSensitiveFileNames()
};
this.getModuleResolutionsForFile = (fileName) => {
const scriptInfo = this.getScriptInfo(fileName)!;
Expand Down
1 change: 1 addition & 0 deletions src/server/project.ts
Expand Up @@ -1706,6 +1706,7 @@ namespace ts.server {
readFile: this.projectService.host.readFile.bind(this.projectService.host),
getDirectories: this.projectService.host.getDirectories.bind(this.projectService.host),
trace: this.projectService.host.trace?.bind(this.projectService.host),
useCaseSensitiveFileNames: this.program.useCaseSensitiveFileNames(),
};
}
return this.projectService.host;
Expand Down
5 changes: 3 additions & 2 deletions src/testRunner/unittests/moduleResolution.ts
Expand Up @@ -65,11 +65,12 @@ namespace ts {
fileExists: path => {
assert.isTrue(directories.has(getDirectoryPath(path)), `'fileExists' '${path}' request in non-existing directory`);
return map.has(path);
}
},
useCaseSensitiveFileNames: true
};
}
else {
return { readFile, realpath, fileExists: path => map.has(path) };
return { readFile, realpath, fileExists: path => map.has(path), useCaseSensitiveFileNames: true };
}
function readFile(path: string): string | undefined {
const file = map.get(path);
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Expand Up @@ -3059,6 +3059,7 @@ declare namespace ts {
realpath?(path: string): string;
getCurrentDirectory?(): string;
getDirectories?(path: string): string[];
useCaseSensitiveFileNames?: boolean | (() => boolean);
}
/**
* Represents the result of module resolution.
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/typescript.d.ts
Expand Up @@ -3059,6 +3059,7 @@ declare namespace ts {
realpath?(path: string): string;
getCurrentDirectory?(): string;
getDirectories?(path: string): string[];
useCaseSensitiveFileNames?: boolean | (() => boolean);
}
/**
* Represents the result of module resolution.
Expand Down

0 comments on commit 7fc1cb4

Please sign in to comment.