in node/task.ts [907:1017]
export function find(findPath: string, options?: FindOptions): string[] {
if (!findPath) {
debug('no path specified');
return [];
}
// normalize the path, otherwise the first result is inconsistently formatted from the rest of the results
// because path.join() performs normalization.
findPath = path.normalize(findPath);
// debug trace the parameters
debug(`findPath: '${findPath}'`);
options = options || _getDefaultFindOptions();
_debugFindOptions(options)
// return empty if not exists
try {
fs.lstatSync(findPath);
}
catch (err) {
if (err.code == 'ENOENT') {
debug('0 results')
return [];
}
throw err;
}
try {
let result: string[] = [];
// push the first item
let stack: _FindItem[] = [new _FindItem(findPath, 1)];
let traversalChain: string[] = []; // used to detect cycles
while (stack.length) {
// pop the next item and push to the result array
let item = stack.pop()!; // non-null because `stack.length` was truthy
let stats: fs.Stats;
try {
// `item.path` equals `findPath` for the first item to be processed, when the `result` array is empty
const isPathToSearch: boolean = !result.length;
// following specified symlinks only if current path equals specified path
const followSpecifiedSymbolicLink: boolean = options.followSpecifiedSymbolicLink && isPathToSearch;
// following all symlinks or following symlink for the specified path
const followSymbolicLink: boolean = options.followSymbolicLinks || followSpecifiedSymbolicLink;
// stat the item. The stat info is used further below to determine whether to traverse deeper
stats = _getStats(item.path, followSymbolicLink, options.allowBrokenSymbolicLinks);
} catch (err) {
if (err.code == 'ENOENT' && options.skipMissingFiles) {
warning(`No such file or directory: "${item.path}" - skipping.`);
continue;
}
throw err;
}
result.push(item.path);
// note, isDirectory() returns false for the lstat of a symlink
if (stats.isDirectory()) {
debug(` ${item.path} (directory)`);
if (options.followSymbolicLinks) {
// get the realpath
let realPath: string;
if (im._isUncPath(item.path)) {
// Sometimes there are spontaneous issues when working with unc-paths, so retries have been added for them.
realPath = retry(fs.realpathSync, [item.path], { continueOnError: false, retryCount: 5 });
} else {
realPath = fs.realpathSync(item.path);
}
// fixup the traversal chain to match the item level
while (traversalChain.length >= item.level) {
traversalChain.pop();
}
// test for a cycle
if (traversalChain.some((x: string) => x == realPath)) {
debug(' cycle detected');
continue;
}
// update the traversal chain
traversalChain.push(realPath);
}
// push the child items in reverse onto the stack
let childLevel: number = item.level + 1;
let childItems: _FindItem[] =
fs.readdirSync(item.path)
.map((childName: string) => new _FindItem(path.join(item.path, childName), childLevel));
for (var i = childItems.length - 1; i >= 0; i--) {
stack.push(childItems[i]);
}
}
else {
debug(` ${item.path} (file)`);
}
}
debug(`${result.length} results`);
return result;
}
catch (err) {
throw new Error(loc('LIB_OperationFailed', 'find', err.message));
}
}