Skip to content

Commit f51e232

Browse files
committed
Optimize symlink module specifier generation
1 parent a02a2e7 commit f51e232

File tree

3 files changed

+29
-17
lines changed

3 files changed

+29
-17
lines changed

src/compiler/moduleSpecifiers.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,8 @@ namespace ts.moduleSpecifiers {
274274
const getCanonicalFileName = hostGetCanonicalFileName(host);
275275
const cwd = host.getCurrentDirectory();
276276
const referenceRedirect = host.isSourceOfProjectReferenceRedirect(importedFileName) ? host.getProjectReferenceRedirect(importedFileName) : undefined;
277-
const redirects = host.redirectTargetsMap.get(toPath(importedFileName, cwd, getCanonicalFileName)) || emptyArray;
277+
const importedPath = toPath(importedFileName, cwd, getCanonicalFileName);
278+
const redirects = host.redirectTargetsMap.get(importedPath) || emptyArray;
278279
const importedFileNames = [...(referenceRedirect ? [referenceRedirect] : emptyArray), importedFileName, ...redirects];
279280
const targets = importedFileNames.map(f => getNormalizedAbsolutePath(f, cwd));
280281
if (!preferSymlinks) {
@@ -287,22 +288,24 @@ namespace ts.moduleSpecifiers {
287288
? host.getSymlinkCache()
288289
: discoverProbableSymlinks(host.getSourceFiles(), getCanonicalFileName, cwd);
289290

290-
const symlinkedDirectories = links.getSymlinkedDirectories();
291-
const useCaseSensitiveFileNames = !host.useCaseSensitiveFileNames || host.useCaseSensitiveFileNames();
292-
const result = symlinkedDirectories && forEachEntry(symlinkedDirectories, (resolved, path) => {
293-
if (resolved === false) return undefined;
294-
if (startsWithDirectory(importingFileName, resolved.realPath, getCanonicalFileName)) {
295-
return undefined; // Don't want to a package to globally import from itself
291+
const symlinkedDirectories = links.getSymlinkedDirectoriesByRealpath();
292+
const result = symlinkedDirectories && forEachAncestorDirectory(getDirectoryPath(importedPath), realPathDirectory => {
293+
const symlinkDirectories = symlinkedDirectories.get(realPathDirectory);
294+
if (!symlinkDirectories) return undefined; // Continue to ancestor directory
295+
296+
// Don't want to a package to globally import from itself (importNameCodeFix_symlink_own_package.ts)
297+
if (startsWithDirectory(importingFileName, realPathDirectory, getCanonicalFileName)) {
298+
return false; // Stop search, each ancestor directory will also hit this condition
296299
}
297300

298301
return forEach(targets, target => {
299-
if (!containsPath(resolved.real, target, !useCaseSensitiveFileNames)) {
302+
if (!startsWithDirectory(target, realPathDirectory, getCanonicalFileName)) {
300303
return;
301304
}
302305

303-
const relative = getRelativePathFromDirectory(resolved.real, target, getCanonicalFileName);
304-
const option = resolvePath(path, relative);
305-
if (!host.fileExists || host.fileExists(option)) {
306+
const relative = getRelativePathFromDirectory(realPathDirectory, target, getCanonicalFileName);
307+
for (const symlinkDirectory of symlinkDirectories) {
308+
const option = resolvePath(symlinkDirectory, relative);
306309
const result = cb(option, target === referenceRedirect);
307310
if (result) return result;
308311
}

src/compiler/path.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -762,7 +762,7 @@ namespace ts {
762762
* Determines whether `fileName` starts with the specified `directoryName` using the provided path canonicalization callback.
763763
* Comparison is case-sensitive between the canonical paths.
764764
*
765-
* @deprecated Use `containsPath` if possible.
765+
* Use `containsPath` if file names are not already reduced and absolute.
766766
*/
767767
export function startsWithDirectory(fileName: string, directoryName: string, getCanonicalFileName: GetCanonicalFileName): boolean {
768768
const canonicalFileName = getCanonicalFileName(fileName);

src/compiler/utilities.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6091,25 +6091,34 @@ namespace ts {
60916091
}
60926092

60936093
export interface SymlinkCache {
6094+
/** Gets a map from symlink to realpath */
60946095
getSymlinkedDirectories(): ReadonlyESMap<Path, SymlinkedDirectory | false> | undefined;
6096+
/** Gets a map from realpath to symlinks */
6097+
getSymlinkedDirectoriesByRealpath(): MultiMap<Path, Path> | undefined;
6098+
/** Gets a map from symlink to realpath */
60956099
getSymlinkedFiles(): ReadonlyESMap<Path, string> | undefined;
6096-
setSymlinkedDirectory(path: Path, directory: SymlinkedDirectory | false): void;
6097-
setSymlinkedFile(path: Path, real: string): void;
6100+
setSymlinkedDirectory(symlinkPath: Path, directory: SymlinkedDirectory | false): void;
6101+
setSymlinkedFile(symlinkPath: Path, real: string): void;
60986102
}
60996103

61006104
export function createSymlinkCache(): SymlinkCache {
61016105
let symlinkedDirectories: ESMap<Path, SymlinkedDirectory | false> | undefined;
6106+
let symlinkedDirectoriesByRealpath: MultiMap<Path, Path> | undefined;
61026107
let symlinkedFiles: ESMap<Path, string> | undefined;
61036108
return {
61046109
getSymlinkedFiles: () => symlinkedFiles,
61056110
getSymlinkedDirectories: () => symlinkedDirectories,
6111+
getSymlinkedDirectoriesByRealpath: () => symlinkedDirectoriesByRealpath,
61066112
setSymlinkedFile: (path, real) => (symlinkedFiles || (symlinkedFiles = new Map())).set(path, real),
6107-
setSymlinkedDirectory: (path, directory) => {
6113+
setSymlinkedDirectory: (symlinkPath, directory) => {
61086114
// Large, interconnected dependency graphs in pnpm will have a huge number of symlinks
61096115
// where both the realpath and the symlink path are inside node_modules/.pnpm. Since
61106116
// this path is never a candidate for a module specifier, we can ignore it entirely.
6111-
if (!pathContainsPnpmDirectory(path)) {
6112-
(symlinkedDirectories || (symlinkedDirectories = new Map())).set(path, directory);
6117+
if (!pathContainsPnpmDirectory(symlinkPath)) {
6118+
if (directory !== false && !symlinkedDirectories?.has(symlinkPath)) {
6119+
(symlinkedDirectoriesByRealpath ||= createMultiMap()).add(directory.realPath, symlinkPath)
6120+
}
6121+
(symlinkedDirectories || (symlinkedDirectories = new Map())).set(symlinkPath, directory);
61136122
}
61146123
}
61156124
};

0 commit comments

Comments
 (0)