@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/bin/impl/extras.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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?:
|
|
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?:
|
|
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?:
|
|
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?:
|
|
21
|
+
export declare function outputJson(file: string, data: unknown, options?: BufferEncoding | OutputJsonOptions | JsonUtilsOptions): Promise<void>;
|
package/bin/impl/output-json.js
CHANGED
|
@@ -1,15 +1,77 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
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
|
|
8
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
}
|
package/bin/impl/read-file.d.ts
CHANGED
|
@@ -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>;
|
package/bin/impl/read-file.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
package/bin/impl/read-json.d.ts
CHANGED
|
@@ -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.
|
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) {
|