difit 5.0.6 → 5.0.8
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.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/dist/cli/comment.js +56 -14
- package/dist/cli/comment.test.js +93 -1
- package/dist/cli/utils.js +1 -1
- package/dist/client/assets/{arc-jpxKSIAg.js → arc-Ce71GpYh.js} +1 -1
- package/dist/client/assets/architecture-7EHR7CIX-DJuaV-Fy.js +1 -0
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-B92ZxjNC.js → architectureDiagram-3BPJPVTR-Co4nMikn.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-D3ySUhR3.js → blockDiagram-GPEHLZMM-BZ8XmnOe.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-BUzdNEmx.js → c4Diagram-AAUBKEIU-Bifc6015.js} +1 -1
- package/dist/client/assets/channel-DCmC_NJ8.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-1DQ-4Bz3.js → chunk-2J33WTMH-BKZtrrMM.js} +1 -1
- package/dist/client/assets/{chunk-3OPIFGDE-BQmtgKnq.js → chunk-3OPIFGDE-p1HDQKHx.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-DFX6Qy74.js → chunk-4BX2VUAB-B1JdC880.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-DtWki8a-.js → chunk-55IACEB6-BfDdrInp.js} +1 -1
- package/dist/client/assets/{chunk-5ZQYHXKU-uSJ_ZgCu.js → chunk-5ZQYHXKU-B0UFDeNc.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-BB6xZgSD.js → chunk-727SXJPM-C1GLS2mt.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-B1Cm665s.js → chunk-AQP2D5EJ-CdhTjctN.js} +1 -1
- package/dist/client/assets/{chunk-BSJP7CBP-C15Vti01.js → chunk-BSJP7CBP-8ezJh_7H.js} +1 -1
- package/dist/client/assets/{chunk-CSCIHK7Q-CaTF_T3W.js → chunk-CSCIHK7Q-DNeLjTAX.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-D1irlAGU.js → chunk-FMBD7UC4-CwtitMT8.js} +1 -1
- package/dist/client/assets/{chunk-KSCS5N6A-DGAjxObb.js → chunk-KSCS5N6A-DdLtmoYV.js} +1 -1
- package/dist/client/assets/{chunk-L5ZTLDWV-F5Tij0A9.js → chunk-L5ZTLDWV-DZrSg_8S.js} +1 -1
- package/dist/client/assets/{chunk-LZXEDZCA-BkUKm4Z2.js → chunk-LZXEDZCA-CPsWjeej.js} +2 -2
- package/dist/client/assets/{chunk-ND2GUHAM-C0rukZcz.js → chunk-ND2GUHAM-B3wT6dSk.js} +1 -1
- package/dist/client/assets/{chunk-NZK2D7GU-DRm_z6tq.js → chunk-NZK2D7GU-DTCItxUJ.js} +1 -1
- package/dist/client/assets/{chunk-O5CBEL6O-BAuwNGEL.js → chunk-O5CBEL6O-HSc7nkWp.js} +1 -1
- package/dist/client/assets/chunk-QZHKN3VN-Dl7i9TFZ.js +1 -0
- package/dist/client/assets/chunk-WU5MYG2G-H9-XOWa2.js +1 -0
- package/dist/client/assets/{chunk-XPW4576I-2r3neXxa.js → chunk-XPW4576I-DWA1L2SH.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-CIis5MpW.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CIis5MpW.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-BF8GqjYH.js → cose-bilkent-S5V4N54A-BA0dewjs.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-BcWO-l21.js → dagre-BM42HDAG-DQ6amw4B.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-ChdDM46l.js → diagram-2AECGRRQ-lt3q0PPx.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-DOPYGuMt.js → diagram-5GNKFQAL-RN_cFqeH.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-D6gmyyoA.js → diagram-KO2AKTUF-69-WWf-i.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-C93nmtac.js → diagram-LMA3HP47-CJp_AQpE.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-Cgm6DAM8.js → diagram-OG6HWLK6-ld8yVyEO.js} +1 -1
- package/dist/client/assets/{dist-C7Q1SrJ8.js → dist-MrZ1zwt-.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-WzvVTKP0.js → erDiagram-TEJ5UH35-C9-9-98D.js} +1 -1
- package/dist/client/assets/eventmodeling-FCH6USID-sUz5BqkA.js +1 -0
- package/dist/client/assets/{flowDiagram-I6XJVG4X-BRAzB7RU.js → flowDiagram-I6XJVG4X-BIWAxSyl.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-Bned6Md4.js → ganttDiagram-6RSMTGT7-DEekfrSb.js} +1 -1
- package/dist/client/assets/{gitGraph-WXDBUCRP-BsCF41a6.js → gitGraph-WXDBUCRP-iLmA5ZOg.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-sjN-t2iw.js → gitGraphDiagram-PVQCEYII-Cp9no67W.js} +1 -1
- package/dist/client/assets/index-D6ilk_N-.js +83 -0
- package/dist/client/assets/index-DJEQpFaQ.css +2 -0
- package/dist/client/assets/{info-J43DQDTF-BnH-OC8Z.js → info-J43DQDTF-WGiiYO2H.js} +1 -1
- package/dist/client/assets/{infoDiagram-5YYISTIA-s8VJEWX-.js → infoDiagram-5YYISTIA-DSq2paHS.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-Cr3iGWKj.js → ishikawaDiagram-YF4QCWOH-BRvjhcHO.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-DsSeOYHM.js → journeyDiagram-JHISSGLW-BeojzJiQ.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU--hgCxMq5.js → kanban-definition-UN3LZRKU-Cxd2TSzC.js} +1 -1
- package/dist/client/assets/{line-BgzUTU3B.js → line-DfNX00iH.js} +1 -1
- package/dist/client/assets/{linear-Cj1FBal1.js → linear-BqTigxmw.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-oUly7wXy.js → mermaid-parser.core-B_EZyTa5.js} +2 -2
- package/dist/client/assets/{mermaid.core-QvJGj5O5.js → mermaid.core-Ssrtijme.js} +3 -3
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-Cwyk4Byp.js → mindmap-definition-RKZ34NQL-CFexQB41.js} +1 -1
- package/dist/client/assets/{packet-YPE3B663-C559rspA.js → packet-YPE3B663-DWRftxLl.js} +1 -1
- package/dist/client/assets/{pie-LRSECV5Y-DZyJ170B.js → pie-LRSECV5Y-FBJQi9YS.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-lGuBsE2s.js → pieDiagram-4H26LBE5-rWmojAfB.js} +1 -1
- package/dist/client/assets/prism-gdscript-C1gnCftx.js +1 -0
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-BCczJuw5.js → quadrantDiagram-W4KKPZXB-ClhgNDn7.js} +1 -1
- package/dist/client/assets/{radar-GUYGQ44K-Ckz6UanW.js → radar-GUYGQ44K-C948fgjI.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-C4-ojYg3.js → requirementDiagram-4Y6WPE33-BuMMQGw0.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-CFpy-I_l.js → sankeyDiagram-5OEKKPKP-CEd3YUpA.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-DmD28UaM.js → sequenceDiagram-3UESZ5HK-DgsZJ52B.js} +1 -1
- package/dist/client/assets/{src-BWoEPgyG.js → src-CAgFxagX.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-BNaiNMdT.js → stateDiagram-AJRCARHV-Cg0npyBC.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CpV01lL-.js +1 -0
- package/dist/client/assets/{timeline-definition-PNZ67QCA-DQyT9Hys.js → timeline-definition-PNZ67QCA-Be8CZnBb.js} +1 -1
- package/dist/client/assets/{treeView-BLDUP644-BDNxHT3u.js → treeView-BLDUP644-B4fII_xt.js} +1 -1
- package/dist/client/assets/{treemap-LRROVOQU-mnVB3buf.js → treemap-LRROVOQU-Dy2AIK2x.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-BaDh5wWt.js → vennDiagram-CIIHVFJN-DehXkI0n.js} +1 -1
- package/dist/client/assets/{wardley-L42UT6IY-CZ9sEBPa.js → wardley-L42UT6IY-AqOhXLdx.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-B_afE6fY.js → wardleyDiagram-YWT4CUSO-BzK61bpB.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-6Na9BohO.js → xychartDiagram-2RQKCTM6-D0uYHNx6.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/file-watcher.js +4 -1
- package/dist/server/git-diff.d.ts +1 -0
- package/dist/server/git-diff.js +79 -21
- package/dist/server/git-diff.test.js +110 -0
- package/dist/server/server.js +16 -0
- package/dist/server/server.test.js +56 -4
- package/package.json +2 -1
- package/dist/client/assets/architecture-7EHR7CIX-CGrQkVgf.js +0 -1
- package/dist/client/assets/channel-DG8kr4eK.js +0 -1
- package/dist/client/assets/chunk-QZHKN3VN-M1-XSu5b.js +0 -1
- package/dist/client/assets/chunk-WU5MYG2G-BvFjxMiG.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-BtF02SOc.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BtF02SOc.js +0 -1
- package/dist/client/assets/eventmodeling-FCH6USID-D2pfiIpz.js +0 -1
- package/dist/client/assets/index-BC8r72Kq.js +0 -82
- package/dist/client/assets/index-DxvaSOa7.css +0 -2
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CkEmqrGj.js +0 -1
|
@@ -11,6 +11,7 @@ export declare class GitDiffParser {
|
|
|
11
11
|
private resolveBaseCommitish;
|
|
12
12
|
parseDiff(selection: DiffSelection, ignoreWhitespace?: boolean, contextLines?: number): Promise<DiffResponse>;
|
|
13
13
|
private parseUnifiedDiff;
|
|
14
|
+
private splitPlainUnifiedDiff;
|
|
14
15
|
private getGitattributesSourceArgs;
|
|
15
16
|
private parseGitattributesGeneratedOutput;
|
|
16
17
|
private getGitattributesGeneratedPaths;
|
package/dist/server/git-diff.js
CHANGED
|
@@ -113,15 +113,68 @@ export class GitDiffParser {
|
|
|
113
113
|
parseUnifiedDiff(diffText) {
|
|
114
114
|
const files = [];
|
|
115
115
|
const fileBlocks = diffText.split(/^diff --git /m).slice(1);
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
116
|
+
if (fileBlocks.length > 0) {
|
|
117
|
+
for (const fileBlock of fileBlocks) {
|
|
118
|
+
const block = `diff --git ${fileBlock}`;
|
|
119
|
+
const file = this.parseFileBlock(block);
|
|
120
|
+
if (file) {
|
|
121
|
+
files.push(file);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return files;
|
|
125
|
+
}
|
|
126
|
+
for (const fileBlock of this.splitPlainUnifiedDiff(diffText)) {
|
|
127
|
+
const file = this.parseFileBlock(fileBlock, 'plain');
|
|
119
128
|
if (file) {
|
|
120
129
|
files.push(file);
|
|
121
130
|
}
|
|
122
131
|
}
|
|
123
132
|
return files;
|
|
124
133
|
}
|
|
134
|
+
splitPlainUnifiedDiff(diffText) {
|
|
135
|
+
const lines = diffText.split(/\r?\n/);
|
|
136
|
+
const blocks = [];
|
|
137
|
+
let blockStart = null;
|
|
138
|
+
let remainingOldLines = 0;
|
|
139
|
+
let remainingNewLines = 0;
|
|
140
|
+
for (let index = 0; index < lines.length; index++) {
|
|
141
|
+
const line = lines[index];
|
|
142
|
+
if (blockStart !== null && line.startsWith('@@')) {
|
|
143
|
+
const match = line.match(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);
|
|
144
|
+
if (match) {
|
|
145
|
+
remainingOldLines = parseInt(match[2] ?? '1');
|
|
146
|
+
remainingNewLines = parseInt(match[4] ?? '1');
|
|
147
|
+
}
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (blockStart !== null && (remainingOldLines > 0 || remainingNewLines > 0)) {
|
|
151
|
+
if (line.startsWith('\\')) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (line.startsWith('+')) {
|
|
155
|
+
remainingNewLines--;
|
|
156
|
+
}
|
|
157
|
+
else if (line.startsWith('-')) {
|
|
158
|
+
remainingOldLines--;
|
|
159
|
+
}
|
|
160
|
+
else if (line.startsWith(' ')) {
|
|
161
|
+
remainingOldLines--;
|
|
162
|
+
remainingNewLines--;
|
|
163
|
+
}
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (line.startsWith('--- ') && lines[index + 1]?.startsWith('+++ ')) {
|
|
167
|
+
if (blockStart !== null) {
|
|
168
|
+
blocks.push(lines.slice(blockStart, index).join('\n'));
|
|
169
|
+
}
|
|
170
|
+
blockStart = index;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (blockStart !== null) {
|
|
174
|
+
blocks.push(lines.slice(blockStart).join('\n'));
|
|
175
|
+
}
|
|
176
|
+
return blocks;
|
|
177
|
+
}
|
|
125
178
|
getGitattributesSourceArgs(ref) {
|
|
126
179
|
if (ref === 'working' || ref === '.') {
|
|
127
180
|
return [];
|
|
@@ -179,23 +232,25 @@ export class GitDiffParser {
|
|
|
179
232
|
}
|
|
180
233
|
return files.map((file) => generatedPaths.has(file.path) ? { ...file, isGenerated: true } : file);
|
|
181
234
|
}
|
|
182
|
-
decodeGitPath(rawPath) {
|
|
235
|
+
decodeGitPath(rawPath, stripGitPrefix = true) {
|
|
183
236
|
if (typeof rawPath !== 'string') {
|
|
184
237
|
return undefined;
|
|
185
238
|
}
|
|
186
|
-
const
|
|
239
|
+
const tabIndex = rawPath.indexOf('\t');
|
|
240
|
+
const pathWithoutTimestamp = tabIndex === -1 ? rawPath : rawPath.slice(0, tabIndex);
|
|
241
|
+
const trimmed = pathWithoutTimestamp.startsWith('"') && pathWithoutTimestamp.endsWith('"')
|
|
242
|
+
? pathWithoutTimestamp.slice(1, -1)
|
|
243
|
+
: pathWithoutTimestamp;
|
|
187
244
|
const gitPrefixes = ['a/', 'b/', 'c/', 'i/', 'w/'];
|
|
188
245
|
let withoutPrefix = trimmed;
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
246
|
+
if (stripGitPrefix) {
|
|
247
|
+
for (const prefix of gitPrefixes) {
|
|
248
|
+
if (withoutPrefix.startsWith(prefix)) {
|
|
249
|
+
withoutPrefix = withoutPrefix.slice(prefix.length);
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
193
252
|
}
|
|
194
253
|
}
|
|
195
|
-
const tabIndex = withoutPrefix.indexOf('\t');
|
|
196
|
-
if (tabIndex !== -1) {
|
|
197
|
-
withoutPrefix = withoutPrefix.slice(0, tabIndex);
|
|
198
|
-
}
|
|
199
254
|
if (withoutPrefix === '/dev/null') {
|
|
200
255
|
return undefined;
|
|
201
256
|
}
|
|
@@ -261,11 +316,11 @@ export class GitDiffParser {
|
|
|
261
316
|
}
|
|
262
317
|
return Buffer.from(bytes).toString('utf8');
|
|
263
318
|
}
|
|
264
|
-
extractPathFromLine(line, prefix) {
|
|
319
|
+
extractPathFromLine(line, prefix, stripGitPrefix = true) {
|
|
265
320
|
if (!line?.startsWith(prefix)) {
|
|
266
321
|
return undefined;
|
|
267
322
|
}
|
|
268
|
-
return this.decodeGitPath(line.slice(prefix.length));
|
|
323
|
+
return this.decodeGitPath(line.slice(prefix.length), stripGitPrefix);
|
|
269
324
|
}
|
|
270
325
|
parseDiffHeaderPaths(headerLine) {
|
|
271
326
|
if (!headerLine.startsWith('diff --git ')) {
|
|
@@ -304,7 +359,7 @@ export class GitDiffParser {
|
|
|
304
359
|
newPath: this.decodeGitPath(rawNewPath),
|
|
305
360
|
};
|
|
306
361
|
}
|
|
307
|
-
parseFileBlock(block) {
|
|
362
|
+
parseFileBlock(block, format = 'git') {
|
|
308
363
|
const lines = block.split('\n');
|
|
309
364
|
const headerLine = lines[0];
|
|
310
365
|
const headerPaths = this.parseDiffHeaderPaths(headerLine);
|
|
@@ -312,12 +367,15 @@ export class GitDiffParser {
|
|
|
312
367
|
const plusLine = lines.find((line) => line.startsWith('+++ '));
|
|
313
368
|
const renameFromLine = lines.find((line) => line.startsWith('rename from '));
|
|
314
369
|
const renameToLine = lines.find((line) => line.startsWith('rename to '));
|
|
315
|
-
const
|
|
316
|
-
const
|
|
370
|
+
const stripGitPrefix = format !== 'plain';
|
|
371
|
+
const plusPath = this.extractPathFromLine(plusLine, '+++ ', stripGitPrefix);
|
|
372
|
+
const minusPath = this.extractPathFromLine(minusLine, '--- ', stripGitPrefix);
|
|
317
373
|
const renameFromPath = this.extractPathFromLine(renameFromLine, 'rename from ');
|
|
318
374
|
const renameToPath = this.extractPathFromLine(renameToLine, 'rename to ');
|
|
319
|
-
const
|
|
320
|
-
const
|
|
375
|
+
const parsedNewPath = renameToPath ?? plusPath ?? headerPaths?.newPath;
|
|
376
|
+
const parsedOldPath = renameFromPath ?? minusPath ?? headerPaths?.oldPath;
|
|
377
|
+
const newPath = parsedNewPath ?? parsedOldPath;
|
|
378
|
+
const oldPath = parsedOldPath ?? newPath;
|
|
321
379
|
if (!newPath) {
|
|
322
380
|
return null;
|
|
323
381
|
}
|
|
@@ -333,7 +391,7 @@ export class GitDiffParser {
|
|
|
333
391
|
else if (deletedFileMode || plusLine?.includes('/dev/null')) {
|
|
334
392
|
status = 'deleted';
|
|
335
393
|
}
|
|
336
|
-
else if (oldPath !== newPath) {
|
|
394
|
+
else if (format !== 'plain' && oldPath !== newPath) {
|
|
337
395
|
status = 'renamed';
|
|
338
396
|
}
|
|
339
397
|
// Common properties for all files
|
|
@@ -765,6 +765,116 @@ index 0000000..1234567
|
|
|
765
765
|
isGenerated: false,
|
|
766
766
|
});
|
|
767
767
|
});
|
|
768
|
+
it('parses plain unified diff output without git headers', () => {
|
|
769
|
+
const diffContent = `--- file1.txt\t2026-07-11 12:00:00
|
|
770
|
+
+++ file2.txt\t2026-07-11 12:00:01
|
|
771
|
+
@@ -1,2 +1,2 @@
|
|
772
|
+
unchanged
|
|
773
|
+
-old content
|
|
774
|
+
+new content`;
|
|
775
|
+
const result = parser.parseStdinDiff(diffContent);
|
|
776
|
+
expect(result).toMatchObject({
|
|
777
|
+
commit: 'stdin diff',
|
|
778
|
+
isEmpty: false,
|
|
779
|
+
files: [
|
|
780
|
+
{
|
|
781
|
+
path: 'file2.txt',
|
|
782
|
+
status: 'modified',
|
|
783
|
+
additions: 1,
|
|
784
|
+
deletions: 1,
|
|
785
|
+
isGenerated: false,
|
|
786
|
+
},
|
|
787
|
+
],
|
|
788
|
+
});
|
|
789
|
+
});
|
|
790
|
+
it('normalizes CRLF in plain unified diff output', () => {
|
|
791
|
+
const diffContent = [
|
|
792
|
+
'--- old.txt',
|
|
793
|
+
'+++ new.txt',
|
|
794
|
+
'@@ -1 +1 @@',
|
|
795
|
+
'-old content',
|
|
796
|
+
'+new content',
|
|
797
|
+
].join('\r\n');
|
|
798
|
+
const result = parser.parseStdinDiff(diffContent);
|
|
799
|
+
expect(result.files).toHaveLength(1);
|
|
800
|
+
expect(result.files[0]).toMatchObject({
|
|
801
|
+
path: 'new.txt',
|
|
802
|
+
chunks: [
|
|
803
|
+
{
|
|
804
|
+
lines: [
|
|
805
|
+
{ type: 'delete', content: 'old content' },
|
|
806
|
+
{ type: 'add', content: 'new content' },
|
|
807
|
+
],
|
|
808
|
+
},
|
|
809
|
+
],
|
|
810
|
+
});
|
|
811
|
+
});
|
|
812
|
+
it('parses multiple concatenated plain unified diffs', () => {
|
|
813
|
+
const diffContent = `--- first-old.txt
|
|
814
|
+
+++ first-new.txt
|
|
815
|
+
@@ -1 +1 @@
|
|
816
|
+
-before
|
|
817
|
+
+after
|
|
818
|
+
--- second-old.txt
|
|
819
|
+
+++ second-new.txt
|
|
820
|
+
@@ -1 +1,2 @@
|
|
821
|
+
existing
|
|
822
|
+
+added`;
|
|
823
|
+
const result = parser.parseStdinDiff(diffContent);
|
|
824
|
+
expect(result.files).toMatchObject([
|
|
825
|
+
{
|
|
826
|
+
path: 'first-new.txt',
|
|
827
|
+
status: 'modified',
|
|
828
|
+
additions: 1,
|
|
829
|
+
deletions: 1,
|
|
830
|
+
},
|
|
831
|
+
{
|
|
832
|
+
path: 'second-new.txt',
|
|
833
|
+
status: 'modified',
|
|
834
|
+
additions: 1,
|
|
835
|
+
deletions: 0,
|
|
836
|
+
},
|
|
837
|
+
]);
|
|
838
|
+
});
|
|
839
|
+
it('does not treat header-like hunk content as another plain diff', () => {
|
|
840
|
+
const diffContent = `--- old.txt
|
|
841
|
+
+++ new.txt
|
|
842
|
+
@@ -1,2 +1,2 @@
|
|
843
|
+
--- deleted content
|
|
844
|
+
+++ added content`;
|
|
845
|
+
const result = parser.parseStdinDiff(diffContent);
|
|
846
|
+
expect(result.files).toHaveLength(1);
|
|
847
|
+
expect(result.files[0]).toMatchObject({
|
|
848
|
+
path: 'new.txt',
|
|
849
|
+
additions: 1,
|
|
850
|
+
deletions: 1,
|
|
851
|
+
});
|
|
852
|
+
});
|
|
853
|
+
it('detects added and deleted files in plain unified diffs', () => {
|
|
854
|
+
const diffContent = `--- /dev/null
|
|
855
|
+
+++ added.txt
|
|
856
|
+
@@ -0,0 +1 @@
|
|
857
|
+
+added
|
|
858
|
+
--- deleted.txt
|
|
859
|
+
+++ /dev/null
|
|
860
|
+
@@ -1 +0,0 @@
|
|
861
|
+
-deleted`;
|
|
862
|
+
const result = parser.parseStdinDiff(diffContent);
|
|
863
|
+
expect(result.files).toMatchObject([
|
|
864
|
+
{
|
|
865
|
+
path: 'added.txt',
|
|
866
|
+
status: 'added',
|
|
867
|
+
additions: 1,
|
|
868
|
+
deletions: 0,
|
|
869
|
+
},
|
|
870
|
+
{
|
|
871
|
+
path: 'deleted.txt',
|
|
872
|
+
status: 'deleted',
|
|
873
|
+
additions: 0,
|
|
874
|
+
deletions: 1,
|
|
875
|
+
},
|
|
876
|
+
]);
|
|
877
|
+
});
|
|
768
878
|
it('should handle deleted files', async () => {
|
|
769
879
|
const diffContent = `diff --git a/deleted.txt b/deleted.txt
|
|
770
880
|
deleted file mode 100644
|
package/dist/server/server.js
CHANGED
|
@@ -567,6 +567,22 @@ export async function startServer(options) {
|
|
|
567
567
|
res.status(400).json({ error: 'Invalid comment import data' });
|
|
568
568
|
}
|
|
569
569
|
});
|
|
570
|
+
app.delete('/api/comments/:threadId', (req, res) => {
|
|
571
|
+
const selection = getCommentSelectionFromQuery(req.query);
|
|
572
|
+
const session = getOrCreateCommentSession(selection);
|
|
573
|
+
const threadId = req.params.threadId;
|
|
574
|
+
const nextThreads = session.threads.filter((thread) => thread.id !== threadId);
|
|
575
|
+
if (nextThreads.length === session.threads.length) {
|
|
576
|
+
res.status(404).json({ error: `Thread not found: ${threadId}` });
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
updateCommentSession(selection, nextThreads);
|
|
580
|
+
res.json({
|
|
581
|
+
success: true,
|
|
582
|
+
threadId,
|
|
583
|
+
version: session.version,
|
|
584
|
+
});
|
|
585
|
+
});
|
|
570
586
|
app.get('/api/comments-json', (req, res) => {
|
|
571
587
|
const selection = getCommentSelectionFromQuery(req.query);
|
|
572
588
|
const session = getOrCreateCommentSession(selection);
|
|
@@ -191,7 +191,10 @@ describe('Server Integration Tests', () => {
|
|
|
191
191
|
};
|
|
192
192
|
it('merges concurrent agent additions instead of clobbering on a stale push', async () => {
|
|
193
193
|
const port = await getAvailablePort(4966);
|
|
194
|
-
const result = await startServer({
|
|
194
|
+
const result = await startServer({
|
|
195
|
+
preferredPort: port,
|
|
196
|
+
openBrowser: false,
|
|
197
|
+
});
|
|
195
198
|
try {
|
|
196
199
|
// Browser establishes a thread; it now knows version 1.
|
|
197
200
|
await postThreads(result.port, [makeThread('t1', 'src/a.ts', 10, 'human thread')]);
|
|
@@ -234,7 +237,10 @@ describe('Server Integration Tests', () => {
|
|
|
234
237
|
});
|
|
235
238
|
it('replaces (honoring deletions) when the push version matches', async () => {
|
|
236
239
|
const port = await getAvailablePort(4966);
|
|
237
|
-
const result = await startServer({
|
|
240
|
+
const result = await startServer({
|
|
241
|
+
preferredPort: port,
|
|
242
|
+
openBrowser: false,
|
|
243
|
+
});
|
|
238
244
|
try {
|
|
239
245
|
await postThreads(result.port, [makeThread('t1', 'src/a.ts', 10, 'human thread')]);
|
|
240
246
|
const afterFirst = await getSession(result.port);
|
|
@@ -258,7 +264,10 @@ describe('Server Integration Tests', () => {
|
|
|
258
264
|
});
|
|
259
265
|
it('bumps the version when a reply is imported (so the change is broadcast)', async () => {
|
|
260
266
|
const port = await getAvailablePort(4966);
|
|
261
|
-
const result = await startServer({
|
|
267
|
+
const result = await startServer({
|
|
268
|
+
preferredPort: port,
|
|
269
|
+
openBrowser: false,
|
|
270
|
+
});
|
|
262
271
|
try {
|
|
263
272
|
await postThreads(result.port, [makeThread('t1', 'src/a.ts', 10, 'human thread')]);
|
|
264
273
|
const before = await getSession(result.port);
|
|
@@ -792,6 +801,46 @@ describe('Server Integration Tests', () => {
|
|
|
792
801
|
const threads = data.threads.filter((t) => t.filePath === 'src/dedup.ts');
|
|
793
802
|
expect(threads).toHaveLength(1);
|
|
794
803
|
});
|
|
804
|
+
it('DELETE /api/comments/:threadId removes the thread and bumps the version', async () => {
|
|
805
|
+
await fetch(`http://localhost:${port}/api/comment-imports`, {
|
|
806
|
+
method: 'POST',
|
|
807
|
+
headers: { 'Content-Type': 'application/json' },
|
|
808
|
+
body: JSON.stringify([
|
|
809
|
+
{
|
|
810
|
+
type: 'thread',
|
|
811
|
+
id: 'delete-me',
|
|
812
|
+
filePath: 'src/delete-test.ts',
|
|
813
|
+
position: { side: 'new', line: 3 },
|
|
814
|
+
body: 'Thread to delete',
|
|
815
|
+
},
|
|
816
|
+
]),
|
|
817
|
+
});
|
|
818
|
+
const beforeResponse = await fetch(`http://localhost:${port}/api/comments-json`);
|
|
819
|
+
const before = (await beforeResponse.json());
|
|
820
|
+
expect(before.threads.some((t) => t.id === 'delete-me')).toBe(true);
|
|
821
|
+
const deleteResponse = await fetch(`http://localhost:${port}/api/comments/delete-me`, {
|
|
822
|
+
method: 'DELETE',
|
|
823
|
+
});
|
|
824
|
+
const deleteData = (await deleteResponse.json());
|
|
825
|
+
expect(deleteResponse.ok).toBe(true);
|
|
826
|
+
expect(deleteData).toMatchObject({
|
|
827
|
+
success: true,
|
|
828
|
+
threadId: 'delete-me',
|
|
829
|
+
});
|
|
830
|
+
expect(deleteData.version).toBe(before.version + 1);
|
|
831
|
+
const afterResponse = await fetch(`http://localhost:${port}/api/comments-json`);
|
|
832
|
+
const after = (await afterResponse.json());
|
|
833
|
+
expect(after.threads.some((t) => t.id === 'delete-me')).toBe(false);
|
|
834
|
+
});
|
|
835
|
+
it('DELETE /api/comments/:threadId returns 404 for unknown thread', async () => {
|
|
836
|
+
const response = await fetch(`http://localhost:${port}/api/comments/does-not-exist`, {
|
|
837
|
+
method: 'DELETE',
|
|
838
|
+
});
|
|
839
|
+
expect(response.status).toBe(404);
|
|
840
|
+
const data = (await response.json());
|
|
841
|
+
expect(data).toHaveProperty('error');
|
|
842
|
+
expect(data.error).toContain('does-not-exist');
|
|
843
|
+
});
|
|
795
844
|
it('isolates comment sessions between different diff selections', async () => {
|
|
796
845
|
const importServer = await startServer({
|
|
797
846
|
selection: { targetCommitish: 'HEAD', baseCommitish: 'HEAD^' },
|
|
@@ -961,7 +1010,10 @@ describe('Server Integration Tests', () => {
|
|
|
961
1010
|
const data = (await response.json());
|
|
962
1011
|
expect(response.ok).toBe(true);
|
|
963
1012
|
expect(data.specialOptions).toHaveLength(3);
|
|
964
|
-
expect(data.specialOptions).not.toContainEqual({
|
|
1013
|
+
expect(data.specialOptions).not.toContainEqual({
|
|
1014
|
+
value: 'merge-base',
|
|
1015
|
+
label: 'Merge Base',
|
|
1016
|
+
});
|
|
965
1017
|
expect(data.branches).toEqual([{ name: 'main', current: true }]);
|
|
966
1018
|
expect(data.commits).toEqual([
|
|
967
1019
|
{ hash: 'abc1234', shortHash: 'abc1234', message: 'Test commit' },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "difit",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.8",
|
|
4
4
|
"description": "A lightweight command-line tool that spins up a local web server to display Git commit diffs in a GitHub-like Files changed view",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -79,6 +79,7 @@
|
|
|
79
79
|
"react-dom": "^19.1.0",
|
|
80
80
|
"react-hotkeys-hook": "^5.2.4",
|
|
81
81
|
"react-markdown": "^10.1.0",
|
|
82
|
+
"remark-breaks": "^4.0.0",
|
|
82
83
|
"remark-gfm": "^4.0.1",
|
|
83
84
|
"simple-git": "^3.28.0"
|
|
84
85
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./chunk-NNHCCRGN-DlpIbxXb.js";import{x as e}from"./mermaid-parser.core-oUly7wXy.js";export{e as createArchitectureServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{at as e,ot as t}from"./chunk-CSCIHK7Q-CaTF_T3W.js";var n=(n,r)=>t.lang.round(e.parse(n)[r]);export{n as t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{h as e}from"./src-BWoEPgyG.js";var t=class{constructor(e){this.init=e,this.records=this.init()}static{e(this,`ImperativeState`)}reset(){this.records=this.init()}};export{t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{h as e,p as t}from"./src-BWoEPgyG.js";import{x as n}from"./chunk-CSCIHK7Q-CaTF_T3W.js";var r=e(e=>{let{securityLevel:r}=n(),i=t(`body`);return r===`sandbox`&&(i=t((t(`#i${e}`).node()?.contentDocument??document).body)),i.select(`#${e}`)},`selectSvgElement`);export{r as t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{h as e}from"./src-BWoEPgyG.js";import"./chunk-CSCIHK7Q-CaTF_T3W.js";import"./chunk-5ZQYHXKU-uSJ_ZgCu.js";import"./chunk-O5CBEL6O-BAuwNGEL.js";import"./chunk-FMBD7UC4-D1irlAGU.js";import"./chunk-BSJP7CBP-C15Vti01.js";import"./chunk-L5ZTLDWV-F5Tij0A9.js";import"./chunk-ND2GUHAM-C0rukZcz.js";import"./chunk-55IACEB6-DtWki8a-.js";import"./chunk-2J33WTMH-1DQ-4Bz3.js";import"./chunk-NZK2D7GU-DRm_z6tq.js";import"./chunk-3OPIFGDE-BQmtgKnq.js";import"./chunk-KSCS5N6A-DGAjxObb.js";import"./chunk-LZXEDZCA-BkUKm4Z2.js";import{i as t,n,r,t as i}from"./chunk-727SXJPM-BB6xZgSD.js";var a={parser:n,get db(){return new i},renderer:r,styles:t,init:e(e=>{e.class||={},e.class.arrowMarkerAbsolute=e.arrowMarkerAbsolute},`init`)};export{a as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{h as e}from"./src-BWoEPgyG.js";import"./chunk-CSCIHK7Q-CaTF_T3W.js";import"./chunk-5ZQYHXKU-uSJ_ZgCu.js";import"./chunk-O5CBEL6O-BAuwNGEL.js";import"./chunk-FMBD7UC4-D1irlAGU.js";import"./chunk-BSJP7CBP-C15Vti01.js";import"./chunk-L5ZTLDWV-F5Tij0A9.js";import"./chunk-ND2GUHAM-C0rukZcz.js";import"./chunk-55IACEB6-DtWki8a-.js";import"./chunk-2J33WTMH-1DQ-4Bz3.js";import"./chunk-NZK2D7GU-DRm_z6tq.js";import"./chunk-3OPIFGDE-BQmtgKnq.js";import"./chunk-KSCS5N6A-DGAjxObb.js";import"./chunk-LZXEDZCA-BkUKm4Z2.js";import{i as t,n,r,t as i}from"./chunk-727SXJPM-BB6xZgSD.js";var a={parser:n,get db(){return new i},renderer:r,styles:t,init:e(e=>{e.class||={},e.class.arrowMarkerAbsolute=e.arrowMarkerAbsolute},`init`)};export{a as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./chunk-NNHCCRGN-DlpIbxXb.js";import{y as e}from"./mermaid-parser.core-oUly7wXy.js";export{e as createEventModelingServices};
|