@reliverse/relifso 1.4.0 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/LICENSES +16 -0
  2. package/README.md +48 -17
  3. package/bin/impl/bun.d.ts +5 -28
  4. package/bin/impl/bun.js +2 -126
  5. package/bin/impl/copy.js +8 -7
  6. package/bin/impl/create.d.ts +34 -0
  7. package/bin/impl/create.js +54 -0
  8. package/bin/impl/dive.d.ts +10 -0
  9. package/bin/impl/dive.js +89 -0
  10. package/bin/impl/empty.d.ts +28 -0
  11. package/bin/impl/empty.js +75 -0
  12. package/bin/impl/extras.d.ts +22 -2
  13. package/bin/impl/extras.js +68 -3
  14. package/bin/impl/json-utils.d.ts +30 -0
  15. package/bin/impl/json-utils.js +46 -0
  16. package/bin/impl/output-file.d.ts +3 -2
  17. package/bin/impl/output-json.d.ts +7 -2
  18. package/bin/impl/output-json.js +73 -11
  19. package/bin/impl/read-file.d.ts +15 -4
  20. package/bin/impl/read-file.js +88 -10
  21. package/bin/impl/read-json.d.ts +6 -0
  22. package/bin/impl/read-json.js +133 -21
  23. package/bin/impl/stats.d.ts +31 -0
  24. package/bin/impl/stats.js +141 -0
  25. package/bin/impl/write-file.d.ts +19 -8
  26. package/bin/impl/write-file.js +153 -24
  27. package/bin/impl/write-json.d.ts +13 -2
  28. package/bin/impl/write-json.js +46 -7
  29. package/bin/mod.d.ts +84 -36
  30. package/bin/mod.js +108 -39
  31. package/bin/utils/json/helpers/JSONRepairError.d.ts +4 -0
  32. package/bin/utils/json/helpers/JSONRepairError.js +7 -0
  33. package/bin/utils/json/helpers/JsonSchemaError.d.ts +6 -0
  34. package/bin/utils/json/helpers/JsonSchemaError.js +6 -0
  35. package/bin/utils/json/helpers/stringUtils.d.ts +64 -0
  36. package/bin/utils/json/helpers/stringUtils.js +87 -0
  37. package/bin/utils/json/regular/jsonc.d.ts +45 -0
  38. package/bin/utils/json/regular/jsonc.js +88 -0
  39. package/bin/utils/json/regular/jsonrepair.d.ts +17 -0
  40. package/bin/utils/json/regular/jsonrepair.js +576 -0
  41. package/bin/utils/json/regular/validate.d.ts +22 -0
  42. package/bin/utils/json/regular/validate.js +52 -0
  43. package/bin/utils/json/stream/JsonStreamError.d.ts +6 -0
  44. package/bin/utils/json/stream/JsonStreamError.js +6 -0
  45. package/bin/utils/json/stream/buffer/InputBuffer.d.ts +13 -0
  46. package/bin/utils/json/stream/buffer/InputBuffer.js +68 -0
  47. package/bin/utils/json/stream/buffer/OutputBuffer.d.ts +17 -0
  48. package/bin/utils/json/stream/buffer/OutputBuffer.js +101 -0
  49. package/bin/utils/json/stream/core.d.ts +10 -0
  50. package/bin/utils/json/stream/core.js +695 -0
  51. package/bin/utils/json/stream/jsonl.d.ts +21 -0
  52. package/bin/utils/json/stream/jsonl.js +55 -0
  53. package/bin/utils/json/stream/parser.d.ts +14 -0
  54. package/bin/utils/json/stream/parser.js +81 -0
  55. package/bin/utils/json/stream/stack.d.ts +19 -0
  56. package/bin/utils/json/stream/stack.js +43 -0
  57. package/bin/utils/json/stream/stream.d.ts +6 -0
  58. package/bin/utils/json/stream/stream.js +30 -0
  59. package/bin/utils/json/stream/writer.d.ts +14 -0
  60. package/bin/utils/json/stream/writer.js +44 -0
  61. package/package.json +3 -2
  62. package/bin/impl/create-file.d.ts +0 -2
  63. package/bin/impl/create-file.js +0 -21
  64. package/bin/impl/dive-async.d.ts +0 -11
  65. package/bin/impl/dive-async.js +0 -88
  66. package/bin/impl/empty-dir.d.ts +0 -2
  67. package/bin/impl/empty-dir.js +0 -24
  68. package/bin/impl/file-utils.d.ts +0 -20
  69. package/bin/impl/file-utils.js +0 -63
  70. /package/bin/{impl/logger.d.ts → utils/log.d.ts} +0 -0
  71. /package/bin/{impl/logger.js → utils/log.js} +0 -0
@@ -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 { logInternal } from "./logger.js";
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
- outputJsonSync(file, defaultValue);
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
- const parsed = JSON.parse(data, reviver);
39
- if (parsed === null || typeof parsed !== "object") {
40
- if (throws) {
41
- throw new Error("Invalid JSON data: expected an object");
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 defaultValue;
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
- if (throws) {
48
- throw parseError;
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 content = await fileRef.text();
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
- if (throws) {
119
- throw parseError;
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
+ }
@@ -1,20 +1,31 @@
1
- import { type PathLike } from "node:fs";
2
- export type WriteFileOptions = import("node:fs").WriteFileOptions;
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. If something other than a Buffer or Uint8Array is provided, it is converted to a string.
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: PathLike | number, data: string | NodeJS.ArrayBufferView, options?: WriteFileOptions): void;
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. If something other than a Buffer or Uint8Array is provided, it is converted to a string.
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: PathLike | number, data: string | NodeJS.ArrayBufferView, options?: WriteFileOptions): Promise<void>;
30
+ export declare function writeFile(file: WriteTarget, data: string | NodeJS.ArrayBufferView | Blob | Response | unknown, options?: WriteFileOptions): Promise<void>;
31
+ export {};
@@ -1,48 +1,177 @@
1
- import { writeFileSync as nodeWriteFileSync } from "node:fs";
2
- import { mkdir, writeFile as nodeWriteFileAsync, stat } from "node:fs/promises";
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 { isBun, getFileBun } from "./bun.js";
5
- import { logInternal } from "./logger.js";
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
- nodeWriteFileSync(file, data, options);
82
+ const filePath = file.toString();
83
+ Bun.write(filePath, stringData);
13
84
  return;
14
85
  } catch (_error) {
15
86
  }
16
87
  }
17
- nodeWriteFileSync(file, data, options);
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
- try {
22
- await stat(dir);
23
- } catch (_error) {
24
- if (_error.code === "ENOENT") {
25
- await mkdir(dir, { recursive: true });
26
- } else {
27
- throw _error;
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
- const bunFile = getFileBun(filePath);
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
- return nodeWriteFileAsync(file, data, options);
176
+ await nodeWriteFileAsync(file, stringData, { encoding: encoding || "utf8", mode, flag });
48
177
  }
@@ -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?: 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.