critique 0.1.134 → 0.1.135
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/balance-delimiters.d.ts.map +1 -1
- package/dist/balance-delimiters.js +178 -1
- package/dist/balance-delimiters.test.js +317 -5
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +11 -1
- package/dist/diff-utils.d.ts +5 -0
- package/dist/diff-utils.d.ts.map +1 -1
- package/dist/diff-utils.js +15 -0
- package/dist/diff-utils.test.js +18 -1
- package/package.json +1 -1
- package/src/balance-delimiters.test.ts +333 -5
- package/src/balance-delimiters.ts +212 -1
- package/src/cli.tsx +12 -0
- package/src/diff-utils.test.ts +25 -0
- package/src/diff-utils.ts +15 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"balance-delimiters.d.ts","sourceRoot":"","sources":["../src/balance-delimiters.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"balance-delimiters.d.ts","sourceRoot":"","sources":["../src/balance-delimiters.ts"],"names":[],"mappings":"AAmEA;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CActE;AAwbD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CA4E5E"}
|
|
@@ -32,7 +32,7 @@ const htmlCommentRule = {
|
|
|
32
32
|
const LANGUAGE_DELIMITERS = {
|
|
33
33
|
typescript: [{ token: "`" }, cStyleBlockCommentRule],
|
|
34
34
|
python: [{ token: '"""' }, { token: "'''" }],
|
|
35
|
-
markdown: [{ token: "```" }],
|
|
35
|
+
markdown: [{ token: "```", classifyFence: classifyMarkdownFence }],
|
|
36
36
|
go: [{ token: "`" }, cStyleBlockCommentRule],
|
|
37
37
|
rust: [cStyleBlockCommentRule],
|
|
38
38
|
cpp: [cStyleBlockCommentRule],
|
|
@@ -201,6 +201,148 @@ function escapeDelimiterAt(lines, hunkLineIndex, column) {
|
|
|
201
201
|
return prefix + content.slice(0, column) + "\\" + content.slice(column);
|
|
202
202
|
});
|
|
203
203
|
}
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
// Contextual fence classification and repair (markdown code fences)
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
/**
|
|
208
|
+
* Classify a ``` occurrence as a markdown fence opener, closer, or not a fence.
|
|
209
|
+
*
|
|
210
|
+
* Returns null if the occurrence is not a valid block-level fence (e.g. inline
|
|
211
|
+
* triple-backtick in prose, or indented more than 3 spaces).
|
|
212
|
+
* Returns "open" if followed by an info string (language tag).
|
|
213
|
+
* Returns "close" if nothing follows the backtick run (only whitespace).
|
|
214
|
+
*/
|
|
215
|
+
function classifyMarkdownFence(content, column) {
|
|
216
|
+
// Must be at start of line with at most 3 spaces of indentation
|
|
217
|
+
const beforeFence = content.slice(0, column);
|
|
218
|
+
if (beforeFence.length > 3 || /\S/.test(beforeFence))
|
|
219
|
+
return null;
|
|
220
|
+
// Count consecutive backticks (support 4+ backtick fences per CommonMark)
|
|
221
|
+
let fenceLen = 0;
|
|
222
|
+
for (let i = column; i < content.length && content[i] === "`"; i++)
|
|
223
|
+
fenceLen++;
|
|
224
|
+
if (fenceLen < 3)
|
|
225
|
+
return null;
|
|
226
|
+
// What comes after the backtick run?
|
|
227
|
+
const afterFence = content.slice(column + fenceLen).trim();
|
|
228
|
+
// Closing fence: nothing after backticks (only whitespace)
|
|
229
|
+
if (!afterFence)
|
|
230
|
+
return "close";
|
|
231
|
+
// Opening fence: has info string (language tag)
|
|
232
|
+
// Info string must not contain backticks (CommonMark spec)
|
|
233
|
+
if (!afterFence.includes("`"))
|
|
234
|
+
return "open";
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Simulate a sequential walk through classified fences to detect boundary
|
|
239
|
+
* artifacts. Markdown code fences don't nest, so depth toggles between 0
|
|
240
|
+
* (outside) and 1 (inside).
|
|
241
|
+
*
|
|
242
|
+
* - "open" (has info string) always pushes depth to 1.
|
|
243
|
+
* - "close" (bare fence) decrements if inside, or acts as a bare opener
|
|
244
|
+
* if already outside (a code block without a language tag).
|
|
245
|
+
*
|
|
246
|
+
* Conflicts are counted when a must-open fires while already inside (depth
|
|
247
|
+
* was already 1) or when other impossible transitions occur.
|
|
248
|
+
*/
|
|
249
|
+
function walkFences(fences, startDepth) {
|
|
250
|
+
let depth = startDepth;
|
|
251
|
+
let conflicts = 0;
|
|
252
|
+
for (const fence of fences) {
|
|
253
|
+
if (fence.kind === "open") {
|
|
254
|
+
if (depth > 0)
|
|
255
|
+
conflicts++;
|
|
256
|
+
depth = 1;
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
// "close" (bare fence): close if inside, otherwise bare opener
|
|
260
|
+
if (depth > 0) {
|
|
261
|
+
depth = 0;
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
depth = 1;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return { startDepth, endDepth: depth, conflicts };
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Repair context-dependent fences (like markdown ```) using sequential
|
|
272
|
+
* open/close pairing instead of simple odd/even counting.
|
|
273
|
+
*
|
|
274
|
+
* Tries two starting states (outside vs inside a code block), picks the
|
|
275
|
+
* walk with fewer conflicts, and adds synthetic fence tokens inline:
|
|
276
|
+
* - If the hunk starts inside a block: prepend ``` to the first content
|
|
277
|
+
* line so the boundary closer has something to close.
|
|
278
|
+
* - If the hunk ends inside a block: append ``` to the last content
|
|
279
|
+
* line so the boundary opener is properly closed.
|
|
280
|
+
* Tokens are added inline (no new lines) to preserve patch header counts.
|
|
281
|
+
*/
|
|
282
|
+
function repairContextualFences(contentLines, lines, rule) {
|
|
283
|
+
if (!rule.classifyFence)
|
|
284
|
+
return [...lines];
|
|
285
|
+
const occurrences = findDelimiterOccurrences(contentLines, rule.token);
|
|
286
|
+
if (occurrences.length === 0)
|
|
287
|
+
return [...lines];
|
|
288
|
+
// Classify each occurrence as fence open, close, or not-a-fence
|
|
289
|
+
const fences = [];
|
|
290
|
+
for (const occ of occurrences) {
|
|
291
|
+
const content = contentLines[occ.contentLineIndex]?.content;
|
|
292
|
+
if (!content)
|
|
293
|
+
continue;
|
|
294
|
+
const kind = rule.classifyFence(content, occ.column);
|
|
295
|
+
if (kind) {
|
|
296
|
+
fences.push({ occurrence: occ, kind });
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (fences.length === 0)
|
|
300
|
+
return [...lines];
|
|
301
|
+
// Try both starting states
|
|
302
|
+
const walk0 = walkFences(fences, 0);
|
|
303
|
+
const walk1 = walkFences(fences, 1);
|
|
304
|
+
// Pick better walk: fewer conflicts → fewer repairs → content heuristic
|
|
305
|
+
let chosen;
|
|
306
|
+
if (walk0.conflicts < walk1.conflicts) {
|
|
307
|
+
chosen = walk0;
|
|
308
|
+
}
|
|
309
|
+
else if (walk1.conflicts < walk0.conflicts) {
|
|
310
|
+
chosen = walk1;
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
const repairs0 = (walk0.startDepth > 0 ? 1 : 0) + (walk0.endDepth > 0 ? 1 : 0);
|
|
314
|
+
const repairs1 = (walk1.startDepth > 0 ? 1 : 0) + (walk1.endDepth > 0 ? 1 : 0);
|
|
315
|
+
if (repairs0 < repairs1) {
|
|
316
|
+
chosen = walk0;
|
|
317
|
+
}
|
|
318
|
+
else if (repairs1 < repairs0) {
|
|
319
|
+
chosen = walk1;
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
// Still tied: disambiguate by content position relative to the first fence.
|
|
323
|
+
// If there's non-empty content before the first fence in the hunk, the fence
|
|
324
|
+
// is likely closing a block from before the hunk → prefer depth=1 (starts inside).
|
|
325
|
+
// This produces better tree-sitter pairing: the prepended ``` + original ```
|
|
326
|
+
// form a matched pair, while appended inline ``` doesn't close a fence.
|
|
327
|
+
const firstIdx = fences[0]?.occurrence.contentLineIndex ?? 0;
|
|
328
|
+
const hasContentBefore = contentLines.slice(0, firstIdx).some((line) => line.content.trim() !== "");
|
|
329
|
+
chosen = hasContentBefore ? walk1 : walk0;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
let result = [...lines];
|
|
333
|
+
// Append synthetic closer to end of last content line (inline, no new lines)
|
|
334
|
+
if (chosen.endDepth > 0) {
|
|
335
|
+
result = appendClosingTokensToLastContentLine(result, rule.token, 1);
|
|
336
|
+
}
|
|
337
|
+
// Prepend synthetic opener to start of first content line (inline, no new lines).
|
|
338
|
+
// Pass the first fence's hunk line index so the search only looks at lines
|
|
339
|
+
// before the boundary closer — the opener must precede it to form a pair.
|
|
340
|
+
if (chosen.startDepth > 0) {
|
|
341
|
+
const firstFenceHunkLine = fences[0]?.occurrence.hunkLineIndex;
|
|
342
|
+
result = prependOpeningTokenToFirstContentLine(result, rule.token, firstFenceHunkLine);
|
|
343
|
+
}
|
|
344
|
+
return result;
|
|
345
|
+
}
|
|
204
346
|
function getRuleOpenTokens(rule) {
|
|
205
347
|
return rule.openTokens ?? [rule.token];
|
|
206
348
|
}
|
|
@@ -239,6 +381,35 @@ function appendClosingTokensToLastContentLine(lines, closeToken, count) {
|
|
|
239
381
|
return `${line} ${closingSuffix}`;
|
|
240
382
|
});
|
|
241
383
|
}
|
|
384
|
+
function prependOpeningTokenToFirstContentLine(lines, openToken, beforeHunkLineIndex) {
|
|
385
|
+
// Prefer a blank/whitespace content line to avoid creating a fake info string
|
|
386
|
+
// (e.g. "``` handler() {" makes tree-sitter think "handler()" is a language).
|
|
387
|
+
// Only search among lines BEFORE the first fence so the synthetic opener
|
|
388
|
+
// appears before the boundary closer (they need to pair).
|
|
389
|
+
const firstContentLineIndex = lines.findIndex(isDiffContentLine);
|
|
390
|
+
if (firstContentLineIndex === -1) {
|
|
391
|
+
return [...lines];
|
|
392
|
+
}
|
|
393
|
+
const searchEnd = beforeHunkLineIndex ?? lines.length;
|
|
394
|
+
let targetIndex = firstContentLineIndex;
|
|
395
|
+
for (let i = firstContentLineIndex; i < searchEnd; i++) {
|
|
396
|
+
const line = lines[i];
|
|
397
|
+
if (!isDiffContentLine(line))
|
|
398
|
+
continue;
|
|
399
|
+
if (line.slice(1).trim() === "") {
|
|
400
|
+
targetIndex = i;
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return lines.map((line, index) => {
|
|
405
|
+
if (index !== targetIndex || !isDiffContentLine(line)) {
|
|
406
|
+
return line;
|
|
407
|
+
}
|
|
408
|
+
const prefix = line[0] ?? "";
|
|
409
|
+
const content = line.slice(1);
|
|
410
|
+
return `${prefix}${openToken} ${content}`;
|
|
411
|
+
});
|
|
412
|
+
}
|
|
242
413
|
/**
|
|
243
414
|
* Balance paired delimiters in a unified diff patch for correct syntax
|
|
244
415
|
* highlighting.
|
|
@@ -284,6 +455,12 @@ export function balanceDelimiters(rawDiff, filetype) {
|
|
|
284
455
|
if (!isSymmetricRule(rule)) {
|
|
285
456
|
continue;
|
|
286
457
|
}
|
|
458
|
+
// Contextual fence pairing (markdown code fences): uses sequential
|
|
459
|
+
// open/close tracking instead of simple odd/even parity.
|
|
460
|
+
if (rule.classifyFence) {
|
|
461
|
+
repairedLines = repairContextualFences(contentLines, repairedLines, rule);
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
287
464
|
const occurrences = findDelimiterOccurrences(contentLines, rule.token);
|
|
288
465
|
if (occurrences.length % 2 === 0) {
|
|
289
466
|
continue;
|
|
@@ -439,7 +439,7 @@ describe("balanceDelimiters", () => {
|
|
|
439
439
|
]);
|
|
440
440
|
expect(balanceDelimiters(patch, "markdown")).toBe(patch);
|
|
441
441
|
});
|
|
442
|
-
it("
|
|
442
|
+
it("prepends synthetic opener when hunk starts inside a code block", () => {
|
|
443
443
|
const patch = mdPatch([
|
|
444
444
|
" inside fenced block",
|
|
445
445
|
" ```",
|
|
@@ -448,8 +448,9 @@ describe("balanceDelimiters", () => {
|
|
|
448
448
|
]);
|
|
449
449
|
const result = balanceDelimiters(patch, "markdown");
|
|
450
450
|
const lines = result.split("\n");
|
|
451
|
-
|
|
452
|
-
expect(lines[
|
|
451
|
+
// Synthetic ``` opener prepended inline to first content line
|
|
452
|
+
expect(lines[3]).toBe(" ``` inside fenced block");
|
|
453
|
+
expect(lines[4]).toBe(" ```");
|
|
453
454
|
});
|
|
454
455
|
it("does not modify when only inline code backticks are present", () => {
|
|
455
456
|
const patch = mdPatch([
|
|
@@ -459,7 +460,7 @@ describe("balanceDelimiters", () => {
|
|
|
459
460
|
]);
|
|
460
461
|
expect(balanceDelimiters(patch, "markdown")).toBe(patch);
|
|
461
462
|
});
|
|
462
|
-
it("
|
|
463
|
+
it("appends synthetic closer when hunk ends with unclosed opener", () => {
|
|
463
464
|
const patch = mdPatch([
|
|
464
465
|
" ```ts",
|
|
465
466
|
" const x = 1",
|
|
@@ -468,8 +469,319 @@ describe("balanceDelimiters", () => {
|
|
|
468
469
|
]);
|
|
469
470
|
const result = balanceDelimiters(patch, "markdown");
|
|
470
471
|
const lines = result.split("\n");
|
|
471
|
-
|
|
472
|
+
// Original content untouched
|
|
473
|
+
expect(lines[3]).toBe(" ```ts");
|
|
472
474
|
expect(lines[4]).toBe(" const x = 1");
|
|
475
|
+
// Synthetic ``` closer appended inline to last content line
|
|
476
|
+
expect(lines[lines.length - 1]).toBe("+new line ```");
|
|
477
|
+
});
|
|
478
|
+
it("adds synthetic fences at both boundaries when even count but first=closer last=opener (6 tokens)", () => {
|
|
479
|
+
const patch = mdPatch([
|
|
480
|
+
" ```",
|
|
481
|
+
" ",
|
|
482
|
+
" ## Section",
|
|
483
|
+
" ",
|
|
484
|
+
" ```ts",
|
|
485
|
+
" const a = 1",
|
|
486
|
+
" ```",
|
|
487
|
+
" ",
|
|
488
|
+
" ```ts",
|
|
489
|
+
" const b = 2",
|
|
490
|
+
" ```",
|
|
491
|
+
" ",
|
|
492
|
+
"+```ts",
|
|
493
|
+
]);
|
|
494
|
+
const result = balanceDelimiters(patch, "markdown");
|
|
495
|
+
const lines = result.split("\n");
|
|
496
|
+
// Synthetic opener prepended inline to first content line
|
|
497
|
+
expect(lines[3]).toBe(" ``` ```");
|
|
498
|
+
// Synthetic closer appended inline to last content line
|
|
499
|
+
expect(lines[lines.length - 1]).toBe("+```ts ```");
|
|
500
|
+
// middle fences stay untouched
|
|
501
|
+
expect(lines[7]).toBe(" ```ts");
|
|
502
|
+
expect(lines[9]).toBe(" ```");
|
|
503
|
+
expect(lines[11]).toBe(" ```ts");
|
|
504
|
+
expect(lines[13]).toBe(" ```");
|
|
505
|
+
});
|
|
506
|
+
it("adds synthetic fences at both boundaries with 4 tokens (bare, ```ts, bare, ```ts)", () => {
|
|
507
|
+
const patch = mdPatch([
|
|
508
|
+
" inside code block",
|
|
509
|
+
" ```",
|
|
510
|
+
" ",
|
|
511
|
+
" ```ts",
|
|
512
|
+
" const x = 1",
|
|
513
|
+
" ```",
|
|
514
|
+
" ",
|
|
515
|
+
"+```ts",
|
|
516
|
+
]);
|
|
517
|
+
const result = balanceDelimiters(patch, "markdown");
|
|
518
|
+
const lines = result.split("\n");
|
|
519
|
+
// Synthetic opener prepended inline to first content line
|
|
520
|
+
expect(lines[3]).toBe(" ``` inside code block");
|
|
521
|
+
// Synthetic closer appended inline to last content line
|
|
522
|
+
expect(lines[lines.length - 1]).toBe("+```ts ```");
|
|
523
|
+
});
|
|
524
|
+
it("returns patch unchanged when 4 tokens are fully balanced (```ts, bare, ```ts, bare)", () => {
|
|
525
|
+
const patch = mdPatch([
|
|
526
|
+
" ```ts",
|
|
527
|
+
" const a = 1",
|
|
528
|
+
" ```",
|
|
529
|
+
" ",
|
|
530
|
+
" ```ts",
|
|
531
|
+
" const b = 2",
|
|
532
|
+
" ```",
|
|
533
|
+
"+New paragraph",
|
|
534
|
+
]);
|
|
535
|
+
expect(balanceDelimiters(patch, "markdown")).toBe(patch);
|
|
536
|
+
});
|
|
537
|
+
it("adds synthetic fences at both boundaries when 2 tokens are bare-closer then opener", () => {
|
|
538
|
+
const patch = mdPatch([
|
|
539
|
+
" inside block",
|
|
540
|
+
" ```",
|
|
541
|
+
" ",
|
|
542
|
+
"+```ts",
|
|
543
|
+
]);
|
|
544
|
+
const result = balanceDelimiters(patch, "markdown");
|
|
545
|
+
const lines = result.split("\n");
|
|
546
|
+
// Synthetic opener prepended inline to first content line
|
|
547
|
+
expect(lines[3]).toBe(" ``` inside block");
|
|
548
|
+
// Synthetic closer appended inline to last content line
|
|
549
|
+
expect(lines[lines.length - 1]).toBe("+```ts ```");
|
|
550
|
+
});
|
|
551
|
+
it("returns patch unchanged for 2 balanced tokens (```ts then bare)", () => {
|
|
552
|
+
const patch = mdPatch([
|
|
553
|
+
" ```ts",
|
|
554
|
+
" const x = 1",
|
|
555
|
+
" ```",
|
|
556
|
+
"+New paragraph",
|
|
557
|
+
]);
|
|
558
|
+
expect(balanceDelimiters(patch, "markdown")).toBe(patch);
|
|
559
|
+
});
|
|
560
|
+
it("returns patch unchanged for two bare fences (open + close, no language)", () => {
|
|
561
|
+
const patch = mdPatch([
|
|
562
|
+
" ```",
|
|
563
|
+
" some code",
|
|
564
|
+
" ```",
|
|
565
|
+
"+New paragraph",
|
|
566
|
+
]);
|
|
567
|
+
expect(balanceDelimiters(patch, "markdown")).toBe(patch);
|
|
568
|
+
});
|
|
569
|
+
it("ignores inline triple backticks in prose (not at start of line)", () => {
|
|
570
|
+
const patch = mdPatch([
|
|
571
|
+
" Use the ``` delimiter for code fences",
|
|
572
|
+
"-old",
|
|
573
|
+
"+new",
|
|
574
|
+
]);
|
|
575
|
+
expect(balanceDelimiters(patch, "markdown")).toBe(patch);
|
|
576
|
+
});
|
|
577
|
+
it("treats fences with up to 3 spaces indent as valid", () => {
|
|
578
|
+
const patch = mdPatch([
|
|
579
|
+
" ```ts",
|
|
580
|
+
" const x = 1",
|
|
581
|
+
" ```",
|
|
582
|
+
"+New paragraph",
|
|
583
|
+
]);
|
|
584
|
+
// 3 spaces + ``` = column 3, indent 3 = valid fence
|
|
585
|
+
expect(balanceDelimiters(patch, "markdown")).toBe(patch);
|
|
586
|
+
});
|
|
587
|
+
it("ignores fences indented more than 3 spaces (code indentation)", () => {
|
|
588
|
+
const patch = mdPatch([
|
|
589
|
+
" ```ts",
|
|
590
|
+
" const x = 1",
|
|
591
|
+
"-old",
|
|
592
|
+
"+new",
|
|
593
|
+
]);
|
|
594
|
+
// 4+ spaces = not a fence, treated as code content → no escaping
|
|
595
|
+
expect(balanceDelimiters(patch, "markdown")).toBe(patch);
|
|
596
|
+
});
|
|
597
|
+
it("handles 4-backtick fence pair correctly", () => {
|
|
598
|
+
const patch = mdPatch([
|
|
599
|
+
" ````ts",
|
|
600
|
+
" const x = 1",
|
|
601
|
+
" ````",
|
|
602
|
+
"+New paragraph",
|
|
603
|
+
]);
|
|
604
|
+
expect(balanceDelimiters(patch, "markdown")).toBe(patch);
|
|
605
|
+
});
|
|
606
|
+
it("recognizes closing fence with trailing spaces", () => {
|
|
607
|
+
const patch = mdPatch([
|
|
608
|
+
" ```ts",
|
|
609
|
+
" const x = 1",
|
|
610
|
+
" ``` ",
|
|
611
|
+
"+New paragraph",
|
|
612
|
+
]);
|
|
613
|
+
expect(balanceDelimiters(patch, "markdown")).toBe(patch);
|
|
614
|
+
});
|
|
615
|
+
it("single bare fence with content on both sides treats as opener (appends closer)", () => {
|
|
616
|
+
// Ambiguous: bare ``` with content before AND after.
|
|
617
|
+
// Tie-break prefers depth=1 (prepend opener) since content exists before fence.
|
|
618
|
+
const patch = mdPatch([
|
|
619
|
+
" Intro paragraph",
|
|
620
|
+
" ```",
|
|
621
|
+
" code line",
|
|
622
|
+
"+new line",
|
|
623
|
+
]);
|
|
624
|
+
const result = balanceDelimiters(patch, "markdown");
|
|
625
|
+
const lines = result.split("\n");
|
|
626
|
+
// Prepend opener on first content line (content before fence)
|
|
627
|
+
expect(lines[3]).toBe(" ``` Intro paragraph");
|
|
628
|
+
expect(lines[4]).toBe(" ```");
|
|
629
|
+
});
|
|
630
|
+
it("single bare fence with content only after treats as opener (appends closer)", () => {
|
|
631
|
+
const patch = mdPatch([
|
|
632
|
+
" ```",
|
|
633
|
+
" code line",
|
|
634
|
+
"-old",
|
|
635
|
+
"+new",
|
|
636
|
+
]);
|
|
637
|
+
const result = balanceDelimiters(patch, "markdown");
|
|
638
|
+
const lines = result.split("\n");
|
|
639
|
+
// No content before fence → depth=0 → append closer
|
|
640
|
+
expect(lines[3]).toBe(" ```");
|
|
641
|
+
expect(lines[lines.length - 1]).toBe("+new ```");
|
|
642
|
+
});
|
|
643
|
+
it("prefers blank line for synthetic opener to avoid fake info string", () => {
|
|
644
|
+
const patch = mdPatch([
|
|
645
|
+
" ",
|
|
646
|
+
" inside code block",
|
|
647
|
+
" ```",
|
|
648
|
+
"-old line",
|
|
649
|
+
"+new line",
|
|
650
|
+
]);
|
|
651
|
+
const result = balanceDelimiters(patch, "markdown");
|
|
652
|
+
const lines = result.split("\n");
|
|
653
|
+
// Blank line before fence is used for synthetic opener (no fake info string)
|
|
654
|
+
expect(lines[3]).toBe(" ``` ");
|
|
655
|
+
expect(lines[4]).toBe(" inside code block");
|
|
656
|
+
expect(lines[5]).toBe(" ```");
|
|
657
|
+
});
|
|
658
|
+
it("handles two hunks independently for markdown fences", () => {
|
|
659
|
+
const patch = [
|
|
660
|
+
"--- file.md",
|
|
661
|
+
"+++ file.md",
|
|
662
|
+
"@@ -5,4 +5,4 @@",
|
|
663
|
+
" ```ts",
|
|
664
|
+
" const x = 1",
|
|
665
|
+
" ```",
|
|
666
|
+
"+New paragraph",
|
|
667
|
+
"@@ -20,4 +20,4 @@",
|
|
668
|
+
" inside block",
|
|
669
|
+
" ```",
|
|
670
|
+
"-old line",
|
|
671
|
+
"+new line",
|
|
672
|
+
].join("\n");
|
|
673
|
+
const result = balanceDelimiters(patch, "markdown");
|
|
674
|
+
const lines = result.split("\n");
|
|
675
|
+
// First hunk: balanced, no changes
|
|
676
|
+
expect(lines[3]).toBe(" ```ts");
|
|
677
|
+
expect(lines[5]).toBe(" ```");
|
|
678
|
+
// Second hunk: bare closer at boundary → synthetic opener prepended inline
|
|
679
|
+
const secondHunkIdx = lines.findIndex((l, i) => i > 2 && l.startsWith("@@"));
|
|
680
|
+
expect(lines[secondHunkIdx + 1]).toBe(" ``` inside block");
|
|
681
|
+
expect(lines[secondHunkIdx + 2]).toBe(" ```");
|
|
682
|
+
});
|
|
683
|
+
it("handles real README.md hunk with boundary fences (from critique.work patch)", () => {
|
|
684
|
+
// Real hunk from https://critique.work/v/daa808658ee537a745b80101ba3195ae.patch
|
|
685
|
+
// First hunk: starts inside a code block (bare ``` closer), ends with ```ts opener
|
|
686
|
+
const patch = [
|
|
687
|
+
"--- README.md",
|
|
688
|
+
"+++ README.md",
|
|
689
|
+
"@@ -741,12 +741,14 @@",
|
|
690
|
+
" }),",
|
|
691
|
+
" )",
|
|
692
|
+
" ```",
|
|
693
|
+
" ",
|
|
694
|
+
" ## Base Path",
|
|
695
|
+
" ",
|
|
696
|
+
"+For standalone API servers (without Vite), set the base path in the constructor:",
|
|
697
|
+
"+",
|
|
698
|
+
" ```ts",
|
|
699
|
+
" import { Spiceflow } from 'spiceflow'",
|
|
700
|
+
" ",
|
|
701
|
+
" const app = new Spiceflow({ basePath: '/api/v1' })",
|
|
702
|
+
" app.route({",
|
|
703
|
+
" method: 'GET',",
|
|
704
|
+
"@@ -754,12 +756,47 @@",
|
|
705
|
+
" handler() {",
|
|
706
|
+
" return 'Hello'",
|
|
707
|
+
" },",
|
|
708
|
+
" }) // Accessible at /api/v1/hello",
|
|
709
|
+
" ```",
|
|
710
|
+
" ",
|
|
711
|
+
"+### Base Path with Vite (RSC apps)",
|
|
712
|
+
"+",
|
|
713
|
+
"+When using Spiceflow as a full-stack RSC framework with Vite, configure the base path via Vite's `base` option instead of the constructor:",
|
|
714
|
+
"+",
|
|
715
|
+
"+```ts",
|
|
716
|
+
"+// vite.config.ts",
|
|
717
|
+
"+import { defineConfig } from 'vite'",
|
|
718
|
+
"+import { spiceflowPlugin } from 'spiceflow/vite'",
|
|
719
|
+
"+",
|
|
720
|
+
"+export default defineConfig({",
|
|
721
|
+
"+ base: '/my-app',",
|
|
722
|
+
"+ plugins: [spiceflowPlugin({ entry: 'src/main.tsx' })],",
|
|
723
|
+
"+})",
|
|
724
|
+
"+```",
|
|
725
|
+
"+",
|
|
726
|
+
"+The base path must be an absolute path starting with `/`. CDN URLs and relative paths are not supported.",
|
|
727
|
+
"+",
|
|
728
|
+
" ## Async Generators (Streaming)",
|
|
729
|
+
" ",
|
|
730
|
+
" Async generators will create a server sent event response.",
|
|
731
|
+
" ",
|
|
732
|
+
" ```ts",
|
|
733
|
+
" // server.ts",
|
|
734
|
+
].join("\n");
|
|
735
|
+
const result = balanceDelimiters(patch, "markdown");
|
|
736
|
+
expect(result).toMatchInlineSnapshot(`
|
|
737
|
+
"--- README.md
|
|
738
|
+
+++ README.md
|
|
739
|
+
@@ -741,12 +741,14 @@
|
|
740
|
+
\`\`\` }),
|
|
741
|
+
)
|
|
742
|
+
\`\`\`
|
|
743
|
+
|
|
744
|
+
## Base Path
|
|
745
|
+
|
|
746
|
+
+For standalone API servers (without Vite), set the base path in the constructor:
|
|
747
|
+
+
|
|
748
|
+
\`\`\`ts
|
|
749
|
+
import { Spiceflow } from 'spiceflow'
|
|
750
|
+
|
|
751
|
+
const app = new Spiceflow({ basePath: '/api/v1' })
|
|
752
|
+
app.route({
|
|
753
|
+
method: 'GET', \`\`\`
|
|
754
|
+
@@ -754,12 +756,47 @@
|
|
755
|
+
\`\`\` handler() {
|
|
756
|
+
return 'Hello'
|
|
757
|
+
},
|
|
758
|
+
}) // Accessible at /api/v1/hello
|
|
759
|
+
\`\`\`
|
|
760
|
+
|
|
761
|
+
+### Base Path with Vite (RSC apps)
|
|
762
|
+
+
|
|
763
|
+
+When using Spiceflow as a full-stack RSC framework with Vite, configure the base path via Vite's \`base\` option instead of the constructor:
|
|
764
|
+
+
|
|
765
|
+
+\`\`\`ts
|
|
766
|
+
+// vite.config.ts
|
|
767
|
+
+import { defineConfig } from 'vite'
|
|
768
|
+
+import { spiceflowPlugin } from 'spiceflow/vite'
|
|
769
|
+
+
|
|
770
|
+
+export default defineConfig({
|
|
771
|
+
+ base: '/my-app',
|
|
772
|
+
+ plugins: [spiceflowPlugin({ entry: 'src/main.tsx' })],
|
|
773
|
+
+})
|
|
774
|
+
+\`\`\`
|
|
775
|
+
+
|
|
776
|
+
+The base path must be an absolute path starting with \`/\`. CDN URLs and relative paths are not supported.
|
|
777
|
+
+
|
|
778
|
+
## Async Generators (Streaming)
|
|
779
|
+
|
|
780
|
+
Async generators will create a server sent event response.
|
|
781
|
+
|
|
782
|
+
\`\`\`ts
|
|
783
|
+
// server.ts \`\`\`"
|
|
784
|
+
`);
|
|
473
785
|
});
|
|
474
786
|
});
|
|
475
787
|
describe("scala", () => {
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.tsx"],"names":[],"mappings":";AAOA,OAAO,gCAAgC,CAAC;AAYxC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAwB/B,OAAO,
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.tsx"],"names":[],"mappings":";AAOA,OAAO,gCAAgC,CAAC;AAYxC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAwB/B,OAAO,EAiBL,KAAK,UAAU,EAEhB,MAAM,iBAAiB,CAAC;AA45CzB,MAAM,WAAW,QAAQ;IACvB,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED,wBAAgB,GAAG,CAAC,EAAE,WAAW,EAAE,EAAE,QAAQ,GAAG,KAAK,CAAC,YAAY,CAkVjE"}
|
package/dist/cli.js
CHANGED
|
@@ -26,7 +26,7 @@ import { debounce } from "./utils.js";
|
|
|
26
26
|
import { DiffView, DirectoryTreeView } from "./components/index.js";
|
|
27
27
|
import { logger } from "./logger.js";
|
|
28
28
|
import { saveStoredLicenseKey } from "./license.js";
|
|
29
|
-
import { buildGitCommand, filterParsedFilesByPatterns, getFileName, getFileStatus, getOldFileName, countChanges, getViewMode, processFiles, detectFiletype, stripSubmoduleHeaders, parseGitDiffFiles, getDirtySubmodulePaths, buildSubmoduleDiffCommand, getFilterPatterns, IGNORED_FILES, } from "./diff-utils.js";
|
|
29
|
+
import { buildGitCommand, ensureGitRepo, filterParsedFilesByPatterns, getFileName, getFileStatus, getOldFileName, countChanges, getViewMode, processFiles, detectFiletype, stripSubmoduleHeaders, parseGitDiffFiles, getDirtySubmodulePaths, buildSubmoduleDiffCommand, getFilterPatterns, IGNORED_FILES, } from "./diff-utils.js";
|
|
30
30
|
import packageJson from "../package.json" assert { type: "json" };
|
|
31
31
|
// Lazy-load watcher only when --watch is used
|
|
32
32
|
let watcherModule = null;
|
|
@@ -1364,6 +1364,7 @@ cli
|
|
|
1364
1364
|
description: "Filter files by glob pattern (can be used multiple times)",
|
|
1365
1365
|
}))
|
|
1366
1366
|
.action(async (options) => {
|
|
1367
|
+
ensureGitRepo();
|
|
1367
1368
|
const { parseHunksWithIds, hunkToStableId, } = await import("./review/index.js");
|
|
1368
1369
|
// Build git command - unstaged by default, staged with --staged
|
|
1369
1370
|
const gitCommand = buildGitCommand({
|
|
@@ -1427,6 +1428,7 @@ cli
|
|
|
1427
1428
|
cli
|
|
1428
1429
|
.command("hunks add [...ids]", "Stage specific hunks by their stable ID")
|
|
1429
1430
|
.action(async (ids) => {
|
|
1431
|
+
ensureGitRepo();
|
|
1430
1432
|
if (!ids || ids.length === 0) {
|
|
1431
1433
|
console.error("Usage: critique hunks add <hunk-id> [<hunk-id> ...]");
|
|
1432
1434
|
console.error("Use 'critique hunks list' to see available hunk IDs.");
|
|
@@ -1540,6 +1542,10 @@ cli
|
|
|
1540
1542
|
.option("--stdin", "Read diff from stdin (for use as a pager)")
|
|
1541
1543
|
.option("--scrollback", "Output to terminal scrollback instead of TUI (auto-enabled when non-TTY)")
|
|
1542
1544
|
.action(async (base, head, options) => {
|
|
1545
|
+
// Ensure we're inside a git repository before doing anything
|
|
1546
|
+
if (!options.stdin) {
|
|
1547
|
+
ensureGitRepo();
|
|
1548
|
+
}
|
|
1543
1549
|
// Apply theme if specified (zustand subscription auto-persists)
|
|
1544
1550
|
if (options.theme && themeNames.includes(options.theme)) {
|
|
1545
1551
|
useAppStore.setState({ themeName: options.theme });
|
|
@@ -1811,6 +1817,7 @@ cli
|
|
|
1811
1817
|
.option("--json", "Output JSON to stdout (implies --web)")
|
|
1812
1818
|
.option("--resume [id]", "Resume a previous review (shows select if no ID provided)")
|
|
1813
1819
|
.action(async (base, head, options) => {
|
|
1820
|
+
ensureGitRepo();
|
|
1814
1821
|
try {
|
|
1815
1822
|
// Handle resume mode
|
|
1816
1823
|
if (options.resume !== undefined) {
|
|
@@ -1877,6 +1884,7 @@ cli
|
|
|
1877
1884
|
cli
|
|
1878
1885
|
.command("difftool <local> <remote>", "Git difftool integration")
|
|
1879
1886
|
.action(async (local, remote) => {
|
|
1887
|
+
ensureGitRepo();
|
|
1880
1888
|
if (!process.stdout.isTTY) {
|
|
1881
1889
|
execSync(`git diff --no-ext-diff "${local}" "${remote}"`, {
|
|
1882
1890
|
stdio: "inherit",
|
|
@@ -1910,6 +1918,7 @@ cli
|
|
|
1910
1918
|
cli
|
|
1911
1919
|
.command("pick <branch>", "Pick files from another branch to apply to HEAD")
|
|
1912
1920
|
.action(async (branch) => {
|
|
1921
|
+
ensureGitRepo();
|
|
1913
1922
|
try {
|
|
1914
1923
|
const { stdout: currentBranch } = await execAsync("git branch --show-current");
|
|
1915
1924
|
const current = currentBranch.trim();
|
|
@@ -2046,6 +2055,7 @@ cli
|
|
|
2046
2055
|
}))
|
|
2047
2056
|
.option("--title <title>", "HTML document title")
|
|
2048
2057
|
.action(async (base, head, options) => {
|
|
2058
|
+
ensureGitRepo();
|
|
2049
2059
|
// Build git command and get diff
|
|
2050
2060
|
const gitCommand = buildGitCommand({
|
|
2051
2061
|
staged: options.staged,
|
package/dist/diff-utils.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if the current directory is inside a git repository.
|
|
3
|
+
* If not, print a friendly error message and exit.
|
|
4
|
+
*/
|
|
5
|
+
export declare function ensureGitRepo(): void;
|
|
1
6
|
/**
|
|
2
7
|
* Strip submodule status lines from git diff output.
|
|
3
8
|
* git diff --submodule=diff adds various status lines that the diff parser doesn't understand:
|
package/dist/diff-utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff-utils.d.ts","sourceRoot":"","sources":["../src/diff-utils.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"diff-utils.d.ts","sourceRoot":"","sources":["../src/diff-utils.ts"],"names":[],"mappings":"AAOA;;;GAGG;AACH,wBAAgB,aAAa,IAAI,IAAI,CASpC;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAchE;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAA;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG;IAC/C,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CACpC,CA6FA;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,CAAC,EAAE,GAChC,CAAC,CAAC,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,CAiBzE;AAED,eAAO,MAAM,aAAa,UASzB,CAAC;AAEF,MAAM,WAAW,UAAU;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,4DAA4D;AAC5D,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,QAAQ,GAAG,mBAAmB,CAAC,GAC/D,MAAM,EAAE,CAQV;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAsBhF;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,CAAC,SAAS,UAAU,EAC9D,KAAK,EAAE,CAAC,EAAE,EACV,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,QAAQ,GAAG,mBAAmB,CAAC,GAC/D,CAAC,EAAE,CAKL;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAyDlE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAiBjD;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,cAAc,EAAE,MAAM,EAAE,EACxB,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,GAC1C,MAAM,CAMR;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAW/C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,CAYT;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,GAAG,SAAS,CAQrB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,GAAG;IAC/D,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,CAYA;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,cAAc,GAAE,MAAY,GAC3B,OAAO,GAAG,SAAS,CAQrB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,UAAU,EAC/C,KAAK,EAAE,CAAC,EAAE,EACV,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAC/B,CAAC,CAAC,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,CAyD7B;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAqGnE"}
|