Skip to content

Commit bd8a8a1

Browse files
committed
Optimize symlink module specifier generation
1 parent b4e1574 commit bd8a8a1

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
@@ -286,7 +286,8 @@ namespace ts.moduleSpecifiers {
286286
const getCanonicalFileName = hostGetCanonicalFileName(host);
287287
const cwd = host.getCurrentDirectory();
288288
const referenceRedirect = host.isSourceOfProjectReferenceRedirect(importedFileName) ? host.getProjectReferenceRedirect(importedFileName) : undefined;
289-
const redirects = host.redirectTargetsMap.get(toPath(importedFileName, cwd, getCanonicalFileName)) || emptyArray;
289+
const importedPath = toPath(importedFileName, cwd, getCanonicalFileName);
290+
const redirects = host.redirectTargetsMap.get(importedPath) || emptyArray;
290291
const importedFileNames = [...(referenceRedirect ? [referenceRedirect] : emptyArray), importedFileName, ...redirects];
291292
const targets = importedFileNames.map(f => getNormalizedAbsolutePath(f, cwd));
292293
if (!preferSymlinks) {
@@ -299,22 +300,24 @@ namespace ts.moduleSpecifiers {
299300
? host.getSymlinkCache()
300301
: discoverProbableSymlinks(host.getSourceFiles(), getCanonicalFileName, cwd);
301302

302-
const symlinkedDirectories = links.getSymlinkedDirectories();
303-
const useCaseSensitiveFileNames = !host.useCaseSensitiveFileNames || host.useCaseSensitiveFileNames();
304-
const result = symlinkedDirectories && forEachEntry(symlinkedDirectories, (resolved, path) => {
305-
if (resolved === false) return undefined;
306-
if (startsWithDirectory(importingFileName, resolved.realPath, getCanonicalFileName)) {
307-
return undefined; // Don't want to a package to globally import from itself
303+
const symlinkedDirectories = links.getSymlinkedDirectoriesByRealpath();
304+
const result = symlinkedDirectories && forEachAncestorDirectory(getDirectoryPath(importedPath), realPathDirectory => {
305+
const symlinkDirectories = symlinkedDirectories.get(realPathDirectory);
306+
if (!symlinkDirectories) return undefined; // Continue to ancestor directory
307+
308+
// Don't want to a package to globally import from itself (importNameCodeFix_symlink_own_package.ts)
309+
if (startsWithDirectory(importingFileName, realPathDirectory, getCanonicalFileName)) {
310+
return false; // Stop search, each ancestor directory will also hit this condition
308311
}
309312

310313
return forEach(targets, target => {
311-
if (!containsPath(resolved.real, target, !useCaseSensitiveFileNames)) {
314+
if (!startsWithDirectory(target, realPathDirectory, getCanonicalFileName)) {
312315
return;
313316
}
314317

315-
const relative = getRelativePathFromDirectory(resolved.real, target, getCanonicalFileName);
316-
const option = resolvePath(path, relative);
317-
if (!host.fileExists || host.fileExists(option)) {
318+
const relative = getRelativePathFromDirectory(realPathDirectory, target, getCanonicalFileName);
319+
for (const symlinkDirectory of symlinkDirectories) {
320+
const option = resolvePath(symlinkDirectory, relative);
318321
const result = cb(option, target === referenceRedirect);
319322
if (result) return result;
320323
}

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
@@ -6079,25 +6079,34 @@ namespace ts {
60796079
}
60806080

60816081
export interface SymlinkCache {
6082+
/** Gets a map from symlink to realpath */
60826083
getSymlinkedDirectories(): ReadonlyESMap<Path, SymlinkedDirectory | false> | undefined;
6084+
/** Gets a map from realpath to symlinks */
6085+
getSymlinkedDirectoriesByRealpath(): MultiMap<Path, Path> | undefined;
6086+
/** Gets a map from symlink to realpath */
60836087
getSymlinkedFiles(): ReadonlyESMap<Path, string> | undefined;
6084-
setSymlinkedDirectory(path: Path, directory: SymlinkedDirectory | false): void;
6085-
setSymlinkedFile(path: Path, real: string): void;
6088+
setSymlinkedDirectory(symlinkPath: Path, directory: SymlinkedDirectory | false): void;
6089+
setSymlinkedFile(symlinkPath: Path, real: string): void;
60866090
}
60876091

60886092
export function createSymlinkCache(): SymlinkCache {
60896093
let symlinkedDirectories: ESMap<Path, SymlinkedDirectory | false> | undefined;
6094+
let symlinkedDirectoriesByRealpath: MultiMap<Path, Path> | undefined;
60906095
let symlinkedFiles: ESMap<Path, string> | undefined;
60916096
return {
60926097
getSymlinkedFiles: () => symlinkedFiles,
60936098
getSymlinkedDirectories: () => symlinkedDirectories,
6099+
getSymlinkedDirectoriesByRealpath: () => symlinkedDirectoriesByRealpath,
60946100
setSymlinkedFile: (path, real) => (symlinkedFiles || (symlinkedFiles = new Map())).set(path, real),
6095-
setSymlinkedDirectory: (path, directory) => {
6101+
setSymlinkedDirectory: (symlinkPath, directory) => {
60966102
// Large, interconnected dependency graphs in pnpm will have a huge number of symlinks
60976103
// where both the realpath and the symlink path are inside node_modules/.pnpm. Since
60986104
// this path is never a candidate for a module specifier, we can ignore it entirely.
6099-
if (!containsIgnoredPath(path)) {
6100-
(symlinkedDirectories || (symlinkedDirectories = new Map())).set(path, directory);
6105+
if (!containsIgnoredPath(symlinkPath)) {
6106+
if (directory !== false && !symlinkedDirectories?.has(symlinkPath)) {
6107+
(symlinkedDirectoriesByRealpath ||= createMultiMap()).add(directory.realPath, symlinkPath)
6108+
}
6109+
(symlinkedDirectories || (symlinkedDirectories = new Map())).set(symlinkPath, directory);
61016110
}
61026111
}
61036112
};

0 commit comments

Comments
 (0)