run npm install to generate a package lock
This commit is contained in:
10
node_modules/tiny-readdir/.editorconfig
generated
vendored
Normal file
10
node_modules/tiny-readdir/.editorconfig
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
5
node_modules/tiny-readdir/dist/constants.d.ts
generated
vendored
Normal file
5
node_modules/tiny-readdir/dist/constants.d.ts
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import type { Callback } from './types.js';
|
||||
declare const NOOP_PROMISE_LIKE: {
|
||||
then: (fn: Callback) => void;
|
||||
};
|
||||
export { NOOP_PROMISE_LIKE };
|
||||
9
node_modules/tiny-readdir/dist/constants.js
generated
vendored
Normal file
9
node_modules/tiny-readdir/dist/constants.js
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/* IMPORT */
|
||||
/* MAIN */
|
||||
const NOOP_PROMISE_LIKE = {
|
||||
then: (fn) => {
|
||||
fn();
|
||||
}
|
||||
};
|
||||
/* EXPORT */
|
||||
export { NOOP_PROMISE_LIKE };
|
||||
4
node_modules/tiny-readdir/dist/index.d.ts
generated
vendored
Normal file
4
node_modules/tiny-readdir/dist/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
import type { Dirent, Options, ResultDirectory, ResultDirectories, Result } from './types.js';
|
||||
declare const readdir: (rootPath: string, options?: Options) => Promise<Result>;
|
||||
export default readdir;
|
||||
export type { Dirent, Options, ResultDirectory, ResultDirectories, Result };
|
||||
185
node_modules/tiny-readdir/dist/index.js
generated
vendored
Normal file
185
node_modules/tiny-readdir/dist/index.js
generated
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
/* IMPORT */
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import makeCounterPromise from 'promise-make-counter';
|
||||
import { NOOP_PROMISE_LIKE } from './constants.js';
|
||||
import { castArray, isFunction } from './utils.js';
|
||||
/* MAIN */
|
||||
//TODO: Streamline the type of dirmaps
|
||||
const readdir = (rootPath, options) => {
|
||||
const followSymlinks = options?.followSymlinks ?? false;
|
||||
const maxDepth = options?.depth ?? Infinity;
|
||||
const maxPaths = options?.limit ?? Infinity;
|
||||
const ignore = options?.ignore ?? [];
|
||||
const ignores = castArray(ignore).map(ignore => isFunction(ignore) ? ignore : (targetPath) => ignore.test(targetPath));
|
||||
const isIgnored = (targetPath) => ignores.some(ignore => ignore(targetPath));
|
||||
const signal = options?.signal ?? { aborted: false };
|
||||
const onDirents = options?.onDirents || (() => { });
|
||||
const directories = [];
|
||||
const directoriesNames = new Set();
|
||||
const directoriesNamesToPaths = {};
|
||||
const files = [];
|
||||
const filesNames = new Set();
|
||||
const filesNamesToPaths = {};
|
||||
const symlinks = [];
|
||||
const symlinksNames = new Set();
|
||||
const symlinksNamesToPaths = {};
|
||||
const map = {};
|
||||
const visited = new Set();
|
||||
const resultEmpty = { directories: [], directoriesNames: new Set(), directoriesNamesToPaths: {}, files: [], filesNames: new Set(), filesNamesToPaths: {}, symlinks: [], symlinksNames: new Set(), symlinksNamesToPaths: {}, map: {} };
|
||||
const result = { directories, directoriesNames, directoriesNamesToPaths, files, filesNames, filesNamesToPaths, symlinks, symlinksNames, symlinksNamesToPaths, map };
|
||||
const { promise, increment, decrement } = makeCounterPromise();
|
||||
let foundPaths = 0;
|
||||
const handleDirectory = (dirmap, subPath, name, depth) => {
|
||||
if (visited.has(subPath))
|
||||
return;
|
||||
if (foundPaths >= maxPaths)
|
||||
return;
|
||||
foundPaths += 1;
|
||||
dirmap.directories.push(subPath);
|
||||
dirmap.directoriesNames.add(name);
|
||||
// dirmap.directoriesNamesToPaths.propertyIsEnumerable(name) || ( dirmap.directoriesNamesToPaths[name] = [] );
|
||||
// dirmap.directoriesNamesToPaths[name].push ( subPath );
|
||||
directories.push(subPath);
|
||||
directoriesNames.add(name);
|
||||
directoriesNamesToPaths.propertyIsEnumerable(name) || (directoriesNamesToPaths[name] = []);
|
||||
directoriesNamesToPaths[name].push(subPath);
|
||||
visited.add(subPath);
|
||||
if (depth >= maxDepth)
|
||||
return;
|
||||
if (foundPaths >= maxPaths)
|
||||
return;
|
||||
populateResultFromPath(subPath, depth + 1);
|
||||
};
|
||||
const handleFile = (dirmap, subPath, name) => {
|
||||
if (visited.has(subPath))
|
||||
return;
|
||||
if (foundPaths >= maxPaths)
|
||||
return;
|
||||
foundPaths += 1;
|
||||
dirmap.files.push(subPath);
|
||||
dirmap.filesNames.add(name);
|
||||
// dirmap.filesNamesToPaths.propertyIsEnumerable(name) || ( dirmap.filesNamesToPaths[name] = [] );
|
||||
// dirmap.filesNamesToPaths[name].push ( subPath );
|
||||
files.push(subPath);
|
||||
filesNames.add(name);
|
||||
filesNamesToPaths.propertyIsEnumerable(name) || (filesNamesToPaths[name] = []);
|
||||
filesNamesToPaths[name].push(subPath);
|
||||
visited.add(subPath);
|
||||
};
|
||||
const handleSymlink = (dirmap, subPath, name, depth) => {
|
||||
if (visited.has(subPath))
|
||||
return;
|
||||
if (foundPaths >= maxPaths)
|
||||
return;
|
||||
foundPaths += 1;
|
||||
dirmap.symlinks.push(subPath);
|
||||
dirmap.symlinksNames.add(name);
|
||||
// dirmap.symlinksNamesToPaths.propertyIsEnumerable(name) || ( dirmap.symlinksNamesToPaths[name] = [] );
|
||||
// dirmap.symlinksNamesToPaths[name].push ( subPath );
|
||||
symlinks.push(subPath);
|
||||
symlinksNames.add(name);
|
||||
symlinksNamesToPaths.propertyIsEnumerable(name) || (symlinksNamesToPaths[name] = []);
|
||||
symlinksNamesToPaths[name].push(subPath);
|
||||
visited.add(subPath);
|
||||
if (!followSymlinks)
|
||||
return;
|
||||
if (depth >= maxDepth)
|
||||
return;
|
||||
if (foundPaths >= maxPaths)
|
||||
return;
|
||||
populateResultFromSymlink(subPath, depth + 1);
|
||||
};
|
||||
const handleStat = (dirmap, rootPath, name, stat, depth) => {
|
||||
if (signal.aborted)
|
||||
return;
|
||||
if (isIgnored(rootPath))
|
||||
return;
|
||||
if (stat.isDirectory()) {
|
||||
handleDirectory(dirmap, rootPath, name, depth);
|
||||
}
|
||||
else if (stat.isFile()) {
|
||||
handleFile(dirmap, rootPath, name);
|
||||
}
|
||||
else if (stat.isSymbolicLink()) {
|
||||
handleSymlink(dirmap, rootPath, name, depth);
|
||||
}
|
||||
};
|
||||
const handleDirent = (dirmap, rootPath, dirent, depth) => {
|
||||
if (signal.aborted)
|
||||
return;
|
||||
const separator = (rootPath === path.sep) ? '' : path.sep;
|
||||
const name = dirent.name;
|
||||
const subPath = `${rootPath}${separator}${name}`;
|
||||
if (isIgnored(subPath))
|
||||
return;
|
||||
if (dirent.isDirectory()) {
|
||||
handleDirectory(dirmap, subPath, name, depth);
|
||||
}
|
||||
else if (dirent.isFile()) {
|
||||
handleFile(dirmap, subPath, name);
|
||||
}
|
||||
else if (dirent.isSymbolicLink()) {
|
||||
handleSymlink(dirmap, subPath, name, depth);
|
||||
}
|
||||
};
|
||||
const handleDirents = (dirmap, rootPath, dirents, depth) => {
|
||||
for (let i = 0, l = dirents.length; i < l; i++) {
|
||||
handleDirent(dirmap, rootPath, dirents[i], depth);
|
||||
}
|
||||
};
|
||||
const populateResultFromPath = (rootPath, depth) => {
|
||||
if (signal.aborted)
|
||||
return;
|
||||
if (depth > maxDepth)
|
||||
return;
|
||||
if (foundPaths >= maxPaths)
|
||||
return;
|
||||
increment();
|
||||
fs.readdir(rootPath, { withFileTypes: true }, (error, dirents) => {
|
||||
if (error)
|
||||
return decrement();
|
||||
if (signal.aborted)
|
||||
return decrement();
|
||||
if (!dirents.length)
|
||||
return decrement();
|
||||
const promise = onDirents(dirents) || NOOP_PROMISE_LIKE;
|
||||
promise.then(() => {
|
||||
const dirmap = map[rootPath] = { directories: [], directoriesNames: new Set(), directoriesNamesToPaths: {}, files: [], filesNames: new Set(), filesNamesToPaths: {}, symlinks: [], symlinksNames: new Set(), symlinksNamesToPaths: {} };
|
||||
handleDirents(dirmap, rootPath, dirents, depth);
|
||||
decrement();
|
||||
});
|
||||
});
|
||||
};
|
||||
const populateResultFromSymlink = (rootPath, depth) => {
|
||||
increment();
|
||||
fs.realpath(rootPath, (error, realPath) => {
|
||||
if (error)
|
||||
return decrement();
|
||||
if (signal.aborted)
|
||||
return decrement();
|
||||
fs.stat(realPath, (error, stat) => {
|
||||
if (error)
|
||||
return decrement();
|
||||
if (signal.aborted)
|
||||
return decrement();
|
||||
const name = path.basename(realPath);
|
||||
const dirmap = map[rootPath] = { directories: [], directoriesNames: new Set(), directoriesNamesToPaths: {}, files: [], filesNames: new Set(), filesNamesToPaths: {}, symlinks: [], symlinksNames: new Set(), symlinksNamesToPaths: {} };
|
||||
handleStat(dirmap, realPath, name, stat, depth);
|
||||
decrement();
|
||||
});
|
||||
});
|
||||
};
|
||||
const getResult = async (rootPath, depth = 1) => {
|
||||
rootPath = path.normalize(rootPath);
|
||||
visited.add(rootPath);
|
||||
populateResultFromPath(rootPath, depth);
|
||||
await promise;
|
||||
if (signal.aborted)
|
||||
return resultEmpty;
|
||||
return result;
|
||||
};
|
||||
return getResult(rootPath);
|
||||
};
|
||||
/* EXPORT */
|
||||
export default readdir;
|
||||
42
node_modules/tiny-readdir/dist/types.d.ts
generated
vendored
Normal file
42
node_modules/tiny-readdir/dist/types.d.ts
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
type Callback = () => void;
|
||||
type ArrayMaybe<T> = T[] | T;
|
||||
type PromiseMaybe<T> = Promise<T> | T;
|
||||
type Dirent = {
|
||||
isFile: () => boolean;
|
||||
isDirectory: () => boolean;
|
||||
isBlockDevice: () => boolean;
|
||||
isCharacterDevice: () => boolean;
|
||||
isSymbolicLink: () => boolean;
|
||||
isFIFO: () => boolean;
|
||||
isSocket: () => boolean;
|
||||
name: string;
|
||||
path: string;
|
||||
};
|
||||
type Options = {
|
||||
depth?: number;
|
||||
limit?: number;
|
||||
followSymlinks?: boolean;
|
||||
ignore?: ArrayMaybe<((targetPath: string) => boolean) | RegExp>;
|
||||
signal?: {
|
||||
aborted: boolean;
|
||||
};
|
||||
onDirents?: (dirents: Dirent[]) => PromiseMaybe<undefined>;
|
||||
};
|
||||
type ResultDirectory = {
|
||||
directories: string[];
|
||||
directoriesNames: Set<string>;
|
||||
directoriesNamesToPaths: Record<string, string[]>;
|
||||
files: string[];
|
||||
filesNames: Set<string>;
|
||||
filesNamesToPaths: Record<string, string[]>;
|
||||
symlinks: string[];
|
||||
symlinksNames: Set<string>;
|
||||
symlinksNamesToPaths: Record<string, string[]>;
|
||||
};
|
||||
type ResultDirectories = {
|
||||
[path: string]: ResultDirectory;
|
||||
};
|
||||
type Result = ResultDirectory & {
|
||||
map: ResultDirectories;
|
||||
};
|
||||
export type { Callback, PromiseMaybe, Dirent, Options, ResultDirectory, ResultDirectories, Result };
|
||||
2
node_modules/tiny-readdir/dist/types.js
generated
vendored
Normal file
2
node_modules/tiny-readdir/dist/types.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/* HELPERS */
|
||||
export {};
|
||||
3
node_modules/tiny-readdir/dist/utils.d.ts
generated
vendored
Normal file
3
node_modules/tiny-readdir/dist/utils.d.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
declare const castArray: <T>(value: T[] | T) => T[];
|
||||
declare const isFunction: (value: unknown) => value is Function;
|
||||
export { castArray, isFunction };
|
||||
9
node_modules/tiny-readdir/dist/utils.js
generated
vendored
Normal file
9
node_modules/tiny-readdir/dist/utils.js
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/* MAIN */
|
||||
const castArray = (value) => {
|
||||
return Array.isArray(value) ? value : [value];
|
||||
};
|
||||
const isFunction = (value) => {
|
||||
return (typeof value === 'function');
|
||||
};
|
||||
/* EXPORT */
|
||||
export { castArray, isFunction };
|
||||
21
node_modules/tiny-readdir/license
generated
vendored
Normal file
21
node_modules/tiny-readdir/license
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2020-present Fabio Spampinato
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
34
node_modules/tiny-readdir/package.json
generated
vendored
Executable file
34
node_modules/tiny-readdir/package.json
generated
vendored
Executable file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "tiny-readdir",
|
||||
"repository": "github:fabiospampinato/tiny-readdir",
|
||||
"description": "A simple promisified recursive readdir function.",
|
||||
"version": "2.7.3",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"exports": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"clean": "tsex clean",
|
||||
"compile": "tsex compile",
|
||||
"compile:watch": "tsex compile --watch",
|
||||
"test": "tsex test",
|
||||
"test:watch": "tsex test --watch",
|
||||
"prepublishOnly": "tsex prepare"
|
||||
},
|
||||
"keywords": [
|
||||
"readdir",
|
||||
"recursive",
|
||||
"promise",
|
||||
"simple",
|
||||
"tiny"
|
||||
],
|
||||
"dependencies": {
|
||||
"promise-make-counter": "^1.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.19.39",
|
||||
"fava": "^0.3.4",
|
||||
"tsex": "^4.0.2",
|
||||
"typescript": "^5.5.2"
|
||||
}
|
||||
}
|
||||
44
node_modules/tiny-readdir/readme.md
generated
vendored
Normal file
44
node_modules/tiny-readdir/readme.md
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
# Tiny Readdir
|
||||
|
||||
A simple promisified recursive readdir function.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
npm install --save tiny-readdir
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import readdir from 'tiny-readdir';
|
||||
|
||||
const aborter = new AbortController ();
|
||||
|
||||
const result = await readdir ( '/foo/bar', {
|
||||
depth: 20, // Maximum depth to look at
|
||||
limit: 1_000_000, // Maximum number of files explored, useful as a stop gap in some edge cases
|
||||
followSymlinks: true, // Whether to follow symlinks or not
|
||||
ignore: targetPath => /node_modules/.test ( targetPath ), // Function that if returns true will ignore this particular file or a directory and its descendants
|
||||
signal: aborter.signal, // Optional abort signal, useful for aborting potentially expensive operations
|
||||
onDirents: dirents => console.log ( dirents ) // Optional callback that will be called as soon as new dirents are available, useful for example for discovering ".gitignore" files while searching
|
||||
});
|
||||
|
||||
result.directories; // => Array of absolute paths pointing to directories
|
||||
result.files; // => Array of absolute paths pointing to files
|
||||
result.symlinks; // => Array of absolute paths pointing to symlinks
|
||||
|
||||
result.directoriesNames; // => Set of directories names found
|
||||
result.filesNames; // => Set of files name found
|
||||
result.symlinksNames; // => Set of symlinks names found
|
||||
|
||||
result.directoriesNamesToPaths; // => Record of directories names found to their paths
|
||||
result.filesNamesToPaths; // => Record of files names found to their paths
|
||||
result.symlinksNamesToPaths; // => Record of symlinks names found to their paths
|
||||
|
||||
setTimeout ( () => aborter.abort (), 10_000 ); // Aborting if it's going to take longer than 10s
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT © Fabio Spampinato
|
||||
16
node_modules/tiny-readdir/src/constants.ts
generated
vendored
Normal file
16
node_modules/tiny-readdir/src/constants.ts
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
/* IMPORT */
|
||||
|
||||
import type {Callback} from './types';
|
||||
|
||||
/* MAIN */
|
||||
|
||||
const NOOP_PROMISE_LIKE = {
|
||||
then: ( fn: Callback ) => {
|
||||
fn ();
|
||||
}
|
||||
};
|
||||
|
||||
/* EXPORT */
|
||||
|
||||
export {NOOP_PROMISE_LIKE};
|
||||
257
node_modules/tiny-readdir/src/index.ts
generated
vendored
Executable file
257
node_modules/tiny-readdir/src/index.ts
generated
vendored
Executable file
@@ -0,0 +1,257 @@
|
||||
|
||||
/* IMPORT */
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import makeCounterPromise from 'promise-make-counter';
|
||||
import {NOOP_PROMISE_LIKE} from './constants';
|
||||
import {castArray, isFunction} from './utils';
|
||||
import type {Dirent, Options, ResultDirectory, ResultDirectories, Result} from './types';
|
||||
|
||||
/* MAIN */
|
||||
|
||||
//TODO: Streamline the type of dirmaps
|
||||
|
||||
const readdir = ( rootPath: string, options?: Options ): Promise<Result> => {
|
||||
|
||||
const followSymlinks = options?.followSymlinks ?? false;
|
||||
const maxDepth = options?.depth ?? Infinity;
|
||||
const maxPaths = options?.limit ?? Infinity;
|
||||
const ignore = options?.ignore ?? [];
|
||||
const ignores = castArray ( ignore ).map ( ignore => isFunction ( ignore ) ? ignore : ( targetPath: string ) => ignore.test ( targetPath ) );
|
||||
const isIgnored = ( targetPath: string ) => ignores.some ( ignore => ignore ( targetPath ) );
|
||||
const signal = options?.signal ?? { aborted: false };
|
||||
const onDirents = options?.onDirents || (() => {});
|
||||
const directories: string[] = [];
|
||||
const directoriesNames: Set<string> = new Set ();
|
||||
const directoriesNamesToPaths: Record<string, string[]> = {};
|
||||
const files: string[] = [];
|
||||
const filesNames: Set<string> = new Set ();
|
||||
const filesNamesToPaths: Record<string, string[]> = {};
|
||||
const symlinks: string[] = [];
|
||||
const symlinksNames: Set<string> = new Set ();
|
||||
const symlinksNamesToPaths: Record<string, string[]> = {};
|
||||
const map: ResultDirectories = {};
|
||||
const visited = new Set<string> ();
|
||||
const resultEmpty: Result = { directories: [], directoriesNames: new Set (), directoriesNamesToPaths: {}, files: [], filesNames: new Set (), filesNamesToPaths: {}, symlinks: [], symlinksNames: new Set (), symlinksNamesToPaths: {}, map: {} };
|
||||
const result: Result = { directories, directoriesNames, directoriesNamesToPaths, files, filesNames, filesNamesToPaths, symlinks, symlinksNames, symlinksNamesToPaths, map };
|
||||
const {promise, increment, decrement} = makeCounterPromise ();
|
||||
|
||||
let foundPaths = 0;
|
||||
|
||||
const handleDirectory = ( dirmap: ResultDirectory, subPath: string, name: string, depth: number ): void => {
|
||||
|
||||
if ( visited.has ( subPath ) ) return;
|
||||
|
||||
if ( foundPaths >= maxPaths ) return;
|
||||
|
||||
foundPaths += 1;
|
||||
dirmap.directories.push ( subPath );
|
||||
dirmap.directoriesNames.add ( name );
|
||||
// dirmap.directoriesNamesToPaths.propertyIsEnumerable(name) || ( dirmap.directoriesNamesToPaths[name] = [] );
|
||||
// dirmap.directoriesNamesToPaths[name].push ( subPath );
|
||||
directories.push ( subPath );
|
||||
directoriesNames.add ( name );
|
||||
directoriesNamesToPaths.propertyIsEnumerable(name) || ( directoriesNamesToPaths[name] = [] );
|
||||
directoriesNamesToPaths[name].push ( subPath );
|
||||
visited.add ( subPath );
|
||||
|
||||
if ( depth >= maxDepth ) return;
|
||||
|
||||
if ( foundPaths >= maxPaths ) return;
|
||||
|
||||
populateResultFromPath ( subPath, depth + 1 );
|
||||
|
||||
};
|
||||
|
||||
const handleFile = ( dirmap: ResultDirectory, subPath: string, name: string ): void => {
|
||||
|
||||
if ( visited.has ( subPath ) ) return;
|
||||
|
||||
if ( foundPaths >= maxPaths ) return;
|
||||
|
||||
foundPaths += 1;
|
||||
dirmap.files.push ( subPath );
|
||||
dirmap.filesNames.add ( name );
|
||||
// dirmap.filesNamesToPaths.propertyIsEnumerable(name) || ( dirmap.filesNamesToPaths[name] = [] );
|
||||
// dirmap.filesNamesToPaths[name].push ( subPath );
|
||||
files.push ( subPath );
|
||||
filesNames.add ( name );
|
||||
filesNamesToPaths.propertyIsEnumerable(name) || ( filesNamesToPaths[name] = [] );
|
||||
filesNamesToPaths[name].push ( subPath );
|
||||
visited.add ( subPath );
|
||||
|
||||
};
|
||||
|
||||
const handleSymlink = ( dirmap: ResultDirectory, subPath: string, name: string, depth: number ): void => {
|
||||
|
||||
if ( visited.has ( subPath ) ) return;
|
||||
|
||||
if ( foundPaths >= maxPaths ) return;
|
||||
|
||||
foundPaths += 1;
|
||||
dirmap.symlinks.push ( subPath );
|
||||
dirmap.symlinksNames.add ( name );
|
||||
// dirmap.symlinksNamesToPaths.propertyIsEnumerable(name) || ( dirmap.symlinksNamesToPaths[name] = [] );
|
||||
// dirmap.symlinksNamesToPaths[name].push ( subPath );
|
||||
symlinks.push ( subPath );
|
||||
symlinksNames.add ( name );
|
||||
symlinksNamesToPaths.propertyIsEnumerable(name) || ( symlinksNamesToPaths[name] = [] );
|
||||
symlinksNamesToPaths[name].push ( subPath );
|
||||
visited.add ( subPath );
|
||||
|
||||
if ( !followSymlinks ) return;
|
||||
|
||||
if ( depth >= maxDepth ) return;
|
||||
|
||||
if ( foundPaths >= maxPaths ) return;
|
||||
|
||||
populateResultFromSymlink ( subPath, depth + 1 );
|
||||
|
||||
};
|
||||
|
||||
const handleStat = ( dirmap: ResultDirectory, rootPath: string, name: string, stat: fs.Stats, depth: number ): void => {
|
||||
|
||||
if ( signal.aborted ) return;
|
||||
|
||||
if ( isIgnored ( rootPath ) ) return;
|
||||
|
||||
if ( stat.isDirectory () ) {
|
||||
|
||||
handleDirectory ( dirmap, rootPath, name, depth );
|
||||
|
||||
} else if ( stat.isFile () ) {
|
||||
|
||||
handleFile ( dirmap, rootPath, name );
|
||||
|
||||
} else if ( stat.isSymbolicLink () ) {
|
||||
|
||||
handleSymlink ( dirmap, rootPath, name, depth );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const handleDirent = ( dirmap: ResultDirectory, rootPath: string, dirent: fs.Dirent, depth: number ): void => {
|
||||
|
||||
if ( signal.aborted ) return;
|
||||
|
||||
const separator = ( rootPath === path.sep ) ? '' : path.sep;
|
||||
const name = dirent.name;
|
||||
const subPath = `${rootPath}${separator}${name}`;
|
||||
|
||||
if ( isIgnored ( subPath ) ) return;
|
||||
|
||||
if ( dirent.isDirectory () ) {
|
||||
|
||||
handleDirectory ( dirmap, subPath, name, depth );
|
||||
|
||||
} else if ( dirent.isFile () ) {
|
||||
|
||||
handleFile ( dirmap, subPath, name );
|
||||
|
||||
} else if ( dirent.isSymbolicLink () ) {
|
||||
|
||||
handleSymlink ( dirmap, subPath, name, depth );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const handleDirents = ( dirmap: ResultDirectory, rootPath: string, dirents: fs.Dirent[], depth: number ): void => {
|
||||
|
||||
for ( let i = 0, l = dirents.length; i < l; i++ ) {
|
||||
|
||||
handleDirent ( dirmap, rootPath, dirents[i], depth );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const populateResultFromPath = ( rootPath: string, depth: number ): void => {
|
||||
|
||||
if ( signal.aborted ) return;
|
||||
|
||||
if ( depth > maxDepth ) return;
|
||||
|
||||
if ( foundPaths >= maxPaths ) return;
|
||||
|
||||
increment ();
|
||||
|
||||
fs.readdir ( rootPath, { withFileTypes: true }, ( error, dirents ) => {
|
||||
|
||||
if ( error ) return decrement ();
|
||||
|
||||
if ( signal.aborted ) return decrement ();
|
||||
|
||||
if ( !dirents.length ) return decrement ();
|
||||
|
||||
const promise = onDirents ( dirents ) || NOOP_PROMISE_LIKE;
|
||||
|
||||
promise.then ( () => {
|
||||
|
||||
const dirmap = map[rootPath] = { directories: [], directoriesNames: new Set (), directoriesNamesToPaths: {}, files: [], filesNames: new Set (), filesNamesToPaths: {}, symlinks: [], symlinksNames: new Set (), symlinksNamesToPaths: {} };
|
||||
|
||||
handleDirents ( dirmap, rootPath, dirents, depth );
|
||||
|
||||
decrement ();
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
const populateResultFromSymlink = ( rootPath: string, depth: number ): void => {
|
||||
|
||||
increment ();
|
||||
|
||||
fs.realpath ( rootPath, ( error, realPath ) => {
|
||||
|
||||
if ( error ) return decrement ();
|
||||
|
||||
if ( signal.aborted ) return decrement ();
|
||||
|
||||
fs.stat ( realPath, ( error, stat ) => {
|
||||
|
||||
if ( error ) return decrement ();
|
||||
|
||||
if ( signal.aborted ) return decrement ();
|
||||
|
||||
const name = path.basename ( realPath );
|
||||
const dirmap = map[rootPath] = { directories: [], directoriesNames: new Set (), directoriesNamesToPaths: {}, files: [], filesNames: new Set (), filesNamesToPaths: {}, symlinks: [], symlinksNames: new Set (), symlinksNamesToPaths: {} };
|
||||
|
||||
handleStat ( dirmap, realPath, name, stat, depth );
|
||||
|
||||
decrement ();
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
const getResult = async ( rootPath: string, depth: number = 1 ): Promise<Result> => {
|
||||
|
||||
rootPath = path.normalize ( rootPath );
|
||||
|
||||
visited.add ( rootPath );
|
||||
|
||||
populateResultFromPath ( rootPath, depth );
|
||||
|
||||
await promise;
|
||||
|
||||
if ( signal.aborted ) return resultEmpty;
|
||||
|
||||
return result;
|
||||
|
||||
};
|
||||
|
||||
return getResult ( rootPath );
|
||||
|
||||
};
|
||||
|
||||
/* EXPORT */
|
||||
|
||||
export default readdir;
|
||||
export type {Dirent, Options, ResultDirectory, ResultDirectories, Result};
|
||||
55
node_modules/tiny-readdir/src/types.ts
generated
vendored
Normal file
55
node_modules/tiny-readdir/src/types.ts
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
|
||||
/* HELPERS */
|
||||
|
||||
type Callback = () => void;
|
||||
|
||||
type ArrayMaybe<T> = T[] | T;
|
||||
|
||||
type PromiseMaybe<T> = Promise<T> | T;
|
||||
|
||||
/* MAIN */
|
||||
|
||||
type Dirent = {
|
||||
isFile: () => boolean,
|
||||
isDirectory: () => boolean,
|
||||
isBlockDevice: () => boolean,
|
||||
isCharacterDevice: () => boolean,
|
||||
isSymbolicLink: () => boolean,
|
||||
isFIFO: () => boolean,
|
||||
isSocket: () => boolean,
|
||||
name: string,
|
||||
path: string
|
||||
};
|
||||
|
||||
type Options = {
|
||||
depth?: number,
|
||||
limit?: number,
|
||||
followSymlinks?: boolean,
|
||||
ignore?: ArrayMaybe<(( targetPath: string ) => boolean) | RegExp>,
|
||||
signal?: { aborted: boolean },
|
||||
onDirents?: ( dirents: Dirent[] ) => PromiseMaybe<undefined>
|
||||
};
|
||||
|
||||
type ResultDirectory = {
|
||||
directories: string[],
|
||||
directoriesNames: Set<string>,
|
||||
directoriesNamesToPaths: Record<string, string[]>,
|
||||
files: string[],
|
||||
filesNames: Set<string>,
|
||||
filesNamesToPaths: Record<string, string[]>,
|
||||
symlinks: string[],
|
||||
symlinksNames: Set<string>,
|
||||
symlinksNamesToPaths: Record<string, string[]>
|
||||
};
|
||||
|
||||
type ResultDirectories = {
|
||||
[path: string]: ResultDirectory
|
||||
};
|
||||
|
||||
type Result = ResultDirectory & {
|
||||
map: ResultDirectories
|
||||
};
|
||||
|
||||
/* EXPORT */
|
||||
|
||||
export type {Callback, PromiseMaybe, Dirent, Options, ResultDirectory, ResultDirectories, Result};
|
||||
18
node_modules/tiny-readdir/src/utils.ts
generated
vendored
Normal file
18
node_modules/tiny-readdir/src/utils.ts
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
/* MAIN */
|
||||
|
||||
const castArray = <T> ( value: T[] | T ): T[] => {
|
||||
|
||||
return Array.isArray ( value ) ? value : [value];
|
||||
|
||||
};
|
||||
|
||||
const isFunction = ( value: unknown ): value is Function => {
|
||||
|
||||
return ( typeof value === 'function' );
|
||||
|
||||
};
|
||||
|
||||
/* EXPORT */
|
||||
|
||||
export {castArray, isFunction};
|
||||
291
node_modules/tiny-readdir/test/index.js
generated
vendored
Normal file
291
node_modules/tiny-readdir/test/index.js
generated
vendored
Normal file
@@ -0,0 +1,291 @@
|
||||
|
||||
/* IMPORT */
|
||||
|
||||
import {describe} from 'fava';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import readdir from '../dist/index.js';
|
||||
|
||||
/* HELPERS */
|
||||
|
||||
const toBasename = filePath => path.basename ( filePath );
|
||||
|
||||
/* MAIN */
|
||||
|
||||
describe ( 'Tiny Readdir', it => {
|
||||
|
||||
it ( 'finds folders, files and symlinks', async t => {
|
||||
|
||||
const cwdPath = process.cwd ();
|
||||
const root1Path = path.join ( cwdPath, 'test', 'root1' );
|
||||
const root2Path = path.join ( cwdPath, 'test', 'root2' );
|
||||
const folder1Path = path.join ( root1Path, 'folder1' );
|
||||
const folder2Path = path.join ( root1Path, 'folder2' );
|
||||
const folder1DeepPath = path.join ( folder1Path, 'deep' );
|
||||
const file1aPath = path.join ( folder1Path, 'file1a.txt' );
|
||||
const file1bPath = path.join ( folder1Path, 'file1b.txt' );
|
||||
const file2Path = path.join ( folder2Path, 'file2.txt' );
|
||||
const fileDeep1Path = path.join ( folder1DeepPath, 'file1.txt' );
|
||||
const symlink1FromPath = path.join ( root1Path, 'symlink' );
|
||||
const symlink1ToPath = root2Path;
|
||||
const symlink2FromPath = path.join ( root2Path, 'symlink' );
|
||||
const symlink2ToPath = root1Path;
|
||||
|
||||
fs.mkdirSync ( root1Path );
|
||||
fs.mkdirSync ( root2Path );
|
||||
fs.mkdirSync ( folder1Path );
|
||||
fs.mkdirSync ( folder2Path );
|
||||
fs.mkdirSync ( folder1DeepPath );
|
||||
fs.writeFileSync ( file1aPath, '' );
|
||||
fs.writeFileSync ( file1bPath, '' );
|
||||
fs.writeFileSync ( file2Path, '' );
|
||||
fs.writeFileSync ( fileDeep1Path, '' );
|
||||
fs.symlinkSync ( symlink1ToPath, symlink1FromPath );
|
||||
fs.symlinkSync ( symlink2ToPath, symlink2FromPath );
|
||||
|
||||
const expected = {
|
||||
directories: [folder1Path, folder2Path, folder1DeepPath, root2Path],
|
||||
directoriesNames: new Set ( [folder1Path, folder2Path, folder1DeepPath, root2Path].map ( toBasename ) ),
|
||||
directoriesNamesToPaths: { folder1: [folder1Path], folder2: [folder2Path], deep: [folder1DeepPath], root2: [root2Path] },
|
||||
files: [file1aPath, file1bPath, file2Path, fileDeep1Path],
|
||||
filesNames: new Set ( [file1aPath, file1bPath, file2Path, fileDeep1Path].map ( toBasename ) ),
|
||||
filesNamesToPaths: { 'file1a.txt': [file1aPath], 'file1b.txt': [file1bPath], 'file2.txt': [file2Path], 'file1.txt': [fileDeep1Path] },
|
||||
symlinks: [symlink1FromPath, symlink2FromPath],
|
||||
symlinksNames: new Set ( [symlink1FromPath, symlink2FromPath].map ( toBasename ) ),
|
||||
symlinksNamesToPaths: { symlink: [symlink1FromPath, symlink2FromPath] },
|
||||
map: {
|
||||
[root1Path]: {
|
||||
directories: [folder1Path, folder2Path],
|
||||
directoriesNames: new Set ( [folder1Path, folder2Path].map ( toBasename ) ),
|
||||
directoriesNamesToPaths: {},
|
||||
files: [],
|
||||
filesNames: new Set (),
|
||||
filesNamesToPaths: {},
|
||||
symlinks: [symlink1FromPath],
|
||||
symlinksNames: new Set ( [symlink1FromPath].map ( toBasename ) ),
|
||||
symlinksNamesToPaths: {}
|
||||
},
|
||||
[root2Path]: {
|
||||
directories: [],
|
||||
directoriesNames: new Set (),
|
||||
directoriesNamesToPaths: {},
|
||||
files: [],
|
||||
filesNames: new Set (),
|
||||
filesNamesToPaths: {},
|
||||
symlinks: [symlink2FromPath],
|
||||
symlinksNames: new Set ( [symlink2FromPath].map ( toBasename ) ),
|
||||
symlinksNamesToPaths: {}
|
||||
},
|
||||
[folder1Path]: {
|
||||
directories: [folder1DeepPath],
|
||||
directoriesNames: new Set ( [folder1DeepPath].map ( toBasename ) ),
|
||||
directoriesNamesToPaths: {},
|
||||
files: [file1aPath, file1bPath],
|
||||
filesNames: new Set ( [file1aPath, file1bPath].map ( toBasename ) ),
|
||||
filesNamesToPaths: {},
|
||||
symlinks: [],
|
||||
symlinksNames: new Set (),
|
||||
symlinksNamesToPaths: {}
|
||||
},
|
||||
[folder2Path]: {
|
||||
directories: [],
|
||||
directoriesNames: new Set (),
|
||||
directoriesNamesToPaths: {},
|
||||
files: [file2Path],
|
||||
filesNames: new Set ( [file2Path].map ( toBasename ) ),
|
||||
filesNamesToPaths: {},
|
||||
symlinks: [],
|
||||
symlinksNames: new Set (),
|
||||
symlinksNamesToPaths: {}
|
||||
},
|
||||
[folder1DeepPath]: {
|
||||
directories: [],
|
||||
directoriesNames: new Set (),
|
||||
directoriesNamesToPaths: {},
|
||||
files: [fileDeep1Path],
|
||||
filesNames: new Set ( [fileDeep1Path].map ( toBasename ) ),
|
||||
filesNamesToPaths: {},
|
||||
symlinks: [],
|
||||
symlinksNames: new Set (),
|
||||
symlinksNamesToPaths: {}
|
||||
},
|
||||
[symlink1FromPath]: {
|
||||
directories: [root2Path],
|
||||
directoriesNames: new Set ( [root2Path].map ( toBasename ) ),
|
||||
directoriesNamesToPaths: {},
|
||||
files: [],
|
||||
filesNames: new Set (),
|
||||
filesNamesToPaths: {},
|
||||
symlinks: [],
|
||||
symlinksNames: new Set (),
|
||||
symlinksNamesToPaths: {}
|
||||
},
|
||||
[symlink2FromPath]: {
|
||||
directories: [],
|
||||
directoriesNames: new Set (),
|
||||
directoriesNamesToPaths: {},
|
||||
files: [],
|
||||
filesNames: new Set (),
|
||||
filesNamesToPaths: {},
|
||||
symlinks: [],
|
||||
symlinksNames: new Set (),
|
||||
symlinksNamesToPaths: {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
|
||||
const result = await readdir ( root1Path, { followSymlinks: true } );
|
||||
|
||||
t.deepEqual ( result, expected );
|
||||
|
||||
} finally {
|
||||
|
||||
fs.rmSync ( root1Path, { recursive: true } );
|
||||
fs.rmSync ( root2Path, { recursive: true } );
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
it ( 'supports a depth option', async t => {
|
||||
|
||||
const cwdPath = process.cwd ();
|
||||
|
||||
const {files: files0} = await readdir ( cwdPath, { depth: 0 } );
|
||||
const {files: files1} = await readdir ( cwdPath, { depth: 1 } );
|
||||
const {files: filesInfinity} = await readdir ( cwdPath, { depth: Infinity } );
|
||||
|
||||
t.true ( files0.length === 0 );
|
||||
t.true ( files1.length > 0 && files1.length < 10 );
|
||||
t.true ( filesInfinity.length > 100 );
|
||||
|
||||
});
|
||||
|
||||
it ( 'supports a limit option', async t => {
|
||||
|
||||
const cwdPath = process.cwd ();
|
||||
const root1Path = path.join ( cwdPath, 'test', 'root1' );
|
||||
const root2Path = path.join ( cwdPath, 'test', 'root2' );
|
||||
const folder1Path = path.join ( root1Path, 'folder1' );
|
||||
const folder2Path = path.join ( root1Path, 'folder2' );
|
||||
const folder1DeepPath = path.join ( folder1Path, 'deep' );
|
||||
const file1aPath = path.join ( folder1Path, 'file1a.txt' );
|
||||
const file1bPath = path.join ( folder1Path, 'file1b.txt' );
|
||||
const file2Path = path.join ( folder2Path, 'file2.txt' );
|
||||
const fileDeep1Path = path.join ( folder1DeepPath, 'file1.txt' );
|
||||
const symlink1FromPath = path.join ( root1Path, 'symlink' );
|
||||
const symlink1ToPath = root2Path;
|
||||
const symlink2FromPath = path.join ( root2Path, 'symlink' );
|
||||
const symlink2ToPath = root1Path;
|
||||
|
||||
fs.mkdirSync ( root1Path );
|
||||
fs.mkdirSync ( root2Path );
|
||||
fs.mkdirSync ( folder1Path );
|
||||
fs.mkdirSync ( folder2Path );
|
||||
fs.mkdirSync ( folder1DeepPath );
|
||||
fs.writeFileSync ( file1aPath, '' );
|
||||
fs.writeFileSync ( file1bPath, '' );
|
||||
fs.writeFileSync ( file2Path, '' );
|
||||
fs.writeFileSync ( fileDeep1Path, '' );
|
||||
fs.symlinkSync ( symlink1ToPath, symlink1FromPath );
|
||||
fs.symlinkSync ( symlink2ToPath, symlink2FromPath );
|
||||
|
||||
const expected = {
|
||||
directories: [folder1Path, folder2Path],
|
||||
directoriesNames: new Set ( [folder1Path, folder2Path].map ( toBasename ) ),
|
||||
directoriesNamesToPaths: { folder1: [folder1Path], folder2: [folder2Path] },
|
||||
files: [],
|
||||
filesNames: new Set (),
|
||||
filesNamesToPaths: {},
|
||||
symlinks: [symlink1FromPath],
|
||||
symlinksNames: new Set ( [symlink1FromPath].map ( toBasename ) ),
|
||||
symlinksNamesToPaths: { symlink: [symlink1FromPath] },
|
||||
map: {
|
||||
[root1Path]: {
|
||||
directories: [folder1Path, folder2Path],
|
||||
directoriesNames: new Set ( [folder1Path, folder2Path].map ( toBasename ) ),
|
||||
directoriesNamesToPaths: {},
|
||||
files: [],
|
||||
filesNames: new Set (),
|
||||
filesNamesToPaths: {},
|
||||
symlinks: [symlink1FromPath],
|
||||
symlinksNames: new Set ( [symlink1FromPath].map ( toBasename ) ),
|
||||
symlinksNamesToPaths: {}
|
||||
},
|
||||
[folder1Path]: {
|
||||
directories: [],
|
||||
directoriesNames: new Set (),
|
||||
directoriesNamesToPaths: {},
|
||||
files: [],
|
||||
filesNames: new Set (),
|
||||
filesNamesToPaths: {},
|
||||
symlinks: [],
|
||||
symlinksNames: new Set (),
|
||||
symlinksNamesToPaths: {}
|
||||
},
|
||||
[folder2Path]: {
|
||||
directories: [],
|
||||
directoriesNames: new Set (),
|
||||
directoriesNamesToPaths: {},
|
||||
files: [],
|
||||
filesNames: new Set (),
|
||||
filesNamesToPaths: {},
|
||||
symlinks: [],
|
||||
symlinksNames: new Set (),
|
||||
symlinksNamesToPaths: {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
|
||||
const result = await readdir ( root1Path, { limit: 3, followSymlinks: true } );
|
||||
|
||||
t.deepEqual ( result, expected );
|
||||
|
||||
} finally {
|
||||
|
||||
fs.rmSync ( root1Path, { recursive: true } );
|
||||
fs.rmSync ( root2Path, { recursive: true } );
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
it ( 'does not freeze the main thread', async t => {
|
||||
|
||||
return new Promise ( resolve => {
|
||||
|
||||
let count = 0;
|
||||
let start = Date.now ();
|
||||
|
||||
const aborter = new AbortController ();
|
||||
const signal = aborter.signal;
|
||||
|
||||
const intervalId = setInterval ( () => {
|
||||
count += 1;
|
||||
console.log ( 'tick', count );
|
||||
if ( count !== 100 ) return;
|
||||
clearInterval ( intervalId );
|
||||
const end = Date.now ();
|
||||
const elapsed = end - start;
|
||||
console.log ( 'elapsed', elapsed );
|
||||
console.log ( elapsed );
|
||||
if ( elapsed > 1500 ) {
|
||||
t.fail ();
|
||||
} else {
|
||||
t.pass ();
|
||||
}
|
||||
aborter.abort ();
|
||||
resolve ();
|
||||
}, 10 );
|
||||
|
||||
readdir ( '/', { signal } );
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
3
node_modules/tiny-readdir/tsconfig.json
generated
vendored
Executable file
3
node_modules/tiny-readdir/tsconfig.json
generated
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "tsex/tsconfig.json"
|
||||
}
|
||||
Reference in New Issue
Block a user