@reliverse/relifso 1.4.0 → 1.4.2

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.
Files changed (71) hide show
  1. package/LICENSES +16 -0
  2. package/README.md +48 -17
  3. package/bin/impl/bun.d.ts +5 -28
  4. package/bin/impl/bun.js +2 -126
  5. package/bin/impl/copy.js +8 -7
  6. package/bin/impl/create.d.ts +34 -0
  7. package/bin/impl/create.js +54 -0
  8. package/bin/impl/dive.d.ts +10 -0
  9. package/bin/impl/dive.js +89 -0
  10. package/bin/impl/empty.d.ts +28 -0
  11. package/bin/impl/empty.js +75 -0
  12. package/bin/impl/extras.d.ts +22 -2
  13. package/bin/impl/extras.js +68 -3
  14. package/bin/impl/json-utils.d.ts +30 -0
  15. package/bin/impl/json-utils.js +46 -0
  16. package/bin/impl/output-file.d.ts +3 -2
  17. package/bin/impl/output-json.d.ts +7 -2
  18. package/bin/impl/output-json.js +73 -11
  19. package/bin/impl/read-file.d.ts +15 -4
  20. package/bin/impl/read-file.js +88 -10
  21. package/bin/impl/read-json.d.ts +6 -0
  22. package/bin/impl/read-json.js +133 -21
  23. package/bin/impl/stats.d.ts +31 -0
  24. package/bin/impl/stats.js +141 -0
  25. package/bin/impl/write-file.d.ts +19 -8
  26. package/bin/impl/write-file.js +153 -24
  27. package/bin/impl/write-json.d.ts +13 -2
  28. package/bin/impl/write-json.js +46 -7
  29. package/bin/mod.d.ts +84 -36
  30. package/bin/mod.js +108 -39
  31. package/bin/utils/json/helpers/JSONRepairError.d.ts +4 -0
  32. package/bin/utils/json/helpers/JSONRepairError.js +7 -0
  33. package/bin/utils/json/helpers/JsonSchemaError.d.ts +6 -0
  34. package/bin/utils/json/helpers/JsonSchemaError.js +6 -0
  35. package/bin/utils/json/helpers/stringUtils.d.ts +64 -0
  36. package/bin/utils/json/helpers/stringUtils.js +87 -0
  37. package/bin/utils/json/regular/jsonc.d.ts +45 -0
  38. package/bin/utils/json/regular/jsonc.js +88 -0
  39. package/bin/utils/json/regular/jsonrepair.d.ts +17 -0
  40. package/bin/utils/json/regular/jsonrepair.js +576 -0
  41. package/bin/utils/json/regular/validate.d.ts +22 -0
  42. package/bin/utils/json/regular/validate.js +52 -0
  43. package/bin/utils/json/stream/JsonStreamError.d.ts +6 -0
  44. package/bin/utils/json/stream/JsonStreamError.js +6 -0
  45. package/bin/utils/json/stream/buffer/InputBuffer.d.ts +13 -0
  46. package/bin/utils/json/stream/buffer/InputBuffer.js +68 -0
  47. package/bin/utils/json/stream/buffer/OutputBuffer.d.ts +17 -0
  48. package/bin/utils/json/stream/buffer/OutputBuffer.js +101 -0
  49. package/bin/utils/json/stream/core.d.ts +10 -0
  50. package/bin/utils/json/stream/core.js +695 -0
  51. package/bin/utils/json/stream/jsonl.d.ts +21 -0
  52. package/bin/utils/json/stream/jsonl.js +55 -0
  53. package/bin/utils/json/stream/parser.d.ts +14 -0
  54. package/bin/utils/json/stream/parser.js +81 -0
  55. package/bin/utils/json/stream/stack.d.ts +19 -0
  56. package/bin/utils/json/stream/stack.js +43 -0
  57. package/bin/utils/json/stream/stream.d.ts +6 -0
  58. package/bin/utils/json/stream/stream.js +30 -0
  59. package/bin/utils/json/stream/writer.d.ts +14 -0
  60. package/bin/utils/json/stream/writer.js +44 -0
  61. package/package.json +3 -2
  62. package/bin/impl/create-file.d.ts +0 -2
  63. package/bin/impl/create-file.js +0 -21
  64. package/bin/impl/dive-async.d.ts +0 -11
  65. package/bin/impl/dive-async.js +0 -88
  66. package/bin/impl/empty-dir.d.ts +0 -2
  67. package/bin/impl/empty-dir.js +0 -24
  68. package/bin/impl/file-utils.d.ts +0 -20
  69. package/bin/impl/file-utils.js +0 -63
  70. /package/bin/{impl/logger.d.ts → utils/log.d.ts} +0 -0
  71. /package/bin/{impl/logger.js → utils/log.js} +0 -0
package/LICENSES ADDED
@@ -0,0 +1,16 @@
1
+ LICENSES
2
+
3
+ ---
4
+
5
+ MIT License
6
+
7
+ Copyright (c) Nazar Kornienko (blefnk), Reliverse
8
+
9
+ See LICENSE file for full license text.
10
+
11
+ ---
12
+
13
+ NOTICES
14
+
15
+ Some parts of this project are based on and significantly adapt:
16
+ - https://github.com/josdejong/jsonrepair/tree/acab2a4 – ISC © Jos de Jong (josdejong)
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Relifso • Node.js Filesystem Toolkit Library
1
+ # Relifso • Bun & Node.js Filesystem Toolkit Library
2
2
 
3
3
  [sponsor](https://github.com/sponsors/blefnk) — [discord](https://discord.gg/Pb8uKbwpsJ) — [npm](https://npmjs.com/package/@reliverse/relifso) — [github](https://github.com/reliverse/relifso)
4
4
 
@@ -6,17 +6,18 @@
6
6
 
7
7
  ## Features
8
8
 
9
+ - 🔥 Both Node.js and Bun-specific filesystem features are exposed via `fs.*`
9
10
  - 🪄 Everything you love from `fs-extra` — now simpler, cleaner, and more beginner-friendly
10
11
  - ⚙️ Drop-in replacement for `node:fs` — with native `Promise`, `async/await`, and sync variants
11
12
  - 🤝 Forget about `try-catch` for common errors like "file not found" — relifso does it under the hood
12
13
  - 🧯 Gracefully handles errors like `EMFILE` (reading or writing a lot of files at once) and other edge cases
13
14
  - 📚 Consistent error-first behavior — even for legacy APIs like `fs.exists()`
14
15
  - 📦 First-class ESM and full TypeScript support — no config hacks required
15
- - 🧼 Zero bloat — small size ([4 kB](https://bundlephobia.com/package/@reliverse/relifso@latest)), zero deps, modern code, no monkey-patching
16
+ - 🧼 Zero bloat — small size, zero deps, modern code, no monkey-patching
16
17
  - 🎯 Supports all Node.js v16+ features — optimized for Node.js v22+
17
- - 🧪 **Soon**: Ready for upcoming Node.js v22+ experimental features
18
+ - 🧪 Soon: Ready for upcoming Node.js v22+ experimental features
18
19
  - ✌️ Bun v1.2+ ready — ships with Bun-aware enhancements out of the box
19
- - 🔥 Bun-specific features are exposed via `fs.*` when running on Bun
20
+ - 🐦‍🔥 Finally! Your `fs.*` usage is now can correctly read/write JSON/JSONC!
20
21
 
21
22
  ## Heads Up
22
23
 
@@ -42,12 +43,49 @@ bun rm @types/fs-extra
42
43
  # bun dler relifso fs-extra to relifso
43
44
  ```
44
45
 
45
- **Coming soon**:
46
+ ### Core Features
47
+
48
+ - **File Operations**
49
+ - Read/write files with various encodings
50
+ - Copy/move files and directories
51
+ - Create/remove files and directories
52
+ - File existence checks
53
+ - File stats and metadata access
54
+
55
+ - **Directory Operations**
56
+ - Create nested directories
57
+ - Empty directories
58
+ - Directory traversal with `dive`
59
+ - Directory existence checks
60
+
61
+ - **JSON Operations**
62
+ - Read/write JSON files with validation
63
+ - JSON repair and validation utilities
64
+ - JSON streaming support
65
+ - JSONC (JSON with Comments) support
66
+
67
+ - **Bun Optimizations**
68
+ - Automatic runtime detection
69
+ - Optimized file operations using Bun APIs
70
+ - Fast file stats and metadata access
71
+ - Graceful fallbacks to Node.js APIs
72
+
73
+ - **Utility Functions**
74
+ - File type detection
75
+ - Hidden file attribute handling
76
+ - Directory emptiness checks
77
+ - File size and last modified time access
46
78
 
47
- ```bash
48
- bun add -D @reliverse/dler
49
- bun dler relifso init ...
50
- ```
79
+ ### Error Handling
80
+
81
+ - Graceful handling of common filesystem errors
82
+ - Consistent error types and messages
83
+ - Automatic error recovery where possible
84
+ - Detailed error information for debugging
85
+ - Runtime detection errors
86
+ - File operation failures
87
+ - All Bun-specific operations include proper error handling
88
+ - Automatic fallback from Bun to Node.js APIs when needed
51
89
 
52
90
  ## Usage
53
91
 
@@ -317,14 +355,6 @@ const stats = await getStats("file.txt");
317
355
  - `getStats(path)` - Get file stats with Bun optimization
318
356
  - `getStatsSync(path)` - Synchronous version of getStats
319
357
 
320
- ### Error Handling
321
-
322
- All Bun-specific operations include proper error handling:
323
-
324
- - Runtime detection errors
325
- - File operation failures
326
- - Automatic fallback to Node.js APIs when needed
327
-
328
358
  ## Contributing
329
359
 
330
360
  ...
@@ -337,6 +367,7 @@ All Bun-specific operations include proper error handling:
337
367
  - [ ] Pass all `fs-extra` tests with Bun (+ fix & improve them).
338
368
  - [ ] Convert all jsdoc comments to TypeScript types.
339
369
  - [ ] Fully improve all `fs-extra` codebase files.
370
+ - [ ] In [docs.reliverse.org](https://docs.reliverse.org) implement feature and performance comparison table with `fs-extra`.
340
371
 
341
372
  ## Shoutouts
342
373
 
package/bin/impl/bun.d.ts CHANGED
@@ -1,34 +1,11 @@
1
- import type { Stats } from "node:fs";
2
1
  export declare const isBun: string | false;
3
2
  /**
4
- * Get a Bun file reference with validation and error handling
3
+ * Get a file reference with validation and error handling
4
+ * Uses Bun's optimized API when available, throws error otherwise
5
5
  */
6
6
  export declare function getFileBun(path: string): Bun.BunFile;
7
7
  /**
8
- * Check if a file exists using Bun's optimized API
8
+ * Get file type
9
+ * Uses Bun's optimized API when available, throws error otherwise
9
10
  */
10
- export declare function existsBun(path: string): Promise<boolean>;
11
- /**
12
- * Get file size using Bun's optimized API
13
- */
14
- export declare function sizeBun(path: string): Promise<number>;
15
- /**
16
- * Get file type using Bun's optimized API
17
- */
18
- export declare function typeBun(path: string): Promise<string>;
19
- /**
20
- * Get file last modified time using Bun's optimized API
21
- */
22
- export declare function lastModifiedBun(path: string): Promise<Date>;
23
- /**
24
- * Convert Bun file stats to Node.js Stats object
25
- */
26
- export declare function toNodeStatsBun(path: string): Promise<Stats>;
27
- /**
28
- * Get file stats using Bun's optimized API with fallback to Node.js
29
- */
30
- export declare function getStatsBun(path: string): Promise<Stats>;
31
- /**
32
- * Get file stats synchronously using Bun's optimized API with fallback to Node.js
33
- */
34
- export declare function getStatsSyncBun(path: string): Stats;
11
+ export declare function getFileTypeBun(path: string): Promise<string>;
package/bin/impl/bun.js CHANGED
@@ -1,6 +1,4 @@
1
- import { statSync } from "node:fs";
2
- import { stat } from "node:fs/promises";
3
- import { logInternal } from "./logger.js";
1
+ import { logInternal } from "../utils/log.js";
4
2
  export const isBun = typeof process !== "undefined" && process.versions.bun;
5
3
  export function getFileBun(path) {
6
4
  if (!isBun) {
@@ -12,29 +10,7 @@ export function getFileBun(path) {
12
10
  throw error;
13
11
  }
14
12
  }
15
- export async function existsBun(path) {
16
- if (!isBun) {
17
- throw new Error("Bun runtime not detected");
18
- }
19
- try {
20
- const file = Bun.file(path);
21
- return await file.exists();
22
- } catch (error) {
23
- return false;
24
- }
25
- }
26
- export async function sizeBun(path) {
27
- if (!isBun) {
28
- throw new Error("Bun runtime not detected");
29
- }
30
- try {
31
- const file = Bun.file(path);
32
- return file.size;
33
- } catch (error) {
34
- throw error;
35
- }
36
- }
37
- export async function typeBun(path) {
13
+ export async function getFileTypeBun(path) {
38
14
  if (!isBun) {
39
15
  throw new Error("Bun runtime not detected");
40
16
  }
@@ -45,103 +21,3 @@ export async function typeBun(path) {
45
21
  throw error;
46
22
  }
47
23
  }
48
- export async function lastModifiedBun(path) {
49
- if (!isBun) {
50
- throw new Error("Bun runtime not detected");
51
- }
52
- try {
53
- const file = Bun.file(path);
54
- return new Date(file.lastModified);
55
- } catch (error) {
56
- throw error;
57
- }
58
- }
59
- export async function toNodeStatsBun(path) {
60
- if (!isBun) {
61
- throw new Error("Bun runtime not detected");
62
- }
63
- try {
64
- const file = Bun.file(path);
65
- const size = file.size;
66
- const lastModified = new Date(file.lastModified);
67
- return {
68
- dev: 0,
69
- ino: 0,
70
- mode: 0,
71
- nlink: 0,
72
- uid: 0,
73
- gid: 0,
74
- rdev: 0,
75
- size,
76
- blksize: 0,
77
- blocks: 0,
78
- atimeMs: lastModified.getTime(),
79
- mtimeMs: lastModified.getTime(),
80
- ctimeMs: lastModified.getTime(),
81
- birthtimeMs: lastModified.getTime(),
82
- atime: lastModified,
83
- mtime: lastModified,
84
- ctime: lastModified,
85
- birthtime: lastModified,
86
- isDirectory: () => false,
87
- // Bun doesn't provide this info directly
88
- isFile: () => true,
89
- isBlockDevice: () => false,
90
- isCharacterDevice: () => false,
91
- isSymbolicLink: () => false,
92
- isFIFO: () => false,
93
- isSocket: () => false
94
- };
95
- } catch (error) {
96
- throw error;
97
- }
98
- }
99
- export async function getStatsBun(path) {
100
- if (!isBun) {
101
- return stat(path);
102
- }
103
- try {
104
- return await toNodeStatsBun(path);
105
- } catch (error) {
106
- return stat(path);
107
- }
108
- }
109
- export function getStatsSyncBun(path) {
110
- if (!isBun) {
111
- return statSync(path);
112
- }
113
- try {
114
- const file = Bun.file(path);
115
- const size = file.size;
116
- const lastModified = new Date(file.lastModified);
117
- return {
118
- dev: 0,
119
- ino: 0,
120
- mode: 0,
121
- nlink: 0,
122
- uid: 0,
123
- gid: 0,
124
- rdev: 0,
125
- size,
126
- blksize: 0,
127
- blocks: 0,
128
- atimeMs: lastModified.getTime(),
129
- mtimeMs: lastModified.getTime(),
130
- ctimeMs: lastModified.getTime(),
131
- birthtimeMs: lastModified.getTime(),
132
- atime: lastModified,
133
- mtime: lastModified,
134
- ctime: lastModified,
135
- birthtime: lastModified,
136
- isDirectory: () => false,
137
- isFile: () => true,
138
- isBlockDevice: () => false,
139
- isCharacterDevice: () => false,
140
- isSymbolicLink: () => false,
141
- isFIFO: () => false,
142
- isSocket: () => false
143
- };
144
- } catch (error) {
145
- return statSync(path);
146
- }
147
- }
package/bin/impl/copy.js CHANGED
@@ -21,9 +21,10 @@ import {
21
21
  import { dirname, join as joinPath } from "node:path";
22
22
  import { mkdirsSync } from "./mkdirs.js";
23
23
  import { mkdirs } from "./mkdirs.js";
24
- import { isBun, getFileBun, getStatsBun, getStatsSyncBun } from "./bun.js";
25
- import { logInternal } from "./logger.js";
24
+ import { logInternal } from "../utils/log.js";
25
+ import { isBun, getFileBun } from "./bun.js";
26
26
  import { pathExists, pathExistsSync } from "./path-exists.js";
27
+ import { getStats, getStatsSync } from "./stats.js";
27
28
  export function copySync(src, dest, options = {}) {
28
29
  const {
29
30
  overwrite = options.clobber ?? true,
@@ -40,10 +41,10 @@ export function copySync(src, dest, options = {}) {
40
41
  }
41
42
  if (isBun && !dereference) {
42
43
  try {
43
- const srcStat2 = getStatsSyncBun(src);
44
+ const srcStat2 = getStatsSync(src);
44
45
  if (!srcStat2.isDirectory()) {
45
46
  try {
46
- const destStat = getStatsSyncBun(dest);
47
+ const destStat = getStatsSync(dest);
47
48
  if (destStat) {
48
49
  if (errorOnExist) {
49
50
  throw new Error(`Destination ${dest} already exists.`);
@@ -135,12 +136,12 @@ export async function copy(src, dest, options = {}) {
135
136
  }
136
137
  if (isBun && !dereference) {
137
138
  try {
138
- const srcStat2 = await getStatsBun(src);
139
+ const srcStat2 = await getStats(src);
139
140
  if (!srcStat2.isDirectory()) {
140
141
  const srcFile = getFileBun(src);
141
142
  const destFile = getFileBun(dest);
142
143
  try {
143
- const destStat = await getStatsBun(dest);
144
+ const destStat = await getStats(dest);
144
145
  if (destStat) {
145
146
  if (errorOnExist) {
146
147
  throw new Error(`Destination ${dest} already exists.`);
@@ -159,7 +160,7 @@ export async function copy(src, dest, options = {}) {
159
160
  await mkdirs(destDir);
160
161
  }
161
162
  try {
162
- const content = await srcFile.text();
163
+ const content = await srcFile?.text();
163
164
  await Bun.write(destFile, content);
164
165
  if (verify && !await pathExists(dest)) {
165
166
  throw new Error(`Bun write failed: destination ${dest} does not exist after write`);
@@ -0,0 +1,34 @@
1
+ export declare function createFileSync(file: string, content?: string): void;
2
+ export declare function createFile(file: string, content?: string): Promise<void>;
3
+ /**
4
+ * Creates a single directory if it doesn't exist
5
+ * @param dir Path to the directory to create
6
+ */
7
+ export declare function createDir(dir: string): Promise<void>;
8
+ /**
9
+ * Creates multiple directories if they don't exist
10
+ * @param dirs Array of directory paths to create
11
+ */
12
+ export declare function createDirs(dirs: string[]): Promise<void>;
13
+ /**
14
+ * Creates multiple files with optional content
15
+ * @param files Array of file paths to create
16
+ * @param content Optional content to write to each file
17
+ */
18
+ export declare function createFiles(files: string[], content?: string): Promise<void>;
19
+ /**
20
+ * Synchronous version of createDir
21
+ * @param dir Path to the directory to create
22
+ */
23
+ export declare function createDirSync(dir: string): void;
24
+ /**
25
+ * Synchronous version of createDirs
26
+ * @param dirs Array of directory paths to create
27
+ */
28
+ export declare function createDirsSync(dirs: string[]): void;
29
+ /**
30
+ * Synchronous version of createFiles
31
+ * @param files Array of file paths to create
32
+ * @param content Optional content to write to each file
33
+ */
34
+ export declare function createFilesSync(files: string[], content?: string): void;
@@ -0,0 +1,54 @@
1
+ import { existsSync, mkdirSync } from "node:fs";
2
+ import { stat, mkdir } from "node:fs/promises";
3
+ import { writeFileSync } from "./write-file.js";
4
+ import { writeFile } from "./write-file.js";
5
+ export function createFileSync(file, content = "") {
6
+ if (existsSync(file)) {
7
+ return;
8
+ }
9
+ return writeFileSync(file, content);
10
+ }
11
+ export async function createFile(file, content = "") {
12
+ try {
13
+ await stat(file);
14
+ return;
15
+ } catch (error) {
16
+ if (error.code === "ENOENT") {
17
+ return writeFile(file, content);
18
+ }
19
+ throw error;
20
+ }
21
+ }
22
+ export async function createDir(dir) {
23
+ try {
24
+ await stat(dir);
25
+ return;
26
+ } catch (error) {
27
+ if (error.code === "ENOENT") {
28
+ await mkdir(dir, { recursive: true });
29
+ } else {
30
+ throw error;
31
+ }
32
+ }
33
+ }
34
+ export async function createDirs(dirs) {
35
+ await Promise.all(dirs.map((dir) => createDir(dir)));
36
+ }
37
+ export async function createFiles(files, content = "") {
38
+ await Promise.all(files.map((file) => createFile(file, content)));
39
+ }
40
+ export function createDirSync(dir) {
41
+ if (!existsSync(dir)) {
42
+ mkdirSync(dir, { recursive: true });
43
+ }
44
+ }
45
+ export function createDirsSync(dirs) {
46
+ for (const dir of dirs) {
47
+ createDirSync(dir);
48
+ }
49
+ }
50
+ export function createFilesSync(files, content = "") {
51
+ for (const file of files) {
52
+ createFileSync(file, content);
53
+ }
54
+ }
@@ -1,3 +1,4 @@
1
+ import type { Stats } from "node:fs";
1
2
  import { statSync } from "node:fs";
2
3
  export interface DiveOptions {
3
4
  all?: boolean;
@@ -15,3 +16,12 @@ export interface DiveOptions {
15
16
  * @param options - Options for the dive operation if a callback is provided as the second argument.
16
17
  */
17
18
  export declare function diveSync(dir: string, callbackOrOptions?: ((path: string, stat: ReturnType<typeof statSync>) => void) | DiveOptions, options?: DiveOptions): Generator<string, void, unknown>;
19
+ /**
20
+ * Recursively dives into a directory and yields files and directories.
21
+ * @param directory - The directory to dive into.
22
+ * @param action - An optional callback function to execute for each file or directory.
23
+ * @param options - An optional object containing options for the dive.
24
+ * @returns A Promise that resolves to an array of file paths if no action is provided, or void if an action is provided.
25
+ */
26
+ export declare function dive(directory: string, action: (file: string, stat: Stats) => void | Promise<void>, options?: DiveOptions): Promise<void>;
27
+ export declare function dive(directory: string, options?: DiveOptions): Promise<string[]>;
package/bin/impl/dive.js CHANGED
@@ -1,5 +1,9 @@
1
1
  import { readdirSync, statSync } from "node:fs";
2
+ import { readdir as nodeReaddirInternal, stat as nodeStatInternal } from "node:fs/promises";
2
3
  import { join } from "node:path";
4
+ import { join as pathJoin } from "node:path";
5
+ import { isBun } from "./bun.js";
6
+ import { getStats } from "./stats.js";
3
7
  export function* diveSync(dir, callbackOrOptions, options) {
4
8
  const currentDepth = 0;
5
9
  let actualOptions = {
@@ -54,3 +58,88 @@ export function* diveSync(dir, callbackOrOptions, options) {
54
58
  }
55
59
  yield* walk(dir, currentDepth);
56
60
  }
61
+ async function* _diveWorker(currentPath, options, currentDepth) {
62
+ const maxDepth = options.depth ?? Number.POSITIVE_INFINITY;
63
+ if (currentDepth > maxDepth) {
64
+ return;
65
+ }
66
+ let entries;
67
+ try {
68
+ entries = await nodeReaddirInternal(currentPath, { withFileTypes: true });
69
+ } catch (_err) {
70
+ return;
71
+ }
72
+ for (const entry of entries) {
73
+ const entryPath = pathJoin(currentPath, entry.name);
74
+ if (!(options.all ?? false) && entry.name.startsWith(".")) {
75
+ continue;
76
+ }
77
+ if (options.ignore) {
78
+ if (Array.isArray(options.ignore) && options.ignore.some((pattern) => entry.name.includes(pattern))) {
79
+ continue;
80
+ }
81
+ if (options.ignore instanceof RegExp && options.ignore.test(entryPath)) {
82
+ continue;
83
+ }
84
+ }
85
+ let entryStat;
86
+ try {
87
+ if (isBun) {
88
+ try {
89
+ entryStat = await getStats(entryPath);
90
+ } catch (_error) {
91
+ entryStat = await nodeStatInternal(entryPath);
92
+ }
93
+ } else {
94
+ entryStat = await nodeStatInternal(entryPath);
95
+ }
96
+ } catch (_err) {
97
+ continue;
98
+ }
99
+ if (entry.isDirectory()) {
100
+ if (options.directories ?? false) {
101
+ yield { file: entryPath, stat: entryStat };
102
+ }
103
+ if (options.recursive ?? true) {
104
+ if (currentDepth < maxDepth) {
105
+ yield* _diveWorker(entryPath, options, currentDepth + 1);
106
+ }
107
+ }
108
+ } else if (entry.isFile()) {
109
+ if (options.files ?? true) {
110
+ yield { file: entryPath, stat: entryStat };
111
+ }
112
+ }
113
+ }
114
+ }
115
+ export async function dive(directory, actionOrOptions, optionsOnly) {
116
+ let action;
117
+ let options;
118
+ if (typeof actionOrOptions === "function") {
119
+ action = actionOrOptions;
120
+ options = optionsOnly;
121
+ } else {
122
+ options = actionOrOptions;
123
+ }
124
+ const currentOptions = {
125
+ recursive: true,
126
+ files: true,
127
+ directories: false,
128
+ all: false,
129
+ depth: Number.POSITIVE_INFINITY,
130
+ ...options
131
+ // User options override defaults
132
+ };
133
+ if (action) {
134
+ for await (const { file, stat: entryStat } of _diveWorker(directory, currentOptions, 0)) {
135
+ await action(file, entryStat);
136
+ }
137
+ return;
138
+ } else {
139
+ const results = [];
140
+ for await (const { file } of _diveWorker(directory, currentOptions, 0)) {
141
+ results.push(file);
142
+ }
143
+ return results;
144
+ }
145
+ }
@@ -0,0 +1,28 @@
1
+ interface EmptyFileOptions {
2
+ /**
3
+ * If true, will replace all object literals with empty objects {}
4
+ * Only works with JSON files
5
+ */
6
+ emptyObjects?: boolean;
7
+ }
8
+ export declare function emptyDirSync(dir: string): void;
9
+ export declare function emptyDir(dir: string): Promise<void>;
10
+ /**
11
+ * Empties an object by removing all its properties
12
+ * @param obj The object to empty
13
+ * @returns The empty object
14
+ */
15
+ export declare function emptyObject<T extends object>(obj: T): T;
16
+ /**
17
+ * Empties a file by removing all its contents
18
+ * @param filePath Path to the file
19
+ * @param options Options for emptying the file
20
+ */
21
+ export declare function emptyFile(filePath: string, options?: EmptyFileOptions): Promise<void>;
22
+ /**
23
+ * Synchronous version of emptyFile
24
+ * @param filePath Path to the file
25
+ * @param options Options for emptying the file
26
+ */
27
+ export declare function emptyFileSync(filePath: string, options?: EmptyFileOptions): void;
28
+ export {};
@@ -0,0 +1,75 @@
1
+ import { existsSync, readdirSync, rmSync, writeFileSync, readFileSync } from "node:fs";
2
+ import { readdir, rm, stat, writeFile, readFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ export function emptyDirSync(dir) {
5
+ if (!existsSync(dir)) {
6
+ return;
7
+ }
8
+ for (const file of readdirSync(dir)) {
9
+ rmSync(path.resolve(dir, file), { recursive: true, force: true });
10
+ }
11
+ }
12
+ export async function emptyDir(dir) {
13
+ try {
14
+ await stat(dir);
15
+ } catch (error) {
16
+ if (error.code === "ENOENT") {
17
+ return;
18
+ }
19
+ throw error;
20
+ }
21
+ for (const file of await readdir(dir)) {
22
+ await rm(path.resolve(dir, file), { recursive: true, force: true });
23
+ }
24
+ }
25
+ export function emptyObject(obj) {
26
+ for (const key in obj) {
27
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
28
+ delete obj[key];
29
+ }
30
+ }
31
+ return obj;
32
+ }
33
+ export async function emptyFile(filePath, options = {}) {
34
+ try {
35
+ await stat(filePath);
36
+ } catch (error) {
37
+ if (error.code === "ENOENT") {
38
+ return;
39
+ }
40
+ throw error;
41
+ }
42
+ if (options.emptyObjects && filePath.toLowerCase().endsWith(".json")) {
43
+ try {
44
+ const content = await readFile(filePath, "utf-8");
45
+ const data = JSON.parse(content);
46
+ if (typeof data === "object" && data !== null) {
47
+ const emptyData = emptyObject(data);
48
+ await writeFile(filePath, JSON.stringify(emptyData, null, 2));
49
+ return;
50
+ }
51
+ } catch (error) {
52
+ console.warn(`Failed to parse JSON file ${filePath}, falling back to regular file emptying:`, error);
53
+ }
54
+ }
55
+ await writeFile(filePath, "");
56
+ }
57
+ export function emptyFileSync(filePath, options = {}) {
58
+ if (!existsSync(filePath)) {
59
+ return;
60
+ }
61
+ if (options.emptyObjects && filePath.toLowerCase().endsWith(".json")) {
62
+ try {
63
+ const content = readFileSync(filePath, "utf-8");
64
+ const data = JSON.parse(content);
65
+ if (typeof data === "object" && data !== null) {
66
+ const emptyData = emptyObject(data);
67
+ writeFileSync(filePath, JSON.stringify(emptyData, null, 2));
68
+ return;
69
+ }
70
+ } catch (error) {
71
+ console.warn(`Failed to parse JSON file ${filePath}, falling back to regular file emptying:`, error);
72
+ }
73
+ }
74
+ writeFileSync(filePath, "");
75
+ }
@@ -1,7 +1,27 @@
1
1
  import { exec } from "node:child_process";
2
+ export declare function readText(filePath: string, options?: BufferEncoding | {
3
+ encoding?: BufferEncoding | null;
4
+ flag?: string;
5
+ }): Promise<string>;
6
+ export declare function readTextSync(filePath: string, options?: BufferEncoding | {
7
+ encoding?: BufferEncoding | null;
8
+ flag?: string;
9
+ }): string;
10
+ export declare function readLines(filePath: string, options?: BufferEncoding | {
11
+ encoding?: BufferEncoding | null;
12
+ flag?: string;
13
+ }): Promise<string[]>;
14
+ export declare function readLinesSync(filePath: string, options?: BufferEncoding | {
15
+ encoding?: BufferEncoding | null;
16
+ flag?: string;
17
+ }): string[];
18
+ export declare function isDirectory(filePath: string): Promise<boolean>;
19
+ export declare function isDirectorySync(filePath: string): boolean;
20
+ export declare function isSymlink(filePath: string): Promise<boolean>;
21
+ export declare function isSymlinkSync(filePath: string): boolean;
2
22
  export declare const execAsync: typeof exec.__promisify__;
3
- export declare function setHiddenAttributeOnWindows(folderPath: string): Promise<void>;
4
- export declare function isHidden(filePath: string): Promise<boolean>;
23
+ export declare function setHiddenAttribute(folderPath: string): Promise<void>;
24
+ export declare function isHiddenAttribute(filePath: string): Promise<boolean>;
5
25
  /**
6
26
  * Checks if a directory is empty
7
27
  * @param directory Path to the directory