local-diff-reviewer 4.0.0 → 4.0.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/dist/cli/start.js +76 -5
- package/package.json +1 -1
package/dist/cli/start.js
CHANGED
|
@@ -8,7 +8,7 @@ import { basename as basename3, dirname as dirname3, join as join5, resolve as r
|
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
|
|
10
10
|
// src/server/storage.ts
|
|
11
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
11
|
+
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
12
12
|
import { createHash } from "node:crypto";
|
|
13
13
|
import { homedir, platform } from "node:os";
|
|
14
14
|
import { basename, dirname, join } from "node:path";
|
|
@@ -68,16 +68,27 @@ async function attachLegacyComments(repoRoot, diffHash2, diffFiles) {
|
|
|
68
68
|
async function readCommentStore(repoRoot) {
|
|
69
69
|
const path = commentsPath(repoRoot);
|
|
70
70
|
try {
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
const text = await readFile(path, "utf8");
|
|
72
|
+
return normalizeStore(parseCommentStore(text));
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (!isMissingFileError(error)) {
|
|
75
|
+
const recovered = await recoverCommentStore(repoRoot, path, error);
|
|
76
|
+
if (recovered) return recovered;
|
|
77
|
+
}
|
|
73
78
|
return readLegacyComments(repoRoot);
|
|
74
79
|
}
|
|
75
80
|
}
|
|
76
81
|
async function writeComments(repoRoot, store) {
|
|
77
82
|
const path = commentsPath(repoRoot);
|
|
83
|
+
const tempPath = `${path}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
78
84
|
await mkdir(dirname(path), { recursive: true });
|
|
79
|
-
|
|
85
|
+
try {
|
|
86
|
+
await writeFile(tempPath, `${JSON.stringify(store, null, 2)}
|
|
80
87
|
`, "utf8");
|
|
88
|
+
await rename(tempPath, path);
|
|
89
|
+
} finally {
|
|
90
|
+
await rm(tempPath, { force: true }).catch(() => void 0);
|
|
91
|
+
}
|
|
81
92
|
}
|
|
82
93
|
function commentsPath(repoRoot) {
|
|
83
94
|
const repoName = basename(repoRoot) || "repo";
|
|
@@ -92,11 +103,71 @@ function commentLogsDir() {
|
|
|
92
103
|
}
|
|
93
104
|
async function readLegacyComments(repoRoot) {
|
|
94
105
|
try {
|
|
95
|
-
return normalizeStore(
|
|
106
|
+
return normalizeStore(parseCommentStore(await readFile(join(repoRoot, ".diff-review", "comments.json"), "utf8")));
|
|
96
107
|
} catch {
|
|
97
108
|
return { threads: [] };
|
|
98
109
|
}
|
|
99
110
|
}
|
|
111
|
+
async function recoverCommentStore(repoRoot, path, cause) {
|
|
112
|
+
try {
|
|
113
|
+
const recovered = normalizeStore(parseRecoverableCommentStore(await readFile(path, "utf8")));
|
|
114
|
+
await writeComments(repoRoot, recovered);
|
|
115
|
+
return recovered;
|
|
116
|
+
} catch {
|
|
117
|
+
console.warn(`Failed to read comment store ${path}: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
118
|
+
return void 0;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function parseCommentStore(text) {
|
|
122
|
+
const parsed = JSON.parse(text);
|
|
123
|
+
if (!Array.isArray(parsed.threads)) return { threads: [] };
|
|
124
|
+
return parsed;
|
|
125
|
+
}
|
|
126
|
+
function parseRecoverableCommentStore(text) {
|
|
127
|
+
const end = findRootJsonObjectEnd(text);
|
|
128
|
+
if (end === -1) throw new Error("Comment store does not contain a complete JSON object");
|
|
129
|
+
return parseCommentStore(text.slice(0, end));
|
|
130
|
+
}
|
|
131
|
+
function findRootJsonObjectEnd(text) {
|
|
132
|
+
let depth = 0;
|
|
133
|
+
let inString = false;
|
|
134
|
+
let escaped = false;
|
|
135
|
+
let started = false;
|
|
136
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
137
|
+
const char = text[index];
|
|
138
|
+
if (!started) {
|
|
139
|
+
if (/\s/.test(char)) continue;
|
|
140
|
+
if (char !== "{") return -1;
|
|
141
|
+
started = true;
|
|
142
|
+
}
|
|
143
|
+
if (inString) {
|
|
144
|
+
if (escaped) {
|
|
145
|
+
escaped = false;
|
|
146
|
+
} else if (char === "\\") {
|
|
147
|
+
escaped = true;
|
|
148
|
+
} else if (char === '"') {
|
|
149
|
+
inString = false;
|
|
150
|
+
}
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (char === '"') {
|
|
154
|
+
inString = true;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (char === "{") {
|
|
158
|
+
depth += 1;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (char === "}") {
|
|
162
|
+
depth -= 1;
|
|
163
|
+
if (depth === 0) return index + 1;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return -1;
|
|
167
|
+
}
|
|
168
|
+
function isMissingFileError(error) {
|
|
169
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
170
|
+
}
|
|
100
171
|
function normalizeStore(store) {
|
|
101
172
|
const groups = /* @__PURE__ */ new Map();
|
|
102
173
|
for (const thread of store.threads) {
|