json-sort-cli 4.2.3 → 4.3.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/CHANGELOG.md +12 -0
- package/README.md +2 -2
- package/cli-main.js +548 -0
- package/cli-update-notifier.js +793 -0
- package/cli.js +1 -422
- package/package.json +10 -8
- package/process-files.js +112 -100
package/process-files.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { constants } from "node:fs";
|
|
3
|
-
import
|
|
3
|
+
import * as filesystem from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { decodeJson, formatParsedJson, parseJson } from "./json-formatter.js";
|
|
6
6
|
|
|
@@ -18,129 +18,141 @@ function asError(error) {
|
|
|
18
18
|
return new Error(description, { cause: error });
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
21
|
+
export function createFileOperations({
|
|
22
|
+
fs = filesystem,
|
|
23
|
+
noFollow = constants.O_NOFOLLOW,
|
|
24
|
+
} = {}) {
|
|
25
|
+
const { lstat, open, realpath, rename, unlink } = fs;
|
|
25
26
|
|
|
26
|
-
async function
|
|
27
|
-
|
|
28
|
-
const pathStat = await lstat(filePath, { bigint: true });
|
|
29
|
-
if (pathStat.isSymbolicLink()) {
|
|
30
|
-
throw new Error(`Refusing to process symbolic link: ${filePath}`);
|
|
27
|
+
async function openWithoutFollowing(filePath) {
|
|
28
|
+
return open(filePath, constants.O_RDONLY | (noFollow ?? 0));
|
|
31
29
|
}
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
if (
|
|
37
|
-
throw new Error(`Refusing to process
|
|
38
|
-
}
|
|
39
|
-
if (!sameIdentity(pathStat, stat)) {
|
|
40
|
-
throw new Error(
|
|
41
|
-
`The file changed while it was being opened: ${filePath}`,
|
|
42
|
-
);
|
|
31
|
+
async function readFileSnapshot(filePath) {
|
|
32
|
+
const realPath = await realpath(filePath);
|
|
33
|
+
const pathStat = await lstat(filePath, { bigint: true });
|
|
34
|
+
if (pathStat.isSymbolicLink()) {
|
|
35
|
+
throw new Error(`Refusing to process symbolic link: ${filePath}`);
|
|
43
36
|
}
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
|
|
38
|
+
const handle = await openWithoutFollowing(filePath);
|
|
39
|
+
try {
|
|
40
|
+
const stat = await handle.stat({ bigint: true });
|
|
41
|
+
if (!stat.isFile()) {
|
|
42
|
+
throw new Error(`Refusing to process a non-file: ${filePath}`);
|
|
43
|
+
}
|
|
44
|
+
if (!sameIdentity(pathStat, stat)) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`The file changed while it was being opened: ${filePath}`,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
if ((await realpath(filePath)) !== realPath) {
|
|
50
|
+
throw new Error(`The file route changed while opening: ${filePath}`);
|
|
51
|
+
}
|
|
52
|
+
return { contents: await handle.readFile(), realPath, stat };
|
|
53
|
+
} finally {
|
|
54
|
+
await handle.close();
|
|
46
55
|
}
|
|
47
|
-
return { contents: await handle.readFile(), realPath, stat };
|
|
48
|
-
} finally {
|
|
49
|
-
await handle.close();
|
|
50
56
|
}
|
|
51
|
-
}
|
|
52
57
|
|
|
53
|
-
function sameIdentity(left, right) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async function commitFile(filePath, output, snapshot) {
|
|
64
|
-
if (
|
|
65
|
-
!snapshot?.stat ||
|
|
66
|
-
!Buffer.isBuffer(snapshot.contents) ||
|
|
67
|
-
typeof snapshot.realPath !== "string"
|
|
68
|
-
) {
|
|
69
|
-
throw new Error("Cannot safely commit without the original file snapshot");
|
|
58
|
+
function sameIdentity(left, right) {
|
|
59
|
+
return (
|
|
60
|
+
left.dev === right.dev &&
|
|
61
|
+
left.ino === right.ino &&
|
|
62
|
+
left.size === right.size &&
|
|
63
|
+
left.mtimeNs === right.mtimeNs &&
|
|
64
|
+
left.ctimeNs === right.ctimeNs
|
|
65
|
+
);
|
|
70
66
|
}
|
|
71
67
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
let temporaryHandle;
|
|
79
|
-
|
|
80
|
-
try {
|
|
81
|
-
if ((await realpath(filePath)) !== snapshot.realPath) {
|
|
68
|
+
async function commitFile(filePath, output, snapshot) {
|
|
69
|
+
if (
|
|
70
|
+
!snapshot?.stat ||
|
|
71
|
+
!Buffer.isBuffer(snapshot.contents) ||
|
|
72
|
+
typeof snapshot.realPath !== "string"
|
|
73
|
+
) {
|
|
82
74
|
throw new Error(
|
|
83
|
-
"
|
|
75
|
+
"Cannot safely commit without the original file snapshot",
|
|
84
76
|
);
|
|
85
77
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
78
|
+
|
|
79
|
+
const commitPath = snapshot.realPath;
|
|
80
|
+
const directory = path.dirname(commitPath);
|
|
81
|
+
const temporaryPath = path.join(
|
|
82
|
+
directory,
|
|
83
|
+
`.${path.basename(commitPath)}.${process.pid}.${randomUUID()}.tmp`,
|
|
90
84
|
);
|
|
91
|
-
|
|
92
|
-
await temporaryHandle.chmod(Number(snapshot.stat.mode & 0o7777n));
|
|
85
|
+
let temporaryHandle;
|
|
93
86
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
87
|
+
try {
|
|
88
|
+
if ((await realpath(filePath)) !== snapshot.realPath) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
"The file route changed after it was read; refusing to overwrite it",
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
temporaryHandle = await open(
|
|
94
|
+
temporaryPath,
|
|
95
|
+
"wx",
|
|
96
|
+
Number(snapshot.stat.mode & 0o7777n),
|
|
102
97
|
);
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
await temporaryHandle.close();
|
|
106
|
-
temporaryHandle = undefined;
|
|
98
|
+
await temporaryHandle.writeFile(output, "utf8");
|
|
99
|
+
await temporaryHandle.chmod(Number(snapshot.stat.mode & 0o7777n));
|
|
107
100
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
101
|
+
const temporaryStat = await temporaryHandle.stat({ bigint: true });
|
|
102
|
+
if (
|
|
103
|
+
temporaryStat.uid !== snapshot.stat.uid ||
|
|
104
|
+
temporaryStat.gid !== snapshot.stat.gid
|
|
105
|
+
) {
|
|
106
|
+
await temporaryHandle.chown(
|
|
107
|
+
Number(snapshot.stat.uid),
|
|
108
|
+
Number(snapshot.stat.gid),
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
await temporaryHandle.sync();
|
|
112
|
+
await temporaryHandle.close();
|
|
113
|
+
temporaryHandle = undefined;
|
|
117
114
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
115
|
+
const current = await readFileSnapshot(filePath);
|
|
116
|
+
if (
|
|
117
|
+
!sameIdentity(snapshot.stat, current.stat) ||
|
|
118
|
+
!snapshot.contents.equals(current.contents)
|
|
119
|
+
) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
"The file changed after it was read; refusing to overwrite it",
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
125
|
+
if ((await realpath(filePath)) !== snapshot.realPath) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
"The file route changed before commit; refusing to overwrite it",
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
await rename(temporaryPath, commitPath);
|
|
131
|
+
|
|
132
|
+
// Directory syncing is not supported by every platform. The file is
|
|
133
|
+
// already durable and atomically visible when this best-effort step runs.
|
|
129
134
|
try {
|
|
130
|
-
await
|
|
131
|
-
|
|
132
|
-
|
|
135
|
+
const directoryHandle = await open(directory, "r");
|
|
136
|
+
try {
|
|
137
|
+
await directoryHandle.sync();
|
|
138
|
+
} finally {
|
|
139
|
+
await directoryHandle.close();
|
|
140
|
+
}
|
|
141
|
+
} catch {}
|
|
142
|
+
} catch (error) {
|
|
143
|
+
if (temporaryHandle) {
|
|
144
|
+
await temporaryHandle.close().catch(() => {});
|
|
133
145
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if (temporaryHandle) {
|
|
137
|
-
await temporaryHandle.close().catch(() => {});
|
|
146
|
+
await unlink(temporaryPath).catch(() => {});
|
|
147
|
+
throw error;
|
|
138
148
|
}
|
|
139
|
-
await unlink(temporaryPath).catch(() => {});
|
|
140
|
-
throw error;
|
|
141
149
|
}
|
|
150
|
+
|
|
151
|
+
return { read: readFileSnapshot, write: commitFile };
|
|
142
152
|
}
|
|
143
153
|
|
|
154
|
+
const { read: readFileSnapshot, write: commitFile } = createFileOperations();
|
|
155
|
+
|
|
144
156
|
export class FileProcessingError extends Error {
|
|
145
157
|
constructor(filePath, stage, error) {
|
|
146
158
|
const cause = asError(error);
|