@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.
- 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 +15 -4
- package/bin/impl/read-file.js +88 -10
- 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 +153 -24
- 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/bin/impl/read-json.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
1
|
+
import { readFileSync, createReadStream } from "node:fs";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { extname } from "node:path";
|
|
4
|
+
import { Transform } from "node:stream";
|
|
5
|
+
import { pipeline } from "node:stream/promises";
|
|
6
|
+
import { parseJsonc, isValidJsonc } from "../utils/json/regular/jsonc.js";
|
|
7
|
+
import { jsonrepair } from "../utils/json/regular/jsonrepair.js";
|
|
8
|
+
import { logInternal } from "../utils/log.js";
|
|
3
9
|
import { isBun, getFileBun } from "./bun.js";
|
|
4
|
-
import {
|
|
5
|
-
import { outputJsonSync } from "./output-json.js";
|
|
10
|
+
import { ensureJsonFileSync } from "./json-utils.js";
|
|
6
11
|
import { pathExistsSync } from "./path-exists.js";
|
|
7
12
|
export function readJsonSync(file, options) {
|
|
8
13
|
let encoding = "utf8";
|
|
@@ -10,6 +15,7 @@ export function readJsonSync(file, options) {
|
|
|
10
15
|
let throws = true;
|
|
11
16
|
let defaultValue = { sync: true };
|
|
12
17
|
let ensure = true;
|
|
18
|
+
let preserveComments = false;
|
|
13
19
|
if (typeof options === "string") {
|
|
14
20
|
encoding = options;
|
|
15
21
|
} else if (options) {
|
|
@@ -24,30 +30,81 @@ export function readJsonSync(file, options) {
|
|
|
24
30
|
if (options.ensure !== void 0) {
|
|
25
31
|
ensure = options.ensure;
|
|
26
32
|
}
|
|
33
|
+
if (options.preserveComments !== void 0) {
|
|
34
|
+
preserveComments = options.preserveComments;
|
|
35
|
+
}
|
|
27
36
|
}
|
|
28
37
|
try {
|
|
29
38
|
if (!pathExistsSync(file) && ensure) {
|
|
30
|
-
|
|
39
|
+
ensureJsonFileSync(file, defaultValue);
|
|
31
40
|
return defaultValue;
|
|
32
41
|
}
|
|
42
|
+
if (isBun) {
|
|
43
|
+
try {
|
|
44
|
+
const fileRef = getFileBun(file);
|
|
45
|
+
try {
|
|
46
|
+
const parsed = fileRef.json();
|
|
47
|
+
if (parsed instanceof Promise) {
|
|
48
|
+
throw new Error("Bun's json() returned a Promise in sync context");
|
|
49
|
+
}
|
|
50
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
51
|
+
if (throws) {
|
|
52
|
+
throw new Error("Invalid JSON data: expected an object");
|
|
53
|
+
}
|
|
54
|
+
return defaultValue;
|
|
55
|
+
}
|
|
56
|
+
return parsed;
|
|
57
|
+
} catch (parseError) {
|
|
58
|
+
if (throws) {
|
|
59
|
+
throw parseError;
|
|
60
|
+
}
|
|
61
|
+
return defaultValue;
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
}
|
|
65
|
+
}
|
|
33
66
|
const data = readFileSync(file, { encoding });
|
|
34
67
|
if (!data || !data.trim()) {
|
|
35
68
|
return defaultValue;
|
|
36
69
|
}
|
|
70
|
+
const isJsonc = extname(file).toLowerCase() === ".jsonc" || isValidJsonc(data);
|
|
37
71
|
try {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (
|
|
41
|
-
|
|
72
|
+
if (isJsonc) {
|
|
73
|
+
const parsed = parseJsonc(data, { preserveComments, throws });
|
|
74
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
75
|
+
if (throws) {
|
|
76
|
+
throw new Error("Invalid JSONC data: expected an object");
|
|
77
|
+
}
|
|
78
|
+
return defaultValue;
|
|
42
79
|
}
|
|
43
|
-
return
|
|
80
|
+
return parsed;
|
|
81
|
+
} else {
|
|
82
|
+
const parsed = JSON.parse(data, reviver);
|
|
83
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
84
|
+
if (throws) {
|
|
85
|
+
throw new Error("Invalid JSON data: expected an object");
|
|
86
|
+
}
|
|
87
|
+
return defaultValue;
|
|
88
|
+
}
|
|
89
|
+
return parsed;
|
|
44
90
|
}
|
|
45
|
-
return parsed;
|
|
46
91
|
} catch (parseError) {
|
|
47
|
-
|
|
48
|
-
|
|
92
|
+
try {
|
|
93
|
+
const repaired = jsonrepair(data);
|
|
94
|
+
const parsed = JSON.parse(repaired, reviver);
|
|
95
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
96
|
+
if (throws) {
|
|
97
|
+
throw parseError;
|
|
98
|
+
}
|
|
99
|
+
return defaultValue;
|
|
100
|
+
}
|
|
101
|
+
return parsed;
|
|
102
|
+
} catch (_repairError) {
|
|
103
|
+
if (throws) {
|
|
104
|
+
throw parseError;
|
|
105
|
+
}
|
|
106
|
+
return defaultValue;
|
|
49
107
|
}
|
|
50
|
-
return defaultValue;
|
|
51
108
|
}
|
|
52
109
|
} catch (err) {
|
|
53
110
|
if (throws) {
|
|
@@ -62,12 +119,14 @@ export async function readJson(file, options) {
|
|
|
62
119
|
let throws = true;
|
|
63
120
|
let flag;
|
|
64
121
|
let defaultValue = {};
|
|
122
|
+
let useStreaming = false;
|
|
65
123
|
if (typeof options === "string") {
|
|
66
124
|
encoding = options;
|
|
67
125
|
} else if (options) {
|
|
68
126
|
encoding = options.encoding ?? encoding;
|
|
69
127
|
reviver = options.reviver;
|
|
70
128
|
flag = options.flag;
|
|
129
|
+
useStreaming = options.useStreaming ?? useStreaming;
|
|
71
130
|
if (options.throws !== void 0) {
|
|
72
131
|
throws = options.throws;
|
|
73
132
|
}
|
|
@@ -80,11 +139,7 @@ export async function readJson(file, options) {
|
|
|
80
139
|
try {
|
|
81
140
|
const fileRef = getFileBun(file);
|
|
82
141
|
try {
|
|
83
|
-
const
|
|
84
|
-
if (!content || !content.trim()) {
|
|
85
|
-
return defaultValue;
|
|
86
|
-
}
|
|
87
|
-
const parsed = JSON.parse(content, reviver);
|
|
142
|
+
const parsed = await fileRef.json();
|
|
88
143
|
if (parsed === null || typeof parsed !== "object") {
|
|
89
144
|
if (throws) {
|
|
90
145
|
throw new Error("Invalid JSON data: expected an object");
|
|
@@ -101,6 +156,51 @@ export async function readJson(file, options) {
|
|
|
101
156
|
} catch (error) {
|
|
102
157
|
}
|
|
103
158
|
}
|
|
159
|
+
if (useStreaming) {
|
|
160
|
+
try {
|
|
161
|
+
const chunks = [];
|
|
162
|
+
const transform = new Transform({
|
|
163
|
+
transform(chunk, _encoding, callback) {
|
|
164
|
+
chunks.push(chunk.toString());
|
|
165
|
+
callback();
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
const readStream = createReadStream(file, { encoding, flags: flag });
|
|
169
|
+
await pipeline(readStream, transform);
|
|
170
|
+
const data2 = chunks.join("");
|
|
171
|
+
if (!data2 || !data2.trim()) {
|
|
172
|
+
return defaultValue;
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
const parsed = JSON.parse(data2, reviver);
|
|
176
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
177
|
+
if (throws) {
|
|
178
|
+
throw new Error("Invalid JSON data: expected an object");
|
|
179
|
+
}
|
|
180
|
+
return defaultValue;
|
|
181
|
+
}
|
|
182
|
+
return parsed;
|
|
183
|
+
} catch (parseError) {
|
|
184
|
+
try {
|
|
185
|
+
const repaired = jsonrepair(data2);
|
|
186
|
+
const parsed = JSON.parse(repaired, reviver);
|
|
187
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
188
|
+
if (throws) {
|
|
189
|
+
throw parseError;
|
|
190
|
+
}
|
|
191
|
+
return defaultValue;
|
|
192
|
+
}
|
|
193
|
+
return parsed;
|
|
194
|
+
} catch (_repairError) {
|
|
195
|
+
if (throws) {
|
|
196
|
+
throw parseError;
|
|
197
|
+
}
|
|
198
|
+
return defaultValue;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
} catch (streamError) {
|
|
202
|
+
}
|
|
203
|
+
}
|
|
104
204
|
const data = await readFile(file, { encoding, flag });
|
|
105
205
|
if (!data || !data.trim()) {
|
|
106
206
|
return defaultValue;
|
|
@@ -115,10 +215,22 @@ export async function readJson(file, options) {
|
|
|
115
215
|
}
|
|
116
216
|
return parsed;
|
|
117
217
|
} catch (parseError) {
|
|
118
|
-
|
|
119
|
-
|
|
218
|
+
try {
|
|
219
|
+
const repaired = jsonrepair(data);
|
|
220
|
+
const parsed = JSON.parse(repaired, reviver);
|
|
221
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
222
|
+
if (throws) {
|
|
223
|
+
throw parseError;
|
|
224
|
+
}
|
|
225
|
+
return defaultValue;
|
|
226
|
+
}
|
|
227
|
+
return parsed;
|
|
228
|
+
} catch (_repairError) {
|
|
229
|
+
if (throws) {
|
|
230
|
+
throw parseError;
|
|
231
|
+
}
|
|
232
|
+
return defaultValue;
|
|
120
233
|
}
|
|
121
|
-
return defaultValue;
|
|
122
234
|
}
|
|
123
235
|
} catch (err) {
|
|
124
236
|
if (throws) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Stats } from "node:fs";
|
|
2
|
+
/**
|
|
3
|
+
* Convert Bun file stats to Node.js Stats object
|
|
4
|
+
* Uses Bun's optimized API when available, falls back to Node.js
|
|
5
|
+
*/
|
|
6
|
+
export declare function toNodeStats(path: string): Promise<Stats>;
|
|
7
|
+
/**
|
|
8
|
+
* Get file stats with fallback to Node.js
|
|
9
|
+
* Uses Bun's optimized API when available
|
|
10
|
+
*/
|
|
11
|
+
export declare function getStats(path: string): Promise<Stats>;
|
|
12
|
+
/**
|
|
13
|
+
* Get file stats synchronously with fallback to Node.js
|
|
14
|
+
* Uses Bun's optimized API when available
|
|
15
|
+
*/
|
|
16
|
+
export declare function getStatsSync(path: string): Stats;
|
|
17
|
+
/**
|
|
18
|
+
* Check if a file exists
|
|
19
|
+
* Uses Bun's optimized API when available, falls back to Node.js
|
|
20
|
+
*/
|
|
21
|
+
export declare function getFileExists(path: string): Promise<boolean>;
|
|
22
|
+
/**
|
|
23
|
+
* Get file size
|
|
24
|
+
* Uses Bun's optimized API when available, falls back to Node.js
|
|
25
|
+
*/
|
|
26
|
+
export declare function getFileSize(path: string): Promise<number>;
|
|
27
|
+
/**
|
|
28
|
+
* Get file last modified time
|
|
29
|
+
* Uses Bun's optimized API when available, falls back to Node.js
|
|
30
|
+
*/
|
|
31
|
+
export declare function getFileLastModified(path: string): Promise<Date>;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { statSync } from "node:fs";
|
|
2
|
+
import { stat } from "node:fs/promises";
|
|
3
|
+
import { logInternal } from "../utils/log.js";
|
|
4
|
+
import { isBun } from "./bun.js";
|
|
5
|
+
export async function toNodeStats(path) {
|
|
6
|
+
if (!isBun) {
|
|
7
|
+
return stat(path);
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
const file = Bun.file(path);
|
|
11
|
+
const size = file.size;
|
|
12
|
+
const lastModified = new Date(file.lastModified);
|
|
13
|
+
return {
|
|
14
|
+
dev: 0,
|
|
15
|
+
ino: 0,
|
|
16
|
+
mode: 0,
|
|
17
|
+
nlink: 0,
|
|
18
|
+
uid: 0,
|
|
19
|
+
gid: 0,
|
|
20
|
+
rdev: 0,
|
|
21
|
+
size,
|
|
22
|
+
blksize: 0,
|
|
23
|
+
blocks: 0,
|
|
24
|
+
atimeMs: lastModified.getTime(),
|
|
25
|
+
mtimeMs: lastModified.getTime(),
|
|
26
|
+
ctimeMs: lastModified.getTime(),
|
|
27
|
+
birthtimeMs: lastModified.getTime(),
|
|
28
|
+
atime: lastModified,
|
|
29
|
+
mtime: lastModified,
|
|
30
|
+
ctime: lastModified,
|
|
31
|
+
birthtime: lastModified,
|
|
32
|
+
isDirectory: () => false,
|
|
33
|
+
// Bun doesn't provide this info directly
|
|
34
|
+
isFile: () => true,
|
|
35
|
+
isBlockDevice: () => false,
|
|
36
|
+
isCharacterDevice: () => false,
|
|
37
|
+
isSymbolicLink: () => false,
|
|
38
|
+
isFIFO: () => false,
|
|
39
|
+
isSocket: () => false
|
|
40
|
+
};
|
|
41
|
+
} catch (error) {
|
|
42
|
+
return stat(path);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export async function getStats(path) {
|
|
46
|
+
if (!isBun) {
|
|
47
|
+
return stat(path);
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
return await toNodeStats(path);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
return stat(path);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export function getStatsSync(path) {
|
|
56
|
+
if (!isBun) {
|
|
57
|
+
return statSync(path);
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const file = Bun.file(path);
|
|
61
|
+
const size = file.size;
|
|
62
|
+
const lastModified = new Date(file.lastModified);
|
|
63
|
+
return {
|
|
64
|
+
dev: 0,
|
|
65
|
+
ino: 0,
|
|
66
|
+
mode: 0,
|
|
67
|
+
nlink: 0,
|
|
68
|
+
uid: 0,
|
|
69
|
+
gid: 0,
|
|
70
|
+
rdev: 0,
|
|
71
|
+
size,
|
|
72
|
+
blksize: 0,
|
|
73
|
+
blocks: 0,
|
|
74
|
+
atimeMs: lastModified.getTime(),
|
|
75
|
+
mtimeMs: lastModified.getTime(),
|
|
76
|
+
ctimeMs: lastModified.getTime(),
|
|
77
|
+
birthtimeMs: lastModified.getTime(),
|
|
78
|
+
atime: lastModified,
|
|
79
|
+
mtime: lastModified,
|
|
80
|
+
ctime: lastModified,
|
|
81
|
+
birthtime: lastModified,
|
|
82
|
+
isDirectory: () => false,
|
|
83
|
+
isFile: () => true,
|
|
84
|
+
isBlockDevice: () => false,
|
|
85
|
+
isCharacterDevice: () => false,
|
|
86
|
+
isSymbolicLink: () => false,
|
|
87
|
+
isFIFO: () => false,
|
|
88
|
+
isSocket: () => false
|
|
89
|
+
};
|
|
90
|
+
} catch (error) {
|
|
91
|
+
return statSync(path);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export async function getFileExists(path) {
|
|
95
|
+
if (!isBun) {
|
|
96
|
+
try {
|
|
97
|
+
await stat(path);
|
|
98
|
+
return true;
|
|
99
|
+
} catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
const file = Bun.file(path);
|
|
105
|
+
return await file.exists();
|
|
106
|
+
} catch (error) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export async function getFileSize(path) {
|
|
111
|
+
if (!isBun) {
|
|
112
|
+
try {
|
|
113
|
+
const stats = await stat(path);
|
|
114
|
+
return stats.size;
|
|
115
|
+
} catch (error) {
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
const file = Bun.file(path);
|
|
121
|
+
return file.size;
|
|
122
|
+
} catch (error) {
|
|
123
|
+
return 0;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
export async function getFileLastModified(path) {
|
|
127
|
+
if (!isBun) {
|
|
128
|
+
try {
|
|
129
|
+
const stats = await stat(path);
|
|
130
|
+
return stats.mtime;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
return /* @__PURE__ */ new Date(0);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
const file = Bun.file(path);
|
|
137
|
+
return new Date(file.lastModified);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
return /* @__PURE__ */ new Date(0);
|
|
140
|
+
}
|
|
141
|
+
}
|
package/bin/impl/write-file.d.ts
CHANGED
|
@@ -1,20 +1,31 @@
|
|
|
1
|
-
import { type PathLike } from "node:fs";
|
|
2
|
-
|
|
1
|
+
import { type PathLike, type WriteStream } from "node:fs";
|
|
2
|
+
import { URL } from "node:url";
|
|
3
|
+
export interface WriteFileOptions {
|
|
4
|
+
encoding?: BufferEncoding | null;
|
|
5
|
+
mode?: number;
|
|
6
|
+
flag?: string;
|
|
7
|
+
isJson?: boolean;
|
|
8
|
+
useStreaming?: boolean;
|
|
9
|
+
replacer?: (key: string, value: unknown) => unknown;
|
|
10
|
+
spaces?: string | number;
|
|
11
|
+
}
|
|
12
|
+
type WriteTarget = PathLike | number | URL | WriteStream;
|
|
3
13
|
/**
|
|
4
14
|
* Synchronously writes data to a file, replacing the file if it already exists.
|
|
5
15
|
* Ensures the directory exists before writing.
|
|
6
16
|
*
|
|
7
|
-
* @param file - Path to the file.
|
|
8
|
-
* @param data - The data to write.
|
|
17
|
+
* @param file - Path to the file, file descriptor, URL, or special streams (stdout/stderr).
|
|
18
|
+
* @param data - The data to write. Will be converted to string if not already a string.
|
|
9
19
|
* @param options - Options for writing the file. Can be an encoding string or an object.
|
|
10
20
|
*/
|
|
11
|
-
export declare function writeFileSync(file:
|
|
21
|
+
export declare function writeFileSync(file: WriteTarget, data: string | NodeJS.ArrayBufferView | Blob | Response | unknown, options?: WriteFileOptions): void;
|
|
12
22
|
/**
|
|
13
23
|
* Asynchronously writes data to a file, replacing the file if it already exists.
|
|
14
24
|
* Ensures the directory exists before writing.
|
|
15
25
|
*
|
|
16
|
-
* @param file - Path to the file.
|
|
17
|
-
* @param data - The data to write.
|
|
26
|
+
* @param file - Path to the file, file descriptor, URL, or special streams (stdout/stderr).
|
|
27
|
+
* @param data - The data to write. Will be converted to string if not already a string.
|
|
18
28
|
* @param options - Options for writing the file. Can be an encoding string or an object.
|
|
19
29
|
*/
|
|
20
|
-
export declare function writeFile(file:
|
|
30
|
+
export declare function writeFile(file: WriteTarget, data: string | NodeJS.ArrayBufferView | Blob | Response | unknown, options?: WriteFileOptions): Promise<void>;
|
|
31
|
+
export {};
|
package/bin/impl/write-file.js
CHANGED
|
@@ -1,48 +1,177 @@
|
|
|
1
|
-
import { writeFileSync as nodeWriteFileSync } from "node:fs";
|
|
2
|
-
import { mkdir, writeFile as nodeWriteFileAsync
|
|
1
|
+
import { writeFileSync as nodeWriteFileSync, createWriteStream } from "node:fs";
|
|
2
|
+
import { mkdir, writeFile as nodeWriteFileAsync } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { Transform } from "node:stream";
|
|
5
|
+
import { pipeline } from "node:stream/promises";
|
|
6
|
+
import { URL } from "node:url";
|
|
7
|
+
import { logInternal } from "../utils/log.js";
|
|
8
|
+
import { isBun } from "./bun.js";
|
|
6
9
|
import { mkdirsSync } from "./mkdirs.js";
|
|
10
|
+
function isStdStream(target) {
|
|
11
|
+
return typeof target === "object" && target !== null && "write" in target && typeof target.write === "function";
|
|
12
|
+
}
|
|
13
|
+
function isFileDescriptor(target) {
|
|
14
|
+
return typeof target === "number";
|
|
15
|
+
}
|
|
16
|
+
function isPathLike(target) {
|
|
17
|
+
return !isStdStream(target) && !isFileDescriptor(target) && !(target instanceof URL);
|
|
18
|
+
}
|
|
7
19
|
export function writeFileSync(file, data, options) {
|
|
20
|
+
let isJson = false;
|
|
21
|
+
let replacer;
|
|
22
|
+
let spaces;
|
|
23
|
+
let encoding;
|
|
24
|
+
let mode;
|
|
25
|
+
let flag;
|
|
26
|
+
if (options) {
|
|
27
|
+
isJson = options.isJson ?? isJson;
|
|
28
|
+
replacer = options.replacer;
|
|
29
|
+
spaces = options.spaces;
|
|
30
|
+
encoding = options.encoding;
|
|
31
|
+
mode = options.mode ? Number(options.mode) : void 0;
|
|
32
|
+
flag = options.flag;
|
|
33
|
+
}
|
|
34
|
+
let stringData;
|
|
35
|
+
if (typeof data === "string") {
|
|
36
|
+
stringData = data;
|
|
37
|
+
} else if (data instanceof Uint8Array) {
|
|
38
|
+
stringData = Buffer.from(data).toString(encoding || "utf8");
|
|
39
|
+
} else if (data instanceof Blob || data instanceof Response) {
|
|
40
|
+
throw new Error("Blob and Response are not supported in sync context");
|
|
41
|
+
} else if (data instanceof ArrayBuffer) {
|
|
42
|
+
stringData = Buffer.from(new Uint8Array(data)).toString(encoding || "utf8");
|
|
43
|
+
} else if (ArrayBuffer.isView(data)) {
|
|
44
|
+
stringData = Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString(encoding || "utf8");
|
|
45
|
+
} else if (isJson) {
|
|
46
|
+
try {
|
|
47
|
+
stringData = JSON.stringify(data, replacer, spaces);
|
|
48
|
+
if (stringData === void 0) {
|
|
49
|
+
throw new Error("Failed to stringify JSON object");
|
|
50
|
+
}
|
|
51
|
+
} catch (err) {
|
|
52
|
+
throw new Error(`Failed to stringify JSON data: ${err}`);
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
stringData = String(data);
|
|
56
|
+
}
|
|
57
|
+
if (isStdStream(file)) {
|
|
58
|
+
file.write(stringData);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (isFileDescriptor(file)) {
|
|
62
|
+
if (isBun) {
|
|
63
|
+
Bun.write(Bun.file(file), stringData);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
nodeWriteFileSync(file, stringData, { encoding: encoding || "utf8", mode, flag });
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (file instanceof URL) {
|
|
70
|
+
if (file.protocol !== "file:") {
|
|
71
|
+
throw new Error("Only file:// URLs are supported");
|
|
72
|
+
}
|
|
73
|
+
file = file.pathname;
|
|
74
|
+
}
|
|
75
|
+
if (!isPathLike(file)) {
|
|
76
|
+
throw new Error("Invalid file target");
|
|
77
|
+
}
|
|
8
78
|
const dir = path.dirname(file.toString());
|
|
9
79
|
mkdirsSync(dir);
|
|
10
80
|
if (isBun) {
|
|
11
81
|
try {
|
|
12
|
-
|
|
82
|
+
const filePath = file.toString();
|
|
83
|
+
Bun.write(filePath, stringData);
|
|
13
84
|
return;
|
|
14
85
|
} catch (_error) {
|
|
15
86
|
}
|
|
16
87
|
}
|
|
17
|
-
nodeWriteFileSync(file,
|
|
88
|
+
nodeWriteFileSync(file, stringData, { encoding: encoding || "utf8", mode, flag });
|
|
18
89
|
}
|
|
19
90
|
export async function writeFile(file, data, options) {
|
|
91
|
+
let isJson = false;
|
|
92
|
+
let useStreaming = false;
|
|
93
|
+
let replacer;
|
|
94
|
+
let spaces;
|
|
95
|
+
let encoding;
|
|
96
|
+
let mode;
|
|
97
|
+
let flag;
|
|
98
|
+
if (options) {
|
|
99
|
+
isJson = options.isJson ?? isJson;
|
|
100
|
+
useStreaming = options.useStreaming ?? useStreaming;
|
|
101
|
+
replacer = options.replacer;
|
|
102
|
+
spaces = options.spaces;
|
|
103
|
+
encoding = options.encoding;
|
|
104
|
+
mode = options.mode ? Number(options.mode) : void 0;
|
|
105
|
+
flag = options.flag;
|
|
106
|
+
}
|
|
107
|
+
let stringData;
|
|
108
|
+
if (typeof data === "string") {
|
|
109
|
+
stringData = data;
|
|
110
|
+
} else if (data instanceof Uint8Array) {
|
|
111
|
+
stringData = Buffer.from(data).toString(encoding || "utf8");
|
|
112
|
+
} else if (data instanceof Blob) {
|
|
113
|
+
stringData = await data.text();
|
|
114
|
+
} else if (data instanceof Response) {
|
|
115
|
+
stringData = await data.text();
|
|
116
|
+
} else if (data instanceof ArrayBuffer) {
|
|
117
|
+
stringData = Buffer.from(new Uint8Array(data)).toString(encoding || "utf8");
|
|
118
|
+
} else if (ArrayBuffer.isView(data)) {
|
|
119
|
+
stringData = Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString(encoding || "utf8");
|
|
120
|
+
} else if (isJson) {
|
|
121
|
+
try {
|
|
122
|
+
stringData = JSON.stringify(data, replacer, spaces);
|
|
123
|
+
if (stringData === void 0) {
|
|
124
|
+
throw new Error("Failed to stringify JSON object");
|
|
125
|
+
}
|
|
126
|
+
} catch (err) {
|
|
127
|
+
throw new Error(`Failed to stringify JSON data: ${err}`);
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
stringData = String(data);
|
|
131
|
+
}
|
|
132
|
+
if (isStdStream(file)) {
|
|
133
|
+
file.write(stringData);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (isFileDescriptor(file)) {
|
|
137
|
+
if (isBun) {
|
|
138
|
+
await Bun.write(Bun.file(file), stringData);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
await nodeWriteFileAsync(file, stringData, { encoding: encoding || "utf8", mode, flag });
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (file instanceof URL) {
|
|
145
|
+
if (file.protocol !== "file:") {
|
|
146
|
+
throw new Error("Only file:// URLs are supported");
|
|
147
|
+
}
|
|
148
|
+
file = file.pathname;
|
|
149
|
+
}
|
|
150
|
+
if (!isPathLike(file)) {
|
|
151
|
+
throw new Error("Invalid file target");
|
|
152
|
+
}
|
|
20
153
|
const dir = path.dirname(file.toString());
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
154
|
+
await mkdir(dir, { recursive: true });
|
|
155
|
+
if (useStreaming) {
|
|
156
|
+
try {
|
|
157
|
+
const writeStream = createWriteStream(file, { encoding: encoding || "utf8", mode, flags: flag });
|
|
158
|
+
const transform = new Transform({
|
|
159
|
+
transform(chunk, _encoding, callback) {
|
|
160
|
+
callback(null, chunk);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
await pipeline(stringData, transform, writeStream);
|
|
164
|
+
return;
|
|
165
|
+
} catch (streamError) {
|
|
28
166
|
}
|
|
29
167
|
}
|
|
30
168
|
if (isBun) {
|
|
31
169
|
try {
|
|
32
170
|
const filePath = file.toString();
|
|
33
|
-
|
|
34
|
-
let writeData;
|
|
35
|
-
if (typeof data === "string") {
|
|
36
|
-
writeData = data;
|
|
37
|
-
} else if (data instanceof Uint8Array) {
|
|
38
|
-
writeData = data;
|
|
39
|
-
} else {
|
|
40
|
-
writeData = new Uint8Array(data.buffer);
|
|
41
|
-
}
|
|
42
|
-
await Bun.write(bunFile, writeData);
|
|
171
|
+
await Bun.write(filePath, stringData);
|
|
43
172
|
return;
|
|
44
173
|
} catch (_error) {
|
|
45
174
|
}
|
|
46
175
|
}
|
|
47
|
-
|
|
176
|
+
await nodeWriteFileAsync(file, stringData, { encoding: encoding || "utf8", mode, flag });
|
|
48
177
|
}
|
package/bin/impl/write-json.d.ts
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
|
-
import type { Mode } from "node:fs";
|
|
2
1
|
export interface JsonStringifyOptions {
|
|
3
2
|
replacer?: (key: string, value: unknown) => unknown | (number | string)[] | null;
|
|
4
3
|
spaces?: string | number;
|
|
4
|
+
/**
|
|
5
|
+
* Whether to include comments when writing JSONC files
|
|
6
|
+
* @default false
|
|
7
|
+
*/
|
|
8
|
+
includeComments?: boolean;
|
|
5
9
|
}
|
|
6
10
|
export interface WriteJsonOptions {
|
|
7
11
|
encoding?: BufferEncoding | null;
|
|
8
|
-
mode?:
|
|
12
|
+
mode?: number;
|
|
9
13
|
flag?: string;
|
|
10
14
|
replacer?: (key: string, value: unknown) => unknown | (number | string)[] | null;
|
|
11
15
|
spaces?: string | number;
|
|
12
16
|
throws?: boolean;
|
|
17
|
+
useStreaming?: boolean;
|
|
18
|
+
chunkSize?: number;
|
|
19
|
+
/**
|
|
20
|
+
* Whether to include comments when writing JSONC files
|
|
21
|
+
* @default false
|
|
22
|
+
*/
|
|
23
|
+
includeComments?: boolean;
|
|
13
24
|
}
|
|
14
25
|
/**
|
|
15
26
|
* Synchronously writes a JSON file.
|