@reliverse/relifso 1.4.0 → 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 +48 -17
- package/bin/impl/bun.d.ts +5 -28
- package/bin/impl/bun.js +2 -126
- package/bin/impl/copy.js +8 -7
- package/bin/impl/create.d.ts +34 -0
- package/bin/impl/create.js +54 -0
- package/bin/impl/dive.d.ts +10 -0
- package/bin/impl/dive.js +89 -0
- package/bin/impl/empty.d.ts +28 -0
- package/bin/impl/empty.js +75 -0
- package/bin/impl/extras.d.ts +22 -2
- package/bin/impl/extras.js +68 -3
- package/bin/impl/json-utils.d.ts +30 -0
- package/bin/impl/json-utils.js +46 -0
- package/bin/impl/output-file.d.ts +3 -2
- package/bin/impl/output-json.d.ts +7 -2
- package/bin/impl/output-json.js +73 -11
- package/bin/impl/read-file.d.ts +11 -0
- package/bin/impl/read-file.js +82 -4
- package/bin/impl/read-json.d.ts +6 -0
- package/bin/impl/read-json.js +133 -21
- package/bin/impl/stats.d.ts +31 -0
- package/bin/impl/stats.js +141 -0
- package/bin/impl/write-file.d.ts +19 -8
- package/bin/impl/write-file.js +218 -9
- package/bin/impl/write-json.d.ts +13 -2
- package/bin/impl/write-json.js +46 -7
- package/bin/mod.d.ts +84 -36
- package/bin/mod.js +108 -39
- 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/package.json +3 -2
- package/bin/impl/create-file.d.ts +0 -2
- package/bin/impl/create-file.js +0 -21
- package/bin/impl/dive-async.d.ts +0 -11
- package/bin/impl/dive-async.js +0 -88
- package/bin/impl/empty-dir.d.ts +0 -2
- package/bin/impl/empty-dir.js +0 -24
- package/bin/impl/file-utils.d.ts +0 -20
- package/bin/impl/file-utils.js +0 -63
- /package/bin/{impl/logger.d.ts → utils/log.d.ts} +0 -0
- /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 ([
|
|
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
|
+
- 🧪 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
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
|
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
|
-
*
|
|
8
|
+
* Get file type
|
|
9
|
+
* Uses Bun's optimized API when available, throws error otherwise
|
|
9
10
|
*/
|
|
10
|
-
export declare function
|
|
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 {
|
|
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
|
|
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 {
|
|
25
|
-
import {
|
|
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 =
|
|
44
|
+
const srcStat2 = getStatsSync(src);
|
|
44
45
|
if (!srcStat2.isDirectory()) {
|
|
45
46
|
try {
|
|
46
|
-
const destStat =
|
|
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
|
|
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
|
|
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
|
|
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
|
+
}
|
package/bin/impl/dive.d.ts
CHANGED
|
@@ -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
|
+
}
|
package/bin/impl/extras.d.ts
CHANGED
|
@@ -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
|
|
4
|
-
export declare function
|
|
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
|