@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.
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 +11 -0
  20. package/bin/impl/read-file.js +82 -4
  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 +218 -9
  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,15 +1,80 @@
1
1
  import { ensuredir } from "@reliverse/relifso";
2
2
  import { exec } from "node:child_process";
3
+ import { statSync, lstatSync } from "node:fs";
3
4
  import { readdir } from "node:fs/promises";
5
+ import { stat, lstat } from "node:fs/promises";
4
6
  import { promisify } from "node:util";
5
7
  import { pathExists } from "./path-exists.js";
6
8
  import { remove } from "./remove.js";
9
+ import { readFile, readFileSync } from "./read-file.js";
10
+ export async function readText(filePath, options = "utf8") {
11
+ const effectiveOptions = typeof options === "string" ? { encoding: options } : { ...options, encoding: options.encoding || "utf8" };
12
+ return readFile(filePath, effectiveOptions);
13
+ }
14
+ export function readTextSync(filePath, options = "utf8") {
15
+ const effectiveOptions = typeof options === "string" ? { encoding: options } : { ...options, encoding: options.encoding || "utf8" };
16
+ return readFileSync(filePath, effectiveOptions);
17
+ }
18
+ export async function readLines(filePath, options = { encoding: "utf8" }) {
19
+ const effectiveOptions = typeof options === "string" ? { encoding: options } : options;
20
+ const contentBuffer = await readFile(filePath, { ...effectiveOptions, encoding: null });
21
+ return contentBuffer.toString().split(/\r?\n/);
22
+ }
23
+ export function readLinesSync(filePath, options = { encoding: "utf8" }) {
24
+ const effectiveOptions = typeof options === "string" ? { encoding: options } : options;
25
+ const contentBuffer = readFileSync(filePath, { ...effectiveOptions, encoding: null });
26
+ return contentBuffer.toString().split(/\r?\n/);
27
+ }
28
+ export async function isDirectory(filePath) {
29
+ try {
30
+ const stats = await stat(filePath);
31
+ return stats.isDirectory();
32
+ } catch (error) {
33
+ if (error.code === "ENOENT" || error.code === "ENOTDIR") {
34
+ return false;
35
+ }
36
+ throw error;
37
+ }
38
+ }
39
+ export function isDirectorySync(filePath) {
40
+ try {
41
+ const stats = statSync(filePath);
42
+ return stats.isDirectory();
43
+ } catch (error) {
44
+ if (error.code === "ENOENT" || error.code === "ENOTDIR") {
45
+ return false;
46
+ }
47
+ throw error;
48
+ }
49
+ }
50
+ export async function isSymlink(filePath) {
51
+ try {
52
+ const stats = await lstat(filePath);
53
+ return stats.isSymbolicLink();
54
+ } catch (error) {
55
+ if (error.code === "ENOENT") {
56
+ return false;
57
+ }
58
+ throw error;
59
+ }
60
+ }
61
+ export function isSymlinkSync(filePath) {
62
+ try {
63
+ const stats = lstatSync(filePath);
64
+ return stats.isSymbolicLink();
65
+ } catch (error) {
66
+ if (error.code === "ENOENT") {
67
+ return false;
68
+ }
69
+ throw error;
70
+ }
71
+ }
7
72
  export const execAsync = promisify(exec);
8
- export async function setHiddenAttributeOnWindows(folderPath) {
73
+ export async function setHiddenAttribute(folderPath) {
9
74
  if (process.platform === "win32") {
10
75
  try {
11
76
  if (await pathExists(folderPath)) {
12
- const isAlreadyHidden = await isHidden(folderPath);
77
+ const isAlreadyHidden = await isHiddenAttribute(folderPath);
13
78
  if (!isAlreadyHidden) {
14
79
  await execAsync(`attrib +h "${folderPath}"`);
15
80
  }
@@ -19,7 +84,7 @@ export async function setHiddenAttributeOnWindows(folderPath) {
19
84
  }
20
85
  }
21
86
  }
22
- export async function isHidden(filePath) {
87
+ export async function isHiddenAttribute(filePath) {
23
88
  if (process.platform === "win32") {
24
89
  const attributes = await execAsync(`attrib "${filePath}"`);
25
90
  return attributes.stdout.includes("H");
@@ -0,0 +1,30 @@
1
+ import type { WriteJsonOptions } from "./write-json.js";
2
+ export interface JsonUtilsOptions extends WriteJsonOptions {
3
+ /**
4
+ * Whether to preserve comments when reading JSONC files
5
+ * @default false
6
+ */
7
+ preserveComments?: boolean;
8
+ /**
9
+ * Whether to include comments when writing JSONC files
10
+ * @default false
11
+ */
12
+ includeComments?: boolean;
13
+ }
14
+ /**
15
+ * Validates and repairs JSON/JSONC data if necessary.
16
+ * @param data - The JSON/JSONC data to validate and repair
17
+ * @param options - Options for validation and repair
18
+ * @returns The repaired JSON/JSONC data if needed, or the original data if valid
19
+ */
20
+ export declare function validateAndRepairJson(data: unknown, options?: JsonUtilsOptions): unknown;
21
+ /**
22
+ * Ensures that the directory for the JSON/JSONC file exists and then writes the data to the file.
23
+ * This is a utility function to avoid circular dependencies.
24
+ */
25
+ export declare function ensureJsonFileSync(file: string, data: unknown, options?: JsonUtilsOptions): void;
26
+ /**
27
+ * Ensures that the directory for the JSON/JSONC file exists and then asynchronously writes the data to the file.
28
+ * This is a utility function to avoid circular dependencies.
29
+ */
30
+ export declare function ensureJsonFile(file: string, data: unknown, options?: JsonUtilsOptions): Promise<void>;
@@ -0,0 +1,46 @@
1
+ import { dirname } from "node:path";
2
+ import { extname } from "node:path";
3
+ import { parseJsonc, isValidJsonc } from "../utils/json/regular/jsonc.js";
4
+ import { jsonrepair } from "../utils/json/regular/jsonrepair.js";
5
+ import { mkdirsSync } from "./mkdirs.js";
6
+ import { mkdirs } from "./mkdirs.js";
7
+ import { writeJsonSync } from "./write-json.js";
8
+ import { writeJson } from "./write-json.js";
9
+ export function validateAndRepairJson(data, options = {}) {
10
+ if (typeof data === "string") {
11
+ try {
12
+ JSON.parse(data);
13
+ return data;
14
+ } catch {
15
+ const isJsonc = extname(data).toLowerCase() === ".jsonc" || isValidJsonc(data);
16
+ if (isJsonc) {
17
+ try {
18
+ return parseJsonc(data, { preserveComments: options.preserveComments });
19
+ } catch {
20
+ return jsonrepair(data);
21
+ }
22
+ } else {
23
+ return jsonrepair(data);
24
+ }
25
+ }
26
+ }
27
+ return data;
28
+ }
29
+ export function ensureJsonFileSync(file, data, options) {
30
+ const dir = dirname(file);
31
+ mkdirsSync(dir);
32
+ const repairedData = validateAndRepairJson(data, options);
33
+ writeJsonSync(file, repairedData, {
34
+ ...options,
35
+ spaces: 2
36
+ });
37
+ }
38
+ export async function ensureJsonFile(file, data, options) {
39
+ const dir = dirname(file);
40
+ await mkdirs(dir);
41
+ const repairedData = validateAndRepairJson(data, options);
42
+ await writeJson(file, repairedData, {
43
+ ...options,
44
+ spaces: 2
45
+ });
46
+ }
@@ -1,3 +1,4 @@
1
+ import type { WriteFileOptions } from "./write-file.js";
1
2
  /**
2
3
  * Ensures that the directory for the file exists and then writes the data to the file.
3
4
  *
@@ -5,7 +6,7 @@
5
6
  * @param data - The data to write.
6
7
  * @param options - Options for writing the file (e.g., encoding, mode, flag).
7
8
  */
8
- export declare function outputFileSync(file: string, data: string | Uint8Array, options?: unknown): void;
9
+ export declare function outputFileSync(file: string, data: string | Uint8Array, options?: WriteFileOptions): void;
9
10
  /**
10
11
  * Ensures that the directory for the file exists and then asynchronously writes the data to the file.
11
12
  *
@@ -13,4 +14,4 @@ export declare function outputFileSync(file: string, data: string | Uint8Array,
13
14
  * @param data - The data to write.
14
15
  * @param options - Options for writing the file (e.g., encoding, mode, flag).
15
16
  */
16
- export declare function outputFile(file: string, data: string | Uint8Array, options?: unknown): Promise<void>;
17
+ export declare function outputFile(file: string, data: string | Uint8Array, options?: WriteFileOptions): Promise<void>;
@@ -1,3 +1,8 @@
1
+ import type { WriteJsonOptions } from "./write-json.js";
2
+ import { type JsonUtilsOptions } from "./json-utils.js";
3
+ export interface OutputJsonOptions extends WriteJsonOptions {
4
+ useStreaming?: boolean;
5
+ }
1
6
  /**
2
7
  * Ensures that the directory for the JSON file exists and then writes the JSON data to the file.
3
8
  *
@@ -5,7 +10,7 @@
5
10
  * @param data - The JSON data to write.
6
11
  * @param options - Options for writing the JSON file (e.g., replacer, spaces).
7
12
  */
8
- export declare function outputJsonSync(file: string, data: unknown, options?: unknown): void;
13
+ export declare function outputJsonSync(file: string, data: unknown, options?: BufferEncoding | OutputJsonOptions): void;
9
14
  /**
10
15
  * Ensures that the directory for the JSON file exists and then asynchronously writes the JSON data to the file.
11
16
  *
@@ -13,4 +18,4 @@ export declare function outputJsonSync(file: string, data: unknown, options?: un
13
18
  * @param data - The JSON data to write.
14
19
  * @param options - Options for writing the JSON file (e.g., replacer, spaces).
15
20
  */
16
- export declare function outputJson(file: string, data: unknown, options?: unknown): Promise<void>;
21
+ export declare function outputJson(file: string, data: unknown, options?: BufferEncoding | OutputJsonOptions | JsonUtilsOptions): Promise<void>;
@@ -1,15 +1,77 @@
1
- import { dirname } from "node:path";
2
- import { mkdirsSync } from "./mkdirs.js";
3
- import { mkdirs } from "./mkdirs.js";
4
- import { writeJsonSync } from "./write-json.js";
5
- import { writeJson } from "./write-json.js";
1
+ import { createWriteStream } from "node:fs";
2
+ import { Transform } from "node:stream";
3
+ import { pipeline } from "node:stream/promises";
4
+ import { logInternal } from "../utils/log.js";
5
+ import { ensureJsonFileSync, ensureJsonFile } from "./json-utils.js";
6
6
  export function outputJsonSync(file, data, options) {
7
- const dir = dirname(file);
8
- mkdirsSync(dir);
9
- writeJsonSync(file, data, options);
7
+ const jsonOptions = typeof options === "string" ? { encoding: options } : options ?? {};
8
+ ensureJsonFileSync(file, data, jsonOptions);
10
9
  }
11
10
  export async function outputJson(file, data, options) {
12
- const dir = dirname(file);
13
- await mkdirs(dir);
14
- await writeJson(file, data, options);
11
+ let useStreaming = false;
12
+ let encoding = "utf8";
13
+ let replacer;
14
+ let spaces;
15
+ let flag;
16
+ let mode;
17
+ let throws = true;
18
+ if (typeof options === "string") {
19
+ encoding = options;
20
+ } else if (options) {
21
+ if ("encoding" in options) {
22
+ encoding = options.encoding ?? encoding;
23
+ }
24
+ if ("replacer" in options) {
25
+ replacer = options.replacer;
26
+ }
27
+ if ("spaces" in options) {
28
+ spaces = options.spaces;
29
+ }
30
+ if ("flag" in options) {
31
+ flag = options.flag;
32
+ }
33
+ if ("mode" in options) {
34
+ mode = options.mode ? Number(options.mode) : void 0;
35
+ }
36
+ if ("useStreaming" in options) {
37
+ useStreaming = options.useStreaming ?? useStreaming;
38
+ }
39
+ if ("throws" in options && options.throws !== void 0) {
40
+ throws = options.throws;
41
+ }
42
+ }
43
+ try {
44
+ if (useStreaming) {
45
+ try {
46
+ const writeStream = createWriteStream(file, { encoding, mode, flags: flag });
47
+ const jsonString = JSON.stringify(data, replacer, spaces);
48
+ if (jsonString === void 0) {
49
+ throw new Error("Failed to stringify JSON object");
50
+ }
51
+ const transform = new Transform({
52
+ transform(chunk, _encoding, callback) {
53
+ callback(null, chunk);
54
+ }
55
+ });
56
+ transform.write(jsonString);
57
+ transform.end();
58
+ await pipeline(transform, writeStream);
59
+ return;
60
+ } catch (streamError) {
61
+ }
62
+ }
63
+ await ensureJsonFile(file, data, {
64
+ encoding,
65
+ replacer,
66
+ spaces,
67
+ flag,
68
+ mode,
69
+ throws,
70
+ useStreaming
71
+ });
72
+ } catch (err) {
73
+ if (throws) {
74
+ throw err;
75
+ }
76
+ }
15
77
  }
@@ -1,6 +1,9 @@
1
1
  export interface ReadFileOptions {
2
2
  encoding?: BufferEncoding | null;
3
3
  flag?: string;
4
+ isJson?: boolean;
5
+ useStreaming?: boolean;
6
+ reviver?: (key: string, value: unknown) => unknown;
4
7
  }
5
8
  /**
6
9
  * Synchronously reads the entire contents of a file.
@@ -10,6 +13,10 @@ export interface ReadFileOptions {
10
13
  * @returns The contents of the file.
11
14
  */
12
15
  export declare function readFileSync(path: string, options?: BufferEncoding | ReadFileOptions): string | Buffer;
16
+ export declare function readFileSync(path: string, encoding: BufferEncoding): string;
17
+ export declare function readFileSync(path: string, options: {
18
+ encoding: BufferEncoding;
19
+ }): string;
13
20
  /**
14
21
  * Asynchronously reads the entire contents of a file.
15
22
  *
@@ -18,3 +25,7 @@ export declare function readFileSync(path: string, options?: BufferEncoding | Re
18
25
  * @returns A promise that resolves with the contents of the file.
19
26
  */
20
27
  export declare function readFile(path: string, options?: BufferEncoding | ReadFileOptions): Promise<string | Buffer>;
28
+ export declare function readFile(path: string, encoding: BufferEncoding): Promise<string>;
29
+ export declare function readFile(path: string, options: {
30
+ encoding: BufferEncoding;
31
+ }): Promise<string>;
@@ -1,7 +1,11 @@
1
1
  import { readFileSync as nodeReadFileSync } from "node:fs";
2
+ import { createReadStream } from "node:fs";
2
3
  import { readFile as nodeReadFileAsync } from "node:fs/promises";
4
+ import { Transform } from "node:stream";
5
+ import { pipeline } from "node:stream/promises";
6
+ import { jsonrepair } from "../utils/json/regular/jsonrepair.js";
7
+ import { logInternal } from "../utils/log.js";
3
8
  import { isBun, getFileBun } from "./bun.js";
4
- import { logInternal } from "./logger.js";
5
9
  export function readFileSync(path, options) {
6
10
  let encoding;
7
11
  let flag;
@@ -53,35 +57,109 @@ export function readFileSync(path, options) {
53
57
  export async function readFile(path, options) {
54
58
  let encoding;
55
59
  let flag;
60
+ let isJson = false;
61
+ let useStreaming = false;
62
+ let reviver;
56
63
  if (typeof options === "string") {
57
64
  encoding = options;
58
65
  } else if (options) {
59
66
  encoding = options.encoding;
60
67
  flag = options.flag;
68
+ isJson = options.isJson ?? isJson;
69
+ useStreaming = options.useStreaming ?? useStreaming;
70
+ reviver = options.reviver;
61
71
  }
62
72
  if (isBun) {
63
73
  try {
64
74
  const file = getFileBun(path);
65
75
  if (encoding) {
66
76
  try {
67
- return await file.text();
77
+ const result = await file.text();
78
+ if (isJson) {
79
+ try {
80
+ return JSON.parse(result, reviver);
81
+ } catch (_parseError) {
82
+ const repaired = jsonrepair(result);
83
+ return JSON.parse(repaired, reviver);
84
+ }
85
+ }
86
+ return result;
68
87
  } catch (_error) {
69
88
  const buffer = await file.arrayBuffer();
70
- return Buffer.from(buffer).toString(encoding);
89
+ const text = Buffer.from(buffer).toString(encoding);
90
+ if (isJson) {
91
+ try {
92
+ return JSON.parse(text, reviver);
93
+ } catch (_parseError) {
94
+ const repaired = jsonrepair(text);
95
+ return JSON.parse(repaired, reviver);
96
+ }
97
+ }
98
+ return text;
71
99
  }
72
100
  }
73
101
  try {
74
102
  const buffer = await file.arrayBuffer();
103
+ if (isJson) {
104
+ const text = Buffer.from(buffer).toString();
105
+ try {
106
+ return JSON.parse(text, reviver);
107
+ } catch (_parseError) {
108
+ const repaired = jsonrepair(text);
109
+ return JSON.parse(repaired, reviver);
110
+ }
111
+ }
75
112
  return Buffer.from(buffer);
76
113
  } catch (_error) {
77
114
  const text = await file.text();
115
+ if (isJson) {
116
+ try {
117
+ return JSON.parse(text, reviver);
118
+ } catch (_parseError) {
119
+ const repaired = jsonrepair(text);
120
+ return JSON.parse(repaired, reviver);
121
+ }
122
+ }
78
123
  return Buffer.from(text);
79
124
  }
80
125
  } catch (error) {
81
126
  }
82
127
  }
128
+ if (useStreaming && encoding) {
129
+ try {
130
+ const chunks = [];
131
+ const transform = new Transform({
132
+ transform(chunk, _encoding, callback) {
133
+ chunks.push(chunk.toString());
134
+ callback();
135
+ }
136
+ });
137
+ const readStream = createReadStream(path, { encoding, flags: flag });
138
+ await pipeline(readStream, transform);
139
+ const text = chunks.join("");
140
+ if (isJson) {
141
+ try {
142
+ return JSON.parse(text, reviver);
143
+ } catch (_parseError) {
144
+ const repaired = jsonrepair(text);
145
+ return JSON.parse(repaired, reviver);
146
+ }
147
+ }
148
+ return text;
149
+ } catch (streamError) {
150
+ }
151
+ }
83
152
  if (encoding) {
84
- return nodeReadFileAsync(path, { encoding, flag });
153
+ const text = await nodeReadFileAsync(path, { encoding, flag });
154
+ if (isJson) {
155
+ try {
156
+ return JSON.parse(text, reviver);
157
+ } catch (_parseError) {
158
+ const repaired = jsonrepair(text);
159
+ return JSON.parse(repaired, reviver);
160
+ }
161
+ }
162
+ return text;
85
163
  }
86
164
  return nodeReadFileAsync(path, { encoding: null, flag });
87
165
  }
@@ -5,6 +5,12 @@ export interface ReadJsonOptions {
5
5
  throws?: boolean;
6
6
  defaultValue?: unknown;
7
7
  ensure?: boolean;
8
+ useStreaming?: boolean;
9
+ /**
10
+ * Whether to preserve comments when reading JSONC files
11
+ * @default false
12
+ */
13
+ preserveComments?: boolean;
8
14
  }
9
15
  /**
10
16
  * Reads a JSON file and then parses it into an object.
@@ -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) {