@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.
- package/LICENSES +16 -0
- package/README.md +106 -20
- package/bin/impl/bun.d.ts +11 -0
- package/bin/impl/bun.js +23 -0
- package/bin/impl/{node/copy.d.ts → copy.d.ts} +15 -3
- package/bin/impl/copy.js +229 -0
- package/bin/impl/create.d.ts +34 -0
- package/bin/impl/create.js +54 -0
- package/bin/impl/{node/dive.d.ts → dive.d.ts} +10 -0
- package/bin/impl/dive.js +145 -0
- package/bin/impl/empty.d.ts +28 -0
- package/bin/impl/empty.js +75 -0
- package/bin/impl/extras.d.ts +35 -0
- package/bin/impl/extras.js +112 -0
- package/bin/impl/json-utils.d.ts +30 -0
- package/bin/impl/json-utils.js +46 -0
- package/bin/impl/{node/mkdirs.js → mkdirs.js} +10 -3
- package/bin/impl/{node/move.d.ts → move.d.ts} +10 -0
- package/bin/impl/move.js +140 -0
- package/bin/impl/{node/output-file.d.ts → output-file.d.ts} +3 -2
- package/bin/impl/{node/output-json.d.ts → output-json.d.ts} +7 -2
- package/bin/impl/output-json.js +77 -0
- package/bin/impl/read-file.d.ts +31 -0
- package/bin/impl/read-file.js +165 -0
- package/bin/impl/{node/read-json.d.ts → read-json.d.ts} +9 -0
- package/bin/impl/read-json.js +241 -0
- package/bin/impl/stats.d.ts +31 -0
- package/bin/impl/stats.js +141 -0
- package/bin/impl/write-file.d.ts +31 -0
- package/bin/impl/write-file.js +257 -0
- package/bin/impl/write-json.d.ts +41 -0
- package/bin/impl/write-json.js +135 -0
- package/bin/mod.d.ts +89 -57
- package/bin/mod.js +131 -164
- package/bin/utils/json/helpers/JSONRepairError.d.ts +4 -0
- package/bin/utils/json/helpers/JSONRepairError.js +7 -0
- package/bin/utils/json/helpers/JsonSchemaError.d.ts +6 -0
- package/bin/utils/json/helpers/JsonSchemaError.js +6 -0
- package/bin/utils/json/helpers/stringUtils.d.ts +64 -0
- package/bin/utils/json/helpers/stringUtils.js +87 -0
- package/bin/utils/json/regular/jsonc.d.ts +45 -0
- package/bin/utils/json/regular/jsonc.js +88 -0
- package/bin/utils/json/regular/jsonrepair.d.ts +17 -0
- package/bin/utils/json/regular/jsonrepair.js +576 -0
- package/bin/utils/json/regular/validate.d.ts +22 -0
- package/bin/utils/json/regular/validate.js +52 -0
- package/bin/utils/json/stream/JsonStreamError.d.ts +6 -0
- package/bin/utils/json/stream/JsonStreamError.js +6 -0
- package/bin/utils/json/stream/buffer/InputBuffer.d.ts +13 -0
- package/bin/utils/json/stream/buffer/InputBuffer.js +68 -0
- package/bin/utils/json/stream/buffer/OutputBuffer.d.ts +17 -0
- package/bin/utils/json/stream/buffer/OutputBuffer.js +101 -0
- package/bin/utils/json/stream/core.d.ts +10 -0
- package/bin/utils/json/stream/core.js +695 -0
- package/bin/utils/json/stream/jsonl.d.ts +21 -0
- package/bin/utils/json/stream/jsonl.js +55 -0
- package/bin/utils/json/stream/parser.d.ts +14 -0
- package/bin/utils/json/stream/parser.js +81 -0
- package/bin/utils/json/stream/stack.d.ts +19 -0
- package/bin/utils/json/stream/stack.js +43 -0
- package/bin/utils/json/stream/stream.d.ts +6 -0
- package/bin/utils/json/stream/stream.js +30 -0
- package/bin/utils/json/stream/writer.d.ts +14 -0
- package/bin/utils/json/stream/writer.js +44 -0
- package/bin/utils/log.d.ts +1 -0
- package/bin/utils/log.js +7 -0
- package/package.json +4 -3
- package/bin/impl/node/copy.js +0 -94
- package/bin/impl/node/create-file.d.ts +0 -2
- package/bin/impl/node/create-file.js +0 -21
- package/bin/impl/node/dive.js +0 -56
- package/bin/impl/node/empty-dir.d.ts +0 -2
- package/bin/impl/node/empty-dir.js +0 -24
- package/bin/impl/node/move.js +0 -93
- package/bin/impl/node/output-json.js +0 -15
- package/bin/impl/node/read-file.d.ts +0 -30
- package/bin/impl/node/read-file.js +0 -30
- package/bin/impl/node/read-json.js +0 -50
- package/bin/impl/node/write-file.d.ts +0 -20
- package/bin/impl/node/write-file.js +0 -23
- package/bin/impl/node/write-json.d.ts +0 -28
- package/bin/impl/node/write-json.js +0 -22
- package/bin/impl/utils/additional.d.ts +0 -15
- package/bin/impl/utils/additional.js +0 -47
- /package/bin/impl/{node/mkdirs.d.ts → mkdirs.d.ts} +0 -0
- /package/bin/impl/{node/output-file.js → output-file.js} +0 -0
- /package/bin/impl/{node/path-exists.d.ts → path-exists.d.ts} +0 -0
- /package/bin/impl/{node/path-exists.js → path-exists.js} +0 -0
- /package/bin/impl/{node/remove.d.ts → remove.d.ts} +0 -0
- /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
|
|
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 ([
|
|
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
|
-
- 🧪
|
|
18
|
-
- ✌️
|
|
19
|
-
-
|
|
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 **aren
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
- ✨ Everything
|
|
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
|
|
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
|
-
## What
|
|
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 wouldn
|
|
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>;
|
package/bin/impl/bun.js
ADDED
|
@@ -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
|
-
/**
|
|
7
|
-
|
|
8
|
-
/**
|
|
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
|
}
|
package/bin/impl/copy.js
ADDED
|
@@ -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[]>;
|