@reliverse/relifso 1.3.1 → 1.4.1

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 (90) hide show
  1. package/LICENSES +16 -0
  2. package/README.md +106 -20
  3. package/bin/impl/bun.d.ts +11 -0
  4. package/bin/impl/bun.js +23 -0
  5. package/bin/impl/{node/copy.d.ts → copy.d.ts} +15 -3
  6. package/bin/impl/copy.js +229 -0
  7. package/bin/impl/create.d.ts +34 -0
  8. package/bin/impl/create.js +54 -0
  9. package/bin/impl/{node/dive.d.ts → dive.d.ts} +10 -0
  10. package/bin/impl/dive.js +145 -0
  11. package/bin/impl/empty.d.ts +28 -0
  12. package/bin/impl/empty.js +75 -0
  13. package/bin/impl/extras.d.ts +35 -0
  14. package/bin/impl/extras.js +112 -0
  15. package/bin/impl/json-utils.d.ts +30 -0
  16. package/bin/impl/json-utils.js +46 -0
  17. package/bin/impl/{node/mkdirs.js → mkdirs.js} +10 -3
  18. package/bin/impl/{node/move.d.ts → move.d.ts} +10 -0
  19. package/bin/impl/move.js +140 -0
  20. package/bin/impl/{node/output-file.d.ts → output-file.d.ts} +3 -2
  21. package/bin/impl/{node/output-json.d.ts → output-json.d.ts} +7 -2
  22. package/bin/impl/output-json.js +77 -0
  23. package/bin/impl/read-file.d.ts +31 -0
  24. package/bin/impl/read-file.js +165 -0
  25. package/bin/impl/{node/read-json.d.ts → read-json.d.ts} +9 -0
  26. package/bin/impl/read-json.js +241 -0
  27. package/bin/impl/stats.d.ts +31 -0
  28. package/bin/impl/stats.js +141 -0
  29. package/bin/impl/write-file.d.ts +31 -0
  30. package/bin/impl/write-file.js +257 -0
  31. package/bin/impl/write-json.d.ts +41 -0
  32. package/bin/impl/write-json.js +135 -0
  33. package/bin/mod.d.ts +89 -57
  34. package/bin/mod.js +131 -164
  35. package/bin/utils/json/helpers/JSONRepairError.d.ts +4 -0
  36. package/bin/utils/json/helpers/JSONRepairError.js +7 -0
  37. package/bin/utils/json/helpers/JsonSchemaError.d.ts +6 -0
  38. package/bin/utils/json/helpers/JsonSchemaError.js +6 -0
  39. package/bin/utils/json/helpers/stringUtils.d.ts +64 -0
  40. package/bin/utils/json/helpers/stringUtils.js +87 -0
  41. package/bin/utils/json/regular/jsonc.d.ts +45 -0
  42. package/bin/utils/json/regular/jsonc.js +88 -0
  43. package/bin/utils/json/regular/jsonrepair.d.ts +17 -0
  44. package/bin/utils/json/regular/jsonrepair.js +576 -0
  45. package/bin/utils/json/regular/validate.d.ts +22 -0
  46. package/bin/utils/json/regular/validate.js +52 -0
  47. package/bin/utils/json/stream/JsonStreamError.d.ts +6 -0
  48. package/bin/utils/json/stream/JsonStreamError.js +6 -0
  49. package/bin/utils/json/stream/buffer/InputBuffer.d.ts +13 -0
  50. package/bin/utils/json/stream/buffer/InputBuffer.js +68 -0
  51. package/bin/utils/json/stream/buffer/OutputBuffer.d.ts +17 -0
  52. package/bin/utils/json/stream/buffer/OutputBuffer.js +101 -0
  53. package/bin/utils/json/stream/core.d.ts +10 -0
  54. package/bin/utils/json/stream/core.js +695 -0
  55. package/bin/utils/json/stream/jsonl.d.ts +21 -0
  56. package/bin/utils/json/stream/jsonl.js +55 -0
  57. package/bin/utils/json/stream/parser.d.ts +14 -0
  58. package/bin/utils/json/stream/parser.js +81 -0
  59. package/bin/utils/json/stream/stack.d.ts +19 -0
  60. package/bin/utils/json/stream/stack.js +43 -0
  61. package/bin/utils/json/stream/stream.d.ts +6 -0
  62. package/bin/utils/json/stream/stream.js +30 -0
  63. package/bin/utils/json/stream/writer.d.ts +14 -0
  64. package/bin/utils/json/stream/writer.js +44 -0
  65. package/bin/utils/log.d.ts +1 -0
  66. package/bin/utils/log.js +7 -0
  67. package/package.json +4 -3
  68. package/bin/impl/node/copy.js +0 -94
  69. package/bin/impl/node/create-file.d.ts +0 -2
  70. package/bin/impl/node/create-file.js +0 -21
  71. package/bin/impl/node/dive.js +0 -56
  72. package/bin/impl/node/empty-dir.d.ts +0 -2
  73. package/bin/impl/node/empty-dir.js +0 -24
  74. package/bin/impl/node/move.js +0 -93
  75. package/bin/impl/node/output-json.js +0 -15
  76. package/bin/impl/node/read-file.d.ts +0 -30
  77. package/bin/impl/node/read-file.js +0 -30
  78. package/bin/impl/node/read-json.js +0 -50
  79. package/bin/impl/node/write-file.d.ts +0 -20
  80. package/bin/impl/node/write-file.js +0 -23
  81. package/bin/impl/node/write-json.d.ts +0 -28
  82. package/bin/impl/node/write-json.js +0 -22
  83. package/bin/impl/utils/additional.d.ts +0 -15
  84. package/bin/impl/utils/additional.js +0 -47
  85. /package/bin/impl/{node/mkdirs.d.ts → mkdirs.d.ts} +0 -0
  86. /package/bin/impl/{node/output-file.js → output-file.js} +0 -0
  87. /package/bin/impl/{node/path-exists.d.ts → path-exists.d.ts} +0 -0
  88. /package/bin/impl/{node/path-exists.js → path-exists.js} +0 -0
  89. /package/bin/impl/{node/remove.d.ts → remove.d.ts} +0 -0
  90. /package/bin/impl/{node/remove.js → remove.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,26 +1,27 @@
1
- # Relifso • Node.js Filesystem Toolkit Library
2
-
3
- > @reliverse/relifso is a modern filesystem toolkit for builders. drop-in replacement for `node:fs` and `fs-extra` — powered by native promises, built with es modules, and packed with dx-focused utilities.
1
+ # Relifso • Bun & Node.js Filesystem Toolkit Library
4
2
 
5
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)
6
4
 
5
+ > @reliverse/relifso is a modern node and bun filesystem toolkit. drop-in replacement for `node:fs` and `fs-extra` — powered by native promises, built with es modules, and packed with dx-focused and bun-aware utilities.
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
- - 🤝 Forget about `try-catch` for common errors like file not found — relifso does it under the hood
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 ([3.9 kB](https://bundlephobia.com/package/@reliverse/relifso@latest)), zero deps, modern code, no monkey-patching
16
+ - 🧼 Zero bloat — small size ([6 kB](https://bundlephobia.com/package/@reliverse/relifso@latest)), 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**: Bun v1.2+ ready — ships with Bun-aware enhancements out of the box
19
- - 🔥 **Soon**: Bun-specific features are exposed via `fs.*` when running on Bun
18
+ - 🧪 Soon: Ready for upcoming Node.js v22+ experimental features
19
+ - ✌️ Bun v1.2+ ready — ships with Bun-aware enhancements out of the box
20
+ - 🐦‍🔥 Finally! Your `fs.*` usage is now can correctly read/write JSON/JSONC!
20
21
 
21
22
  ## Heads Up
22
23
 
23
- - **Most of the things** mentioned in this doc **arent implemented yet** — theyre part of the vision for ~`v1.3.0`.
24
+ - **Most of the things** mentioned in this doc **aren't implemented yet** — they're part of the vision for ~`v1.5.0`.
24
25
  - Got thoughts? Ideas? Send your feedback in [Discord](https://discord.gg/Pb8uKbwpsJ) or use [GitHub Issues](https://github.com/reliverse/relifso/issues).
25
26
  - Your feedback means the world and helps shape where this project goes next. Thank you!
26
27
 
@@ -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
-
47
- ```bash
48
- bun add -D @reliverse/dler
49
- bun dler relifso init ...
50
- ```
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
78
+
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
 
@@ -65,9 +103,9 @@ if (await pathExists("dist/index.ts")) {
65
103
  }
66
104
  ```
67
105
 
68
- - ✨ Everythings bundled — modern, async, and type-safe.
106
+ - ✨ Everything's bundled — modern, async, and type-safe.
69
107
  - 🧼 No more boilerplate like `promisify(fs.removeSync)` or using `mkdirp`, `ncp`, or `rimraf`.
70
- - 🌱 No more weird `try/catch` for common errors like file not found.”
108
+ - 🌱 No more weird `try/catch` for common errors like "file not found."
71
109
  - ✌️ Just clean, predictable APIs built for 2025 and beyond.
72
110
 
73
111
  ## Example
@@ -118,7 +156,7 @@ All async methods return a `Promise` if no callback is passed.
118
156
  - Compatible with Node.js 16+, best with 22+
119
157
  - Async methods are built from the sync versions — no wrappers, no bloat
120
158
 
121
- ## Whats Inside?
159
+ ## What's Inside?
122
160
 
123
161
  - All async methods follow the `Promise` pattern by default.
124
162
  - All sync methods are safe and throw errors when needed.
@@ -181,7 +219,7 @@ All async methods return a `Promise` if no callback is passed.
181
219
  - [isSymlink](https://uwx-node-modules.github.io/fsxt/functions/isSymlink.html)
182
220
  - [~~lchmod~~](https://uwx-node-modules.github.io/fsxt/functions/lchmod.html)
183
221
  - [lchown](https://uwx-node-modules.github.io/fsxt/functions/lchown.html)
184
- - [link](https://uwx-node-modules.github.io/fsxt/functions/link.html)
222
+ - [`link`](https://uwx-node-modules.github.io/fsxt/functions/link.html)
185
223
  - [lstat](https://uwx-node-modules.github.io/fsxt/functions/lstat.html)
186
224
  - [lutimes](https://uwx-node-modules.github.io/fsxt/functions/lutimes.html)
187
225
  - [mapChildren](https://uwx-node-modules.github.io/fsxt/functions/mapChildren.html)
@@ -270,6 +308,53 @@ All async methods return a `Promise` if no callback is passed.
270
308
  - [utimesSync](https://uwx-node-modules.github.io/fsxt/functions/utimesSync.html)
271
309
  - [writevSync](https://uwx-node-modules.github.io/fsxt/functions/writevSync.html)
272
310
 
311
+ ## Bun Integration
312
+
313
+ Relifso provides first-class support for Bun with automatic fallbacks to Node.js APIs. Here's how it works:
314
+
315
+ ### Automatic Runtime Detection
316
+
317
+ ```ts
318
+ import { isBun } from "@reliverse/relifso";
319
+
320
+ if (isBun) {
321
+ console.log("Running in Bun!");
322
+ } else {
323
+ console.log("Running in Node.js");
324
+ }
325
+ ```
326
+
327
+ ### Optimized File Operations
328
+
329
+ When running in Bun, relifso automatically uses Bun's optimized file system APIs:
330
+
331
+ - `Bun.file()` for file operations
332
+ - Native file existence checks
333
+ - Optimized file size and type detection
334
+ - Fast last modified time access
335
+
336
+ ### Graceful Fallbacks
337
+
338
+ All Bun-specific operations include automatic fallbacks to Node.js APIs:
339
+
340
+ ```ts
341
+ import { getStats } from "@reliverse/relifso";
342
+
343
+ // In Bun: Uses Bun.file() for faster stats
344
+ // In Node.js: Falls back to fs.stat()
345
+ const stats = await getStats("file.txt");
346
+ ```
347
+
348
+ ### Available Bun-Specific Utilities
349
+
350
+ - `getFile(path)` - Get a Bun file reference
351
+ - `exists(path)` - Check file existence using Bun's API
352
+ - `size(path)` - Get file size using Bun's API
353
+ - `type(path)` - Get file MIME type using Bun's API
354
+ - `lastModified(path)` - Get file last modified time
355
+ - `getStats(path)` - Get file stats with Bun optimization
356
+ - `getStatsSync(path)` - Synchronous version of getStats
357
+
273
358
  ## Contributing
274
359
 
275
360
  ...
@@ -282,10 +367,11 @@ All async methods return a `Promise` if no callback is passed.
282
367
  - [ ] Pass all `fs-extra` tests with Bun (+ fix & improve them).
283
368
  - [ ] Convert all jsdoc comments to TypeScript types.
284
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`.
285
371
 
286
372
  ## Shoutouts
287
373
 
288
- Relifso wouldnt be so cool without these gems:
374
+ Relifso wouldn't be so cool without these gems:
289
375
 
290
376
  - [`node:fs`](https://nodejs.org/api/fs.html)+[`node:path`](https://nodejs.org/api/path.html) — origins
291
377
  - [`fs-extra`](https://github.com/jprichardson/node-fs-extra) — classic, reliable
@@ -0,0 +1,11 @@
1
+ export declare const isBun: string | false;
2
+ /**
3
+ * Get a file reference with validation and error handling
4
+ * Uses Bun's optimized API when available, throws error otherwise
5
+ */
6
+ export declare function getFileBun(path: string): Bun.BunFile;
7
+ /**
8
+ * Get file type
9
+ * Uses Bun's optimized API when available, throws error otherwise
10
+ */
11
+ export declare function getFileTypeBun(path: string): Promise<string>;
@@ -0,0 +1,23 @@
1
+ import { logInternal } from "../utils/log.js";
2
+ export const isBun = typeof process !== "undefined" && process.versions.bun;
3
+ export function getFileBun(path) {
4
+ if (!isBun) {
5
+ throw new Error("Bun runtime not detected");
6
+ }
7
+ try {
8
+ return Bun.file(path);
9
+ } catch (error) {
10
+ throw error;
11
+ }
12
+ }
13
+ export async function getFileTypeBun(path) {
14
+ if (!isBun) {
15
+ throw new Error("Bun runtime not detected");
16
+ }
17
+ try {
18
+ const file = Bun.file(path);
19
+ return file.type;
20
+ } catch (error) {
21
+ throw error;
22
+ }
23
+ }
@@ -1,12 +1,24 @@
1
1
  export interface CopyOptions {
2
+ /** Whether to overwrite existing files. Default: true */
3
+ force?: boolean;
4
+ /** Whether to overwrite existing files. Alias for force. Default: true */
2
5
  overwrite?: boolean;
3
6
  /** @deprecated Use `overwrite`. */
4
7
  clobber?: boolean;
8
+ /** Whether to preserve timestamps. Default: false */
5
9
  preserveTimestamps?: boolean;
6
- /** @deprecated Not used. */
7
- errorOnExist?: boolean;
8
- /** @deprecated Not used. */
10
+ /** Whether to recursively copy directories. Default: false */
11
+ recursive?: boolean;
12
+ /** Whether to dereference symlinks. Default: false */
9
13
  dereference?: boolean;
14
+ /** Whether to throw an error if the destination exists. Default: false */
15
+ errorOnExist?: boolean;
16
+ /** Whether to ensure source exists before copying (default: true) */
17
+ ensureSource?: boolean;
18
+ /** Whether to ensure destination directory exists (default: true) */
19
+ ensureDest?: boolean;
20
+ /** Whether to verify operation success (default: true) */
21
+ verify?: boolean;
10
22
  /** @deprecated Not used. */
11
23
  filter?: (src: string, dest: string) => boolean;
12
24
  }
@@ -0,0 +1,229 @@
1
+ import {
2
+ copyFileSync,
3
+ statSync,
4
+ constants as fsConstants,
5
+ readdirSync,
6
+ rmSync,
7
+ lstatSync,
8
+ readlinkSync,
9
+ symlinkSync
10
+ } from "node:fs";
11
+ import {
12
+ stat as statAsync,
13
+ copyFile as copyFileAsync,
14
+ constants as fsConstantsAsync,
15
+ readdir,
16
+ rm,
17
+ lstat,
18
+ readlink,
19
+ symlink
20
+ } from "node:fs/promises";
21
+ import { dirname, join as joinPath } from "node:path";
22
+ import { mkdirsSync } from "./mkdirs.js";
23
+ import { mkdirs } from "./mkdirs.js";
24
+ import { logInternal } from "../utils/log.js";
25
+ import { isBun, getFileBun } from "./bun.js";
26
+ import { pathExists, pathExistsSync } from "./path-exists.js";
27
+ import { getStats, getStatsSync } from "./stats.js";
28
+ export function copySync(src, dest, options = {}) {
29
+ const {
30
+ overwrite = options.clobber ?? true,
31
+ force = true,
32
+ recursive = false,
33
+ dereference = false,
34
+ errorOnExist = false,
35
+ ensureSource = true,
36
+ ensureDest = true,
37
+ verify = true
38
+ } = options;
39
+ if (ensureSource && !pathExistsSync(src)) {
40
+ throw new Error(`Source ${src} does not exist.`);
41
+ }
42
+ if (isBun && !dereference) {
43
+ try {
44
+ const srcStat2 = getStatsSync(src);
45
+ if (!srcStat2.isDirectory()) {
46
+ try {
47
+ const destStat = getStatsSync(dest);
48
+ if (destStat) {
49
+ if (errorOnExist) {
50
+ throw new Error(`Destination ${dest} already exists.`);
51
+ }
52
+ if (!force && !overwrite) {
53
+ throw new Error(`Destination ${dest} already exists and overwrite is false.`);
54
+ }
55
+ if (force || overwrite) {
56
+ rmSync(dest, { force: true });
57
+ }
58
+ }
59
+ } catch (_error) {
60
+ }
61
+ if (ensureDest) {
62
+ const destDir = dirname(dest);
63
+ mkdirsSync(destDir);
64
+ }
65
+ try {
66
+ copyFileSync(src, dest, fsConstants.COPYFILE_FICLONE);
67
+ if (verify && !pathExistsSync(dest)) {
68
+ throw new Error(`Copy operation failed: destination ${dest} does not exist after copy`);
69
+ }
70
+ return;
71
+ } catch (error) {
72
+ }
73
+ }
74
+ } catch (error) {
75
+ }
76
+ }
77
+ const srcStat = dereference ? statSync(src, { throwIfNoEntry: true }) : lstatSync(src, { throwIfNoEntry: true });
78
+ if (srcStat.isDirectory()) {
79
+ if (!recursive) {
80
+ throw new Error(`Cannot copy directory ${src} without recursive flag`);
81
+ }
82
+ if (ensureDest) {
83
+ mkdirsSync(dest);
84
+ }
85
+ const entries = readdirSync(src, { withFileTypes: true });
86
+ for (const entry of entries) {
87
+ const srcPath = joinPath(src, entry.name);
88
+ const destPath = joinPath(dest, entry.name);
89
+ if (entry.isDirectory()) {
90
+ copySync(srcPath, destPath, options);
91
+ } else if (entry.isFile()) {
92
+ copyFileSync(srcPath, destPath, fsConstants.COPYFILE_FICLONE);
93
+ } else if (entry.isSymbolicLink()) {
94
+ const target = readlinkSync(srcPath);
95
+ symlinkSync(target, destPath);
96
+ }
97
+ }
98
+ if (verify && !pathExistsSync(dest)) {
99
+ throw new Error(`Copy operation failed: destination directory ${dest} does not exist after copy`);
100
+ }
101
+ } else {
102
+ if (ensureDest) {
103
+ mkdirsSync(dirname(dest));
104
+ }
105
+ const destExists = statSync(dest, { throwIfNoEntry: false });
106
+ if (destExists) {
107
+ if (errorOnExist) {
108
+ throw new Error(`Destination ${dest} already exists.`);
109
+ }
110
+ if (!force && !overwrite) {
111
+ throw new Error(`Destination ${dest} already exists and overwrite is false.`);
112
+ }
113
+ if (force || overwrite) {
114
+ rmSync(dest, { force: true });
115
+ }
116
+ }
117
+ copyFileSync(src, dest, fsConstants.COPYFILE_FICLONE);
118
+ if (verify && !pathExistsSync(dest)) {
119
+ throw new Error(`Copy operation failed: destination file ${dest} does not exist after copy`);
120
+ }
121
+ }
122
+ }
123
+ export async function copy(src, dest, options = {}) {
124
+ const {
125
+ overwrite = options.clobber ?? true,
126
+ force = true,
127
+ recursive = false,
128
+ dereference = false,
129
+ errorOnExist = false,
130
+ ensureSource = true,
131
+ ensureDest = true,
132
+ verify = true
133
+ } = options;
134
+ if (ensureSource && !await pathExists(src)) {
135
+ throw new Error(`Source ${src} does not exist.`);
136
+ }
137
+ if (isBun && !dereference) {
138
+ try {
139
+ const srcStat2 = await getStats(src);
140
+ if (!srcStat2.isDirectory()) {
141
+ const srcFile = getFileBun(src);
142
+ const destFile = getFileBun(dest);
143
+ try {
144
+ const destStat = await getStats(dest);
145
+ if (destStat) {
146
+ if (errorOnExist) {
147
+ throw new Error(`Destination ${dest} already exists.`);
148
+ }
149
+ if (!force && !overwrite) {
150
+ throw new Error(`Destination ${dest} already exists and overwrite is false.`);
151
+ }
152
+ if (force || overwrite) {
153
+ await rm(dest, { force: true });
154
+ }
155
+ }
156
+ } catch (_error) {
157
+ }
158
+ if (ensureDest) {
159
+ const destDir = dirname(dest);
160
+ await mkdirs(destDir);
161
+ }
162
+ try {
163
+ const content = await srcFile?.text();
164
+ await Bun.write(destFile, content);
165
+ if (verify && !await pathExists(dest)) {
166
+ throw new Error(`Bun write failed: destination ${dest} does not exist after write`);
167
+ }
168
+ return;
169
+ } catch (error) {
170
+ }
171
+ }
172
+ } catch (error) {
173
+ }
174
+ }
175
+ const srcStat = await (dereference ? statAsync(src) : lstat(src)).catch((e) => {
176
+ if (e.code === "ENOENT") return null;
177
+ throw e;
178
+ });
179
+ if (!srcStat) {
180
+ throw new Error(`Source ${src} does not exist`);
181
+ }
182
+ if (srcStat.isDirectory()) {
183
+ if (!recursive) {
184
+ throw new Error(`Cannot copy directory ${src} without recursive flag`);
185
+ }
186
+ if (ensureDest) {
187
+ await mkdirs(dest);
188
+ }
189
+ const entries = await readdir(src, { withFileTypes: true });
190
+ for (const entry of entries) {
191
+ const srcPath = joinPath(src, entry.name);
192
+ const destPath = joinPath(dest, entry.name);
193
+ if (entry.isDirectory()) {
194
+ await copy(srcPath, destPath, options);
195
+ } else if (entry.isFile()) {
196
+ await copyFileAsync(srcPath, destPath, fsConstantsAsync.COPYFILE_FICLONE);
197
+ } else if (entry.isSymbolicLink()) {
198
+ const target = await readlink(srcPath);
199
+ await symlink(target, destPath);
200
+ }
201
+ }
202
+ if (verify && !await pathExists(dest)) {
203
+ throw new Error(`Copy operation failed: destination directory ${dest} does not exist after copy`);
204
+ }
205
+ } else {
206
+ if (ensureDest) {
207
+ await mkdirs(dirname(dest));
208
+ }
209
+ const destExists = await statAsync(dest).catch((e) => {
210
+ if (e.code === "ENOENT") return null;
211
+ throw e;
212
+ });
213
+ if (destExists) {
214
+ if (errorOnExist) {
215
+ throw new Error(`Destination ${dest} already exists.`);
216
+ }
217
+ if (!force && !overwrite) {
218
+ throw new Error(`Destination ${dest} already exists and overwrite is false.`);
219
+ }
220
+ if (force || overwrite) {
221
+ await rm(dest, { force: true });
222
+ }
223
+ }
224
+ await copyFileAsync(src, dest, fsConstantsAsync.COPYFILE_FICLONE);
225
+ if (verify && !await pathExists(dest)) {
226
+ throw new Error(`Copy operation failed: destination file ${dest} does not exist after copy`);
227
+ }
228
+ }
229
+ }
@@ -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[]>;