@theholocron/fs-utils 1.12.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -1
- package/dist/index.d.mts +18 -0
- package/dist/index.mjs +51 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,7 +11,14 @@ pnpm add @theholocron/fs-utils
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
findExecutable,
|
|
16
|
+
findExecutableSync,
|
|
17
|
+
findRoot,
|
|
18
|
+
findUpward,
|
|
19
|
+
hasGitEntry,
|
|
20
|
+
walkFilesSync,
|
|
21
|
+
} from "@theholocron/fs-utils";
|
|
15
22
|
|
|
16
23
|
// nearest ancestor holding a marker file or directory
|
|
17
24
|
const repoRoot = await findRoot(process.cwd(), ".git");
|
|
@@ -84,6 +91,21 @@ than "not there" / "not permitted".
|
|
|
84
91
|
const tsc = findExecutableSync("tsc", { path: join(cwd, "node_modules", ".bin") }) ?? findExecutableSync("tsc");
|
|
85
92
|
```
|
|
86
93
|
|
|
94
|
+
### `walkFilesSync(dir, options?)`
|
|
95
|
+
|
|
96
|
+
Every regular file under `dir`, as absolute paths in a stable (name-sorted,
|
|
97
|
+
depth-first) order. `options.skip` names the directories not entered, at any
|
|
98
|
+
depth (default `.git` and `node_modules`; it replaces the default); a _file_
|
|
99
|
+
with that name is still listed. Symlinks found while walking are neither
|
|
100
|
+
followed nor listed, so a link back up the tree can't loop; `dir` itself is
|
|
101
|
+
followed if it is a symlink. A subdirectory that can't be read is skipped; `dir`
|
|
102
|
+
itself must be readable — its error propagates. The tree is assumed not to
|
|
103
|
+
change mid-walk.
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
const sources = walkFilesSync(repoRoot, { skip: [".git", "node_modules", "dist"] });
|
|
107
|
+
```
|
|
108
|
+
|
|
87
109
|
### `hasGitEntry(dir)`
|
|
88
110
|
|
|
89
111
|
Returns `true` when `dir` has a `.git` entry — a directory in a normal
|
package/dist/index.d.mts
CHANGED
|
@@ -88,4 +88,22 @@ export declare function findUpward<T>(startDir: string, check: (dir: string) =>
|
|
|
88
88
|
* nearest `.git` as the project root, searched but not walked past.
|
|
89
89
|
*/
|
|
90
90
|
export declare function hasGitEntry(dir: string): Promise<boolean>;
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region src/walk-files.d.ts
|
|
93
|
+
export interface WalkFilesOptions {
|
|
94
|
+
/** Directory names not descended into, at any depth (files with these names are still listed). Defaults to `.git` and `node_modules`. */
|
|
95
|
+
skip?: Iterable<string>;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Every regular file under `dir`, as absolute paths in a stable (name-sorted,
|
|
99
|
+
* depth-first) order. Directories named in `options.skip` are not entered.
|
|
100
|
+
*
|
|
101
|
+
* Symlinks found while walking are neither followed nor listed, so a link back
|
|
102
|
+
* up the tree can't loop; `dir` itself is the caller's choice and is followed
|
|
103
|
+
* if it is a symlink. A subdirectory that can't be read is skipped; `dir`
|
|
104
|
+
* itself must be readable — its error propagates, since a typo'd root is a bug
|
|
105
|
+
* to surface, not an empty result. The tree is assumed not to change while it
|
|
106
|
+
* is walked: an entry swapped for a symlink mid-walk is not detected.
|
|
107
|
+
*/
|
|
108
|
+
export declare function walkFilesSync(dir: string, options?: WalkFilesOptions): string[];
|
|
91
109
|
//#endregion
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { accessSync, constants, realpathSync, statSync } from "node:fs";
|
|
1
|
+
import { accessSync, constants, lstatSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
2
2
|
import { access, realpath, stat } from "node:fs/promises";
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
4
|
//#region src/find-executable.ts
|
|
@@ -190,4 +190,53 @@ function findRootSync(startDir, marker, options = {}) {
|
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
//#endregion
|
|
193
|
-
|
|
193
|
+
//#region src/walk-files.ts
|
|
194
|
+
const DEFAULT_SKIP = [".git", "node_modules"];
|
|
195
|
+
/** The entry's type, asking the filesystem when the platform's directory listing couldn't say. */
|
|
196
|
+
function entryKind(entry, full) {
|
|
197
|
+
if (entry.isDirectory()) return "directory";
|
|
198
|
+
if (entry.isFile()) return "file";
|
|
199
|
+
if (entry.isSymbolicLink()) return "other";
|
|
200
|
+
try {
|
|
201
|
+
const stats = lstatSync(full);
|
|
202
|
+
return stats.isDirectory() ? "directory" : stats.isFile() ? "file" : "other";
|
|
203
|
+
} catch {
|
|
204
|
+
return "other";
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Every regular file under `dir`, as absolute paths in a stable (name-sorted,
|
|
209
|
+
* depth-first) order. Directories named in `options.skip` are not entered.
|
|
210
|
+
*
|
|
211
|
+
* Symlinks found while walking are neither followed nor listed, so a link back
|
|
212
|
+
* up the tree can't loop; `dir` itself is the caller's choice and is followed
|
|
213
|
+
* if it is a symlink. A subdirectory that can't be read is skipped; `dir`
|
|
214
|
+
* itself must be readable — its error propagates, since a typo'd root is a bug
|
|
215
|
+
* to surface, not an empty result. The tree is assumed not to change while it
|
|
216
|
+
* is walked: an entry swapped for a symlink mid-walk is not detected.
|
|
217
|
+
*/
|
|
218
|
+
function walkFilesSync(dir, options = {}) {
|
|
219
|
+
const skip = new Set(options.skip ?? DEFAULT_SKIP);
|
|
220
|
+
const files = [];
|
|
221
|
+
function walk(current, isRoot) {
|
|
222
|
+
let entries;
|
|
223
|
+
try {
|
|
224
|
+
entries = readdirSync(current, { withFileTypes: true });
|
|
225
|
+
} catch (error) {
|
|
226
|
+
if (isRoot) throw error;
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
entries.sort((a, b) => Number(a.name > b.name) - Number(a.name < b.name));
|
|
230
|
+
for (const entry of entries) {
|
|
231
|
+
const full = join(current, entry.name);
|
|
232
|
+
const kind = entryKind(entry, full);
|
|
233
|
+
if (kind === "directory") {
|
|
234
|
+
if (!skip.has(entry.name)) walk(full, false);
|
|
235
|
+
} else if (kind === "file") files.push(full);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
walk(resolve(dir), true);
|
|
239
|
+
return files;
|
|
240
|
+
}
|
|
241
|
+
//#endregion
|
|
242
|
+
export { findExecutable, findExecutableSync, findRoot, findRootSync, findUpward, hasGitEntry, walkFilesSync };
|