@reliverse/relifso 1.3.1 → 1.4.0
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/README.md +67 -12
- package/bin/impl/bun.d.ts +34 -0
- package/bin/impl/bun.js +147 -0
- package/bin/impl/{node/copy.d.ts → copy.d.ts} +15 -3
- package/bin/impl/copy.js +228 -0
- package/bin/impl/dive-async.d.ts +11 -0
- package/bin/impl/dive-async.js +88 -0
- package/bin/impl/{utils/additional.js → extras.js} +2 -2
- package/bin/impl/file-utils.d.ts +20 -0
- package/bin/impl/file-utils.js +63 -0
- package/bin/impl/logger.d.ts +1 -0
- package/bin/impl/logger.js +7 -0
- package/bin/impl/{node/mkdirs.js → mkdirs.js} +10 -3
- package/bin/impl/{node/move.d.ts → move.d.ts} +10 -0
- package/bin/impl/move.js +140 -0
- package/bin/impl/read-file.d.ts +20 -0
- package/bin/impl/read-file.js +87 -0
- package/bin/impl/{node/read-json.d.ts → read-json.d.ts} +3 -0
- package/bin/impl/read-json.js +129 -0
- package/bin/impl/write-file.js +48 -0
- package/bin/impl/write-json.d.ts +30 -0
- package/bin/impl/write-json.js +96 -0
- package/bin/mod.d.ts +39 -55
- package/bin/mod.js +60 -162
- package/package.json +2 -2
- package/bin/impl/node/copy.js +0 -94
- package/bin/impl/node/move.js +0 -93
- package/bin/impl/node/read-file.d.ts +0 -30
- package/bin/impl/node/read-file.js +0 -30
- package/bin/impl/node/read-json.js +0 -50
- package/bin/impl/node/write-file.js +0 -23
- package/bin/impl/node/write-json.d.ts +0 -28
- package/bin/impl/node/write-json.js +0 -22
- /package/bin/impl/{node/create-file.d.ts → create-file.d.ts} +0 -0
- /package/bin/impl/{node/create-file.js → create-file.js} +0 -0
- /package/bin/impl/{node/dive.d.ts → dive.d.ts} +0 -0
- /package/bin/impl/{node/dive.js → dive.js} +0 -0
- /package/bin/impl/{node/empty-dir.d.ts → empty-dir.d.ts} +0 -0
- /package/bin/impl/{node/empty-dir.js → empty-dir.js} +0 -0
- /package/bin/impl/{utils/additional.d.ts → extras.d.ts} +0 -0
- /package/bin/impl/{node/mkdirs.d.ts → mkdirs.d.ts} +0 -0
- /package/bin/impl/{node/output-file.d.ts → output-file.d.ts} +0 -0
- /package/bin/impl/{node/output-file.js → output-file.js} +0 -0
- /package/bin/impl/{node/output-json.d.ts → output-json.d.ts} +0 -0
- /package/bin/impl/{node/output-json.js → output-json.js} +0 -0
- /package/bin/impl/{node/path-exists.d.ts → path-exists.d.ts} +0 -0
- /package/bin/impl/{node/path-exists.js → path-exists.js} +0 -0
- /package/bin/impl/{node/remove.d.ts → remove.d.ts} +0 -0
- /package/bin/impl/{node/remove.js → remove.js} +0 -0
- /package/bin/impl/{node/write-file.d.ts → write-file.d.ts} +0 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare function readText(filePath: string, options?: BufferEncoding | {
|
|
2
|
+
encoding?: BufferEncoding | null;
|
|
3
|
+
flag?: string;
|
|
4
|
+
}): Promise<string | Buffer<ArrayBufferLike>>;
|
|
5
|
+
export declare function readTextSync(filePath: string, options?: BufferEncoding | {
|
|
6
|
+
encoding?: BufferEncoding | null;
|
|
7
|
+
flag?: string;
|
|
8
|
+
}): string | Buffer<ArrayBufferLike>;
|
|
9
|
+
export declare function readLines(filePath: string, options?: BufferEncoding | {
|
|
10
|
+
encoding?: BufferEncoding | null;
|
|
11
|
+
flag?: string;
|
|
12
|
+
}): Promise<string[]>;
|
|
13
|
+
export declare function readLinesSync(filePath: string, options?: BufferEncoding | {
|
|
14
|
+
encoding?: BufferEncoding | null;
|
|
15
|
+
flag?: string;
|
|
16
|
+
}): string[];
|
|
17
|
+
export declare function isDirectory(filePath: string): Promise<boolean>;
|
|
18
|
+
export declare function isDirectorySync(filePath: string): boolean;
|
|
19
|
+
export declare function isSymlink(filePath: string): Promise<boolean>;
|
|
20
|
+
export declare function isSymlinkSync(filePath: string): boolean;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { statSync, lstatSync } from "node:fs";
|
|
2
|
+
import { stat, lstat } from "node:fs/promises";
|
|
3
|
+
import { readFile, readFileSync } from "./read-file.js";
|
|
4
|
+
export async function readText(filePath, options = "utf8") {
|
|
5
|
+
return readFile(filePath, options);
|
|
6
|
+
}
|
|
7
|
+
export function readTextSync(filePath, options = "utf8") {
|
|
8
|
+
return readFileSync(filePath, options);
|
|
9
|
+
}
|
|
10
|
+
export async function readLines(filePath, options = { encoding: "utf8" }) {
|
|
11
|
+
const effectiveOptions = typeof options === "string" ? { encoding: options } : options;
|
|
12
|
+
const contentBuffer = await readFile(filePath, { ...effectiveOptions, encoding: null });
|
|
13
|
+
return contentBuffer.toString().split(/\r?\n/);
|
|
14
|
+
}
|
|
15
|
+
export function readLinesSync(filePath, options = { encoding: "utf8" }) {
|
|
16
|
+
const effectiveOptions = typeof options === "string" ? { encoding: options } : options;
|
|
17
|
+
const contentBuffer = readFileSync(filePath, { ...effectiveOptions, encoding: null });
|
|
18
|
+
return contentBuffer.toString().split(/\r?\n/);
|
|
19
|
+
}
|
|
20
|
+
export async function isDirectory(filePath) {
|
|
21
|
+
try {
|
|
22
|
+
const stats = await stat(filePath);
|
|
23
|
+
return stats.isDirectory();
|
|
24
|
+
} catch (error) {
|
|
25
|
+
if (error.code === "ENOENT" || error.code === "ENOTDIR") {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function isDirectorySync(filePath) {
|
|
32
|
+
try {
|
|
33
|
+
const stats = statSync(filePath);
|
|
34
|
+
return stats.isDirectory();
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (error.code === "ENOENT" || error.code === "ENOTDIR") {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export async function isSymlink(filePath) {
|
|
43
|
+
try {
|
|
44
|
+
const stats = await lstat(filePath);
|
|
45
|
+
return stats.isSymbolicLink();
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (error.code === "ENOENT") {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function isSymlinkSync(filePath) {
|
|
54
|
+
try {
|
|
55
|
+
const stats = lstatSync(filePath);
|
|
56
|
+
return stats.isSymbolicLink();
|
|
57
|
+
} catch (error) {
|
|
58
|
+
if (error.code === "ENOENT") {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const logInternal: (msg: string | (() => string)) => void;
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { mkdirSync, existsSync } from "node:fs";
|
|
2
2
|
import { mkdir, stat } from "node:fs/promises";
|
|
3
3
|
export function mkdirsSync(dir, options) {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
try {
|
|
5
|
+
if (existsSync(dir)) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
return mkdirSync(dir, { recursive: true, mode: options?.mode });
|
|
9
|
+
} catch (error) {
|
|
10
|
+
if (error.code === "EEXIST") {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
throw error;
|
|
6
14
|
}
|
|
7
|
-
return mkdirSync(dir, { recursive: true, mode: options?.mode });
|
|
8
15
|
}
|
|
9
16
|
export async function mkdirs(dir, options) {
|
|
10
17
|
try {
|
|
@@ -2,6 +2,16 @@ export interface MoveOptions {
|
|
|
2
2
|
overwrite?: boolean;
|
|
3
3
|
/** @deprecated Use `overwrite`. */
|
|
4
4
|
clobber?: boolean;
|
|
5
|
+
/** Maximum number of retries for EBUSY errors (default: 3) */
|
|
6
|
+
maxRetries?: number;
|
|
7
|
+
/** Delay between retries in milliseconds (default: 100) */
|
|
8
|
+
retryDelay?: number;
|
|
9
|
+
/** Whether to ensure source exists before moving (default: true) */
|
|
10
|
+
ensureSource?: boolean;
|
|
11
|
+
/** Whether to ensure destination directory exists (default: true) */
|
|
12
|
+
ensureDest?: boolean;
|
|
13
|
+
/** Whether to verify operation success (default: true) */
|
|
14
|
+
verify?: boolean;
|
|
5
15
|
}
|
|
6
16
|
/**
|
|
7
17
|
* Moves a file or directory. If the destination is a directory, the source is moved into it.
|
package/bin/impl/move.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { renameSync, statSync, unlinkSync, copyFileSync } from "node:fs";
|
|
2
|
+
import { rename, stat, unlink, copyFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, basename, join as joinPath } from "node:path";
|
|
4
|
+
import { mkdirsSync } from "./mkdirs.js";
|
|
5
|
+
import { mkdirs } from "./mkdirs.js";
|
|
6
|
+
import { pathExists, pathExistsSync } from "./path-exists.js";
|
|
7
|
+
export function moveSync(src, dest, options = {}) {
|
|
8
|
+
const {
|
|
9
|
+
overwrite = options.clobber ?? false,
|
|
10
|
+
maxRetries = 3,
|
|
11
|
+
retryDelay = 100,
|
|
12
|
+
ensureSource = true,
|
|
13
|
+
ensureDest = true,
|
|
14
|
+
verify = true
|
|
15
|
+
} = options;
|
|
16
|
+
if (ensureSource && !pathExistsSync(src)) {
|
|
17
|
+
throw new Error(`Source ${src} does not exist.`);
|
|
18
|
+
}
|
|
19
|
+
const srcStat = statSync(src, { throwIfNoEntry: false });
|
|
20
|
+
if (!srcStat) {
|
|
21
|
+
throw new Error(`Source ${src} does not exist.`);
|
|
22
|
+
}
|
|
23
|
+
let destFinal = dest;
|
|
24
|
+
const destStat = statSync(dest, { throwIfNoEntry: false });
|
|
25
|
+
if (destStat?.isDirectory()) {
|
|
26
|
+
destFinal = joinPath(dest, basename(src));
|
|
27
|
+
}
|
|
28
|
+
if (statSync(destFinal, { throwIfNoEntry: false }) && !overwrite) {
|
|
29
|
+
throw new Error(`Destination ${destFinal} already exists and overwrite is false.`);
|
|
30
|
+
}
|
|
31
|
+
if (ensureDest) {
|
|
32
|
+
const destDir = dirname(destFinal);
|
|
33
|
+
mkdirsSync(destDir);
|
|
34
|
+
}
|
|
35
|
+
let lastError = null;
|
|
36
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
37
|
+
try {
|
|
38
|
+
renameSync(src, destFinal);
|
|
39
|
+
if (verify && !pathExistsSync(destFinal)) {
|
|
40
|
+
throw new Error(`Move operation failed: destination ${destFinal} does not exist after move`);
|
|
41
|
+
}
|
|
42
|
+
return;
|
|
43
|
+
} catch (err) {
|
|
44
|
+
lastError = err;
|
|
45
|
+
if (err.code === "EXDEV") {
|
|
46
|
+
copyFileSync(src, destFinal);
|
|
47
|
+
unlinkSync(src);
|
|
48
|
+
if (verify && !pathExistsSync(destFinal)) {
|
|
49
|
+
throw new Error(`Copy and unlink operation failed: destination ${destFinal} does not exist after operation`);
|
|
50
|
+
}
|
|
51
|
+
return;
|
|
52
|
+
} else if (err.code === "EISDIR" || err.code === "EPERM") {
|
|
53
|
+
copyFileSync(src, destFinal);
|
|
54
|
+
unlinkSync(src);
|
|
55
|
+
if (verify && !pathExistsSync(destFinal)) {
|
|
56
|
+
throw new Error(`Copy and unlink operation failed: destination ${destFinal} does not exist after operation`);
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
} else if (err.code === "EBUSY" && attempt < maxRetries - 1) {
|
|
60
|
+
const start = Date.now();
|
|
61
|
+
while (Date.now() - start < retryDelay) {
|
|
62
|
+
}
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
throw lastError;
|
|
69
|
+
}
|
|
70
|
+
export async function move(src, dest, options = {}) {
|
|
71
|
+
const {
|
|
72
|
+
overwrite = options.clobber ?? false,
|
|
73
|
+
maxRetries = 3,
|
|
74
|
+
retryDelay = 100,
|
|
75
|
+
ensureSource = true,
|
|
76
|
+
ensureDest = true,
|
|
77
|
+
verify = true
|
|
78
|
+
} = options;
|
|
79
|
+
if (ensureSource && !await pathExists(src)) {
|
|
80
|
+
throw new Error(`Source ${src} does not exist.`);
|
|
81
|
+
}
|
|
82
|
+
const srcStat = await stat(src).catch((e) => {
|
|
83
|
+
if (e.code === "ENOENT") return null;
|
|
84
|
+
throw e;
|
|
85
|
+
});
|
|
86
|
+
if (!srcStat) {
|
|
87
|
+
throw new Error(`Source ${src} does not exist.`);
|
|
88
|
+
}
|
|
89
|
+
let destFinal = dest;
|
|
90
|
+
const destStat = await stat(dest).catch((e) => {
|
|
91
|
+
if (e.code === "ENOENT") return null;
|
|
92
|
+
throw e;
|
|
93
|
+
});
|
|
94
|
+
if (destStat?.isDirectory()) {
|
|
95
|
+
destFinal = joinPath(dest, basename(src));
|
|
96
|
+
}
|
|
97
|
+
const destFinalStat = await stat(destFinal).catch((e) => {
|
|
98
|
+
if (e.code === "ENOENT") return null;
|
|
99
|
+
throw e;
|
|
100
|
+
});
|
|
101
|
+
if (destFinalStat && !overwrite) {
|
|
102
|
+
throw new Error(`Destination ${destFinal} already exists and overwrite is false.`);
|
|
103
|
+
}
|
|
104
|
+
if (ensureDest) {
|
|
105
|
+
const destDir = dirname(destFinal);
|
|
106
|
+
await mkdirs(destDir);
|
|
107
|
+
}
|
|
108
|
+
let lastError = null;
|
|
109
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
110
|
+
try {
|
|
111
|
+
await rename(src, destFinal);
|
|
112
|
+
if (verify && !await pathExists(destFinal)) {
|
|
113
|
+
throw new Error(`Move operation failed: destination ${destFinal} does not exist after move`);
|
|
114
|
+
}
|
|
115
|
+
return;
|
|
116
|
+
} catch (err) {
|
|
117
|
+
lastError = err;
|
|
118
|
+
if (err.code === "EXDEV") {
|
|
119
|
+
await copyFile(src, destFinal);
|
|
120
|
+
await unlink(src);
|
|
121
|
+
if (verify && !await pathExists(destFinal)) {
|
|
122
|
+
throw new Error(`Copy and unlink operation failed: destination ${destFinal} does not exist after operation`);
|
|
123
|
+
}
|
|
124
|
+
return;
|
|
125
|
+
} else if (err.code === "EISDIR" || err.code === "EPERM") {
|
|
126
|
+
await copyFile(src, destFinal);
|
|
127
|
+
await unlink(src);
|
|
128
|
+
if (verify && !await pathExists(destFinal)) {
|
|
129
|
+
throw new Error(`Copy and unlink operation failed: destination ${destFinal} does not exist after operation`);
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
} else if (err.code === "EBUSY" && attempt < maxRetries - 1) {
|
|
133
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
throw err;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
throw lastError;
|
|
140
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ReadFileOptions {
|
|
2
|
+
encoding?: BufferEncoding | null;
|
|
3
|
+
flag?: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Synchronously reads the entire contents of a file.
|
|
7
|
+
*
|
|
8
|
+
* @param path - The path to the file.
|
|
9
|
+
* @param options - Options for reading the file. Can be an encoding string or an object.
|
|
10
|
+
* @returns The contents of the file.
|
|
11
|
+
*/
|
|
12
|
+
export declare function readFileSync(path: string, options?: BufferEncoding | ReadFileOptions): string | Buffer;
|
|
13
|
+
/**
|
|
14
|
+
* Asynchronously reads the entire contents of a file.
|
|
15
|
+
*
|
|
16
|
+
* @param path - The path to the file.
|
|
17
|
+
* @param options - Options for reading the file. Can be an encoding string or an object.
|
|
18
|
+
* @returns A promise that resolves with the contents of the file.
|
|
19
|
+
*/
|
|
20
|
+
export declare function readFile(path: string, options?: BufferEncoding | ReadFileOptions): Promise<string | Buffer>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { readFileSync as nodeReadFileSync } from "node:fs";
|
|
2
|
+
import { readFile as nodeReadFileAsync } from "node:fs/promises";
|
|
3
|
+
import { isBun, getFileBun } from "./bun.js";
|
|
4
|
+
import { logInternal } from "./logger.js";
|
|
5
|
+
export function readFileSync(path, options) {
|
|
6
|
+
let encoding;
|
|
7
|
+
let flag;
|
|
8
|
+
if (typeof options === "string") {
|
|
9
|
+
encoding = options;
|
|
10
|
+
} else if (options) {
|
|
11
|
+
encoding = options.encoding;
|
|
12
|
+
flag = options.flag;
|
|
13
|
+
}
|
|
14
|
+
if (isBun) {
|
|
15
|
+
try {
|
|
16
|
+
const file = getFileBun(path);
|
|
17
|
+
if (encoding) {
|
|
18
|
+
try {
|
|
19
|
+
const text = file.text();
|
|
20
|
+
if (text instanceof Promise) {
|
|
21
|
+
throw new Error("Bun's text() returned a Promise in sync context");
|
|
22
|
+
}
|
|
23
|
+
return text;
|
|
24
|
+
} catch (_error) {
|
|
25
|
+
const buffer = file.arrayBuffer();
|
|
26
|
+
if (buffer instanceof Promise) {
|
|
27
|
+
throw new Error("Bun's arrayBuffer() returned a Promise in sync context");
|
|
28
|
+
}
|
|
29
|
+
return Buffer.from(buffer).toString(encoding);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const buffer = file.arrayBuffer();
|
|
34
|
+
if (buffer instanceof Promise) {
|
|
35
|
+
throw new Error("Bun's arrayBuffer() returned a Promise in sync context");
|
|
36
|
+
}
|
|
37
|
+
return Buffer.from(buffer);
|
|
38
|
+
} catch (_error) {
|
|
39
|
+
const text = file.text();
|
|
40
|
+
if (text instanceof Promise) {
|
|
41
|
+
throw new Error("Bun's text() returned a Promise in sync context");
|
|
42
|
+
}
|
|
43
|
+
return Buffer.from(text);
|
|
44
|
+
}
|
|
45
|
+
} catch (error) {
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (encoding) {
|
|
49
|
+
return nodeReadFileSync(path, { encoding, flag });
|
|
50
|
+
}
|
|
51
|
+
return nodeReadFileSync(path, { encoding: null, flag });
|
|
52
|
+
}
|
|
53
|
+
export async function readFile(path, options) {
|
|
54
|
+
let encoding;
|
|
55
|
+
let flag;
|
|
56
|
+
if (typeof options === "string") {
|
|
57
|
+
encoding = options;
|
|
58
|
+
} else if (options) {
|
|
59
|
+
encoding = options.encoding;
|
|
60
|
+
flag = options.flag;
|
|
61
|
+
}
|
|
62
|
+
if (isBun) {
|
|
63
|
+
try {
|
|
64
|
+
const file = getFileBun(path);
|
|
65
|
+
if (encoding) {
|
|
66
|
+
try {
|
|
67
|
+
return await file.text();
|
|
68
|
+
} catch (_error) {
|
|
69
|
+
const buffer = await file.arrayBuffer();
|
|
70
|
+
return Buffer.from(buffer).toString(encoding);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
const buffer = await file.arrayBuffer();
|
|
75
|
+
return Buffer.from(buffer);
|
|
76
|
+
} catch (_error) {
|
|
77
|
+
const text = await file.text();
|
|
78
|
+
return Buffer.from(text);
|
|
79
|
+
}
|
|
80
|
+
} catch (error) {
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (encoding) {
|
|
84
|
+
return nodeReadFileAsync(path, { encoding, flag });
|
|
85
|
+
}
|
|
86
|
+
return nodeReadFileAsync(path, { encoding: null, flag });
|
|
87
|
+
}
|
|
@@ -3,9 +3,12 @@ export interface ReadJsonOptions {
|
|
|
3
3
|
flag?: string;
|
|
4
4
|
reviver?: (key: string, value: unknown) => unknown;
|
|
5
5
|
throws?: boolean;
|
|
6
|
+
defaultValue?: unknown;
|
|
7
|
+
ensure?: boolean;
|
|
6
8
|
}
|
|
7
9
|
/**
|
|
8
10
|
* Reads a JSON file and then parses it into an object.
|
|
11
|
+
* If the file doesn't exist and ensure is true, creates the file with defaultValue.
|
|
9
12
|
*
|
|
10
13
|
* @param file - The path to the file.
|
|
11
14
|
* @param options - Options for reading the file or parsing JSON. Can be an encoding string or an object.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { isBun, getFileBun } from "./bun.js";
|
|
4
|
+
import { logInternal } from "./logger.js";
|
|
5
|
+
import { outputJsonSync } from "./output-json.js";
|
|
6
|
+
import { pathExistsSync } from "./path-exists.js";
|
|
7
|
+
export function readJsonSync(file, options) {
|
|
8
|
+
let encoding = "utf8";
|
|
9
|
+
let reviver;
|
|
10
|
+
let throws = true;
|
|
11
|
+
let defaultValue = { sync: true };
|
|
12
|
+
let ensure = true;
|
|
13
|
+
if (typeof options === "string") {
|
|
14
|
+
encoding = options;
|
|
15
|
+
} else if (options) {
|
|
16
|
+
encoding = options.encoding ?? encoding;
|
|
17
|
+
reviver = options.reviver;
|
|
18
|
+
if (options.throws !== void 0) {
|
|
19
|
+
throws = options.throws;
|
|
20
|
+
}
|
|
21
|
+
if (options.defaultValue !== void 0) {
|
|
22
|
+
defaultValue = options.defaultValue;
|
|
23
|
+
}
|
|
24
|
+
if (options.ensure !== void 0) {
|
|
25
|
+
ensure = options.ensure;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
if (!pathExistsSync(file) && ensure) {
|
|
30
|
+
outputJsonSync(file, defaultValue);
|
|
31
|
+
return defaultValue;
|
|
32
|
+
}
|
|
33
|
+
const data = readFileSync(file, { encoding });
|
|
34
|
+
if (!data || !data.trim()) {
|
|
35
|
+
return defaultValue;
|
|
36
|
+
}
|
|
37
|
+
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");
|
|
42
|
+
}
|
|
43
|
+
return defaultValue;
|
|
44
|
+
}
|
|
45
|
+
return parsed;
|
|
46
|
+
} catch (parseError) {
|
|
47
|
+
if (throws) {
|
|
48
|
+
throw parseError;
|
|
49
|
+
}
|
|
50
|
+
return defaultValue;
|
|
51
|
+
}
|
|
52
|
+
} catch (err) {
|
|
53
|
+
if (throws) {
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
56
|
+
return defaultValue;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export async function readJson(file, options) {
|
|
60
|
+
let encoding = "utf8";
|
|
61
|
+
let reviver;
|
|
62
|
+
let throws = true;
|
|
63
|
+
let flag;
|
|
64
|
+
let defaultValue = {};
|
|
65
|
+
if (typeof options === "string") {
|
|
66
|
+
encoding = options;
|
|
67
|
+
} else if (options) {
|
|
68
|
+
encoding = options.encoding ?? encoding;
|
|
69
|
+
reviver = options.reviver;
|
|
70
|
+
flag = options.flag;
|
|
71
|
+
if (options.throws !== void 0) {
|
|
72
|
+
throws = options.throws;
|
|
73
|
+
}
|
|
74
|
+
if (options.defaultValue !== void 0) {
|
|
75
|
+
defaultValue = options.defaultValue;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
if (isBun) {
|
|
80
|
+
try {
|
|
81
|
+
const fileRef = getFileBun(file);
|
|
82
|
+
try {
|
|
83
|
+
const content = await fileRef.text();
|
|
84
|
+
if (!content || !content.trim()) {
|
|
85
|
+
return defaultValue;
|
|
86
|
+
}
|
|
87
|
+
const parsed = JSON.parse(content, reviver);
|
|
88
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
89
|
+
if (throws) {
|
|
90
|
+
throw new Error("Invalid JSON data: expected an object");
|
|
91
|
+
}
|
|
92
|
+
return defaultValue;
|
|
93
|
+
}
|
|
94
|
+
return parsed;
|
|
95
|
+
} catch (parseError) {
|
|
96
|
+
if (throws) {
|
|
97
|
+
throw parseError;
|
|
98
|
+
}
|
|
99
|
+
return defaultValue;
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const data = await readFile(file, { encoding, flag });
|
|
105
|
+
if (!data || !data.trim()) {
|
|
106
|
+
return defaultValue;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const parsed = JSON.parse(data, reviver);
|
|
110
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
111
|
+
if (throws) {
|
|
112
|
+
throw new Error("Invalid JSON data: expected an object");
|
|
113
|
+
}
|
|
114
|
+
return defaultValue;
|
|
115
|
+
}
|
|
116
|
+
return parsed;
|
|
117
|
+
} catch (parseError) {
|
|
118
|
+
if (throws) {
|
|
119
|
+
throw parseError;
|
|
120
|
+
}
|
|
121
|
+
return defaultValue;
|
|
122
|
+
}
|
|
123
|
+
} catch (err) {
|
|
124
|
+
if (throws) {
|
|
125
|
+
throw err;
|
|
126
|
+
}
|
|
127
|
+
return defaultValue;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { writeFileSync as nodeWriteFileSync } from "node:fs";
|
|
2
|
+
import { mkdir, writeFile as nodeWriteFileAsync, stat } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { isBun, getFileBun } from "./bun.js";
|
|
5
|
+
import { logInternal } from "./logger.js";
|
|
6
|
+
import { mkdirsSync } from "./mkdirs.js";
|
|
7
|
+
export function writeFileSync(file, data, options) {
|
|
8
|
+
const dir = path.dirname(file.toString());
|
|
9
|
+
mkdirsSync(dir);
|
|
10
|
+
if (isBun) {
|
|
11
|
+
try {
|
|
12
|
+
nodeWriteFileSync(file, data, options);
|
|
13
|
+
return;
|
|
14
|
+
} catch (_error) {
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
nodeWriteFileSync(file, data, options);
|
|
18
|
+
}
|
|
19
|
+
export async function writeFile(file, data, options) {
|
|
20
|
+
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;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (isBun) {
|
|
31
|
+
try {
|
|
32
|
+
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);
|
|
43
|
+
return;
|
|
44
|
+
} catch (_error) {
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return nodeWriteFileAsync(file, data, options);
|
|
48
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Mode } from "node:fs";
|
|
2
|
+
export interface JsonStringifyOptions {
|
|
3
|
+
replacer?: (key: string, value: unknown) => unknown | (number | string)[] | null;
|
|
4
|
+
spaces?: string | number;
|
|
5
|
+
}
|
|
6
|
+
export interface WriteJsonOptions {
|
|
7
|
+
encoding?: BufferEncoding | null;
|
|
8
|
+
mode?: Mode;
|
|
9
|
+
flag?: string;
|
|
10
|
+
replacer?: (key: string, value: unknown) => unknown | (number | string)[] | null;
|
|
11
|
+
spaces?: string | number;
|
|
12
|
+
throws?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Synchronously writes a JSON file.
|
|
16
|
+
*
|
|
17
|
+
* @param file - The path to the file.
|
|
18
|
+
* @param object - The object to write.
|
|
19
|
+
* @param options - Options for writing the file or stringifying JSON. Can be an encoding string or an object.
|
|
20
|
+
*/
|
|
21
|
+
export declare function writeJsonSync(file: string, object: unknown, options?: BufferEncoding | WriteJsonOptions): void;
|
|
22
|
+
/**
|
|
23
|
+
* Asynchronously writes a JSON file.
|
|
24
|
+
*
|
|
25
|
+
* @param file - The path to the file.
|
|
26
|
+
* @param object - The object to write.
|
|
27
|
+
* @param options - Options for writing the file or stringifying JSON. Can be an encoding string or an object.
|
|
28
|
+
* @returns A promise that resolves when the file has been written.
|
|
29
|
+
*/
|
|
30
|
+
export declare function writeJson(file: string, object: unknown, options?: BufferEncoding | WriteJsonOptions): Promise<void>;
|