Merge pull request #30376 from Microsoft/recursiveSymLinks
Handle recursive symlinks when matching file names
This commit is contained in:
commit
4e0514db0e
6 changed files with 42 additions and 7 deletions
|
@ -1148,7 +1148,7 @@ namespace ts {
|
|||
}
|
||||
|
||||
function readDirectory(path: string, extensions?: ReadonlyArray<string>, excludes?: ReadonlyArray<string>, includes?: ReadonlyArray<string>, depth?: number): string[] {
|
||||
return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames, process.cwd(), depth, getAccessibleFileSystemEntries);
|
||||
return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames, process.cwd(), depth, getAccessibleFileSystemEntries, realpath);
|
||||
}
|
||||
|
||||
function fileSystemEntryExists(path: string, entryKind: FileSystemEntryKind): boolean {
|
||||
|
|
|
@ -8101,7 +8101,7 @@ namespace ts {
|
|||
}
|
||||
|
||||
/** @param path directory of the tsconfig.json */
|
||||
export function matchFiles(path: string, extensions: ReadonlyArray<string> | undefined, excludes: ReadonlyArray<string> | undefined, includes: ReadonlyArray<string> | undefined, useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number | undefined, getFileSystemEntries: (path: string) => FileSystemEntries): string[] {
|
||||
export function matchFiles(path: string, extensions: ReadonlyArray<string> | undefined, excludes: ReadonlyArray<string> | undefined, includes: ReadonlyArray<string> | undefined, useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number | undefined, getFileSystemEntries: (path: string) => FileSystemEntries, realpath: (path: string) => string): string[] {
|
||||
path = normalizePath(path);
|
||||
currentDirectory = normalizePath(currentDirectory);
|
||||
|
||||
|
@ -8114,7 +8114,8 @@ namespace ts {
|
|||
// Associate an array of results with each include regex. This keeps results in order of the "include" order.
|
||||
// If there are no "includes", then just put everything in results[0].
|
||||
const results: string[][] = includeFileRegexes ? includeFileRegexes.map(() => []) : [[]];
|
||||
|
||||
const visited = createMap<true>();
|
||||
const toCanonical = createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||
for (const basePath of patterns.basePaths) {
|
||||
visitDirectory(basePath, combinePaths(currentDirectory, basePath), depth);
|
||||
}
|
||||
|
@ -8122,6 +8123,9 @@ namespace ts {
|
|||
return flatten<string>(results);
|
||||
|
||||
function visitDirectory(path: string, absolutePath: string, depth: number | undefined) {
|
||||
const canonicalPath = toCanonical(realpath(absolutePath));
|
||||
if (visited.has(canonicalPath)) return;
|
||||
visited.set(canonicalPath, true);
|
||||
const { files, directories } = getFileSystemEntries(path);
|
||||
|
||||
for (const current of sort<string>(files, compareStringsCaseSensitive)) {
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace ts {
|
|||
directoryExists?(path: string): boolean;
|
||||
getDirectories?(path: string): string[];
|
||||
readDirectory?(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
|
||||
realpath?(path: string): string;
|
||||
|
||||
createDirectory?(path: string): void;
|
||||
writeFile?(path: string, data: string, writeByteOrderMark?: boolean): void;
|
||||
|
@ -56,7 +57,8 @@ namespace ts {
|
|||
writeFile: host.writeFile && writeFile,
|
||||
addOrDeleteFileOrDirectory,
|
||||
addOrDeleteFile,
|
||||
clearCache
|
||||
clearCache,
|
||||
realpath: host.realpath && realpath
|
||||
};
|
||||
|
||||
function toPath(fileName: string) {
|
||||
|
@ -170,7 +172,7 @@ namespace ts {
|
|||
const rootDirPath = toPath(rootDir);
|
||||
const result = tryReadDirectory(rootDir, rootDirPath);
|
||||
if (result) {
|
||||
return matchFiles(rootDir, extensions, excludes, includes, useCaseSensitiveFileNames, currentDirectory, depth, getFileSystemEntries);
|
||||
return matchFiles(rootDir, extensions, excludes, includes, useCaseSensitiveFileNames, currentDirectory, depth, getFileSystemEntries, realpath);
|
||||
}
|
||||
return host.readDirectory!(rootDir, extensions, excludes, includes, depth);
|
||||
|
||||
|
@ -183,6 +185,10 @@ namespace ts {
|
|||
}
|
||||
}
|
||||
|
||||
function realpath(s: string) {
|
||||
return host.realpath ? host.realpath(s) : s;
|
||||
}
|
||||
|
||||
function addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path) {
|
||||
const existingResult = getCachedFileSystemEntries(fileOrDirectoryPath);
|
||||
if (existingResult) {
|
||||
|
|
|
@ -87,7 +87,7 @@ namespace fakes {
|
|||
}
|
||||
|
||||
public readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[] {
|
||||
return ts.matchFiles(path, extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), depth, path => this.getAccessibleFileSystemEntries(path));
|
||||
return ts.matchFiles(path, extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), depth, path => this.getAccessibleFileSystemEntries(path), path => this.realpath(path));
|
||||
}
|
||||
|
||||
public getAccessibleFileSystemEntries(path: string): ts.FileSystemEntries {
|
||||
|
|
|
@ -826,7 +826,7 @@ interface Array<T> {}`
|
|||
});
|
||||
}
|
||||
return { directories, files };
|
||||
});
|
||||
}, path => this.realpath(path));
|
||||
}
|
||||
|
||||
watchDirectory(directoryName: string, cb: DirectoryWatcherCallback, recursive: boolean): FileWatcher {
|
||||
|
|
|
@ -1512,5 +1512,30 @@ namespace ts {
|
|||
validateMatches(getExpected(caseSensitiveBasePath), json, caseSensitiveOrderingDiffersWithCaseHost, caseSensitiveBasePath);
|
||||
validateMatches(getExpected(caseInsensitiveBasePath), json, caseInsensitiveOrderingDiffersWithCaseHost, caseInsensitiveBasePath);
|
||||
});
|
||||
|
||||
it("when recursive symlinked directories are present", () => {
|
||||
const fs = new vfs.FileSystem(/*ignoreCase*/ true, {
|
||||
cwd: caseInsensitiveBasePath, files: {
|
||||
"c:/dev/index.ts": ""
|
||||
}
|
||||
});
|
||||
fs.mkdirpSync("c:/dev/a/b/c");
|
||||
fs.symlinkSync("c:/dev/A", "c:/dev/a/self");
|
||||
fs.symlinkSync("c:/dev/a", "c:/dev/a/b/parent");
|
||||
fs.symlinkSync("c:/dev/a", "c:/dev/a/b/c/grandparent");
|
||||
const host = new fakes.ParseConfigHost(fs);
|
||||
const json = {};
|
||||
const expected: ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [],
|
||||
fileNames: [
|
||||
"c:/dev/index.ts"
|
||||
],
|
||||
wildcardDirectories: {
|
||||
"c:/dev": WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
validateMatches(expected, json, host, caseInsensitiveBasePath);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue