lacuna-cli 0.1.8 → 0.1.9
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 +37 -8
- package/dist/agent/context.d.ts +1 -0
- package/dist/agent/context.d.ts.map +1 -1
- package/dist/agent/context.js +141 -6
- package/dist/agent/context.js.map +1 -1
- package/dist/agent/fix-loop.d.ts.map +1 -1
- package/dist/agent/fix-loop.js +135 -20
- package/dist/agent/fix-loop.js.map +1 -1
- package/dist/agent/generator.d.ts +2 -0
- package/dist/agent/generator.d.ts.map +1 -1
- package/dist/agent/generator.js +63 -22
- package/dist/agent/generator.js.map +1 -1
- package/dist/agent/loop.d.ts +1 -1
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +81 -4
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/prompts/index.d.ts +2 -0
- package/dist/agent/prompts/index.d.ts.map +1 -1
- package/dist/agent/prompts/index.js +145 -31
- package/dist/agent/prompts/index.js.map +1 -1
- package/dist/agent/prompts/react-native.d.ts.map +1 -1
- package/dist/agent/prompts/react-native.js +15 -4
- package/dist/agent/prompts/react-native.js.map +1 -1
- package/dist/agent/prompts/react.js +1 -1
- package/dist/agent/prompts/runners/vitest.js +1 -1
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +4 -0
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/fix.d.ts.map +1 -1
- package/dist/commands/fix.js +4 -0
- package/dist/commands/fix.js.map +1 -1
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +4 -0
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +4 -0
- package/dist/commands/run.js.map +1 -1
- package/dist/lib/config.d.ts +6 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +9 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/extract-error.d.ts.map +1 -1
- package/dist/lib/extract-error.js +8 -2
- package/dist/lib/extract-error.js.map +1 -1
- package/dist/lib/reporter.d.ts.map +1 -1
- package/dist/lib/reporter.js +11 -1
- package/dist/lib/reporter.js.map +1 -1
- package/dist/lib/validate.d.ts +25 -0
- package/dist/lib/validate.d.ts.map +1 -1
- package/dist/lib/validate.js +561 -0
- package/dist/lib/validate.js.map +1 -1
- package/package.json +9 -4
- package/oclif.manifest.json +0 -309
package/dist/lib/reporter.js
CHANGED
|
@@ -10,7 +10,17 @@ export function reportTerminal(input) {
|
|
|
10
10
|
console.log(` Lines: ${lineColor(r.coveragePct.toFixed(1) + '%')} Functions: ${lineColor(r.functionCoveragePct.toFixed(1) + '%')}`);
|
|
11
11
|
console.log(` Threshold: ${threshold}% Status: ${status}\n`);
|
|
12
12
|
if (r.gaps.length === 0) {
|
|
13
|
-
|
|
13
|
+
if (r.passed) {
|
|
14
|
+
console.log(chalk.green(' All files meet the threshold.'));
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
// Overall coverage is below threshold even though no per-file gaps were found.
|
|
18
|
+
// This happens when many source files are never imported by any test — they
|
|
19
|
+
// don't appear in the LCOV report individually but pull down the overall rate.
|
|
20
|
+
console.log(chalk.yellow(` Overall coverage is ${r.coveragePct.toFixed(1)}% — below the ${r.threshold}% threshold.`));
|
|
21
|
+
console.log(chalk.dim(` No per-file gaps were found in the coverage report.`));
|
|
22
|
+
console.log(chalk.dim(` Try running ${chalk.cyan('lacuna generate')} to find and cover untested source files.\n`));
|
|
23
|
+
}
|
|
14
24
|
return;
|
|
15
25
|
}
|
|
16
26
|
const belowThreshold = r.gaps.length - r.untouchedCount;
|
package/dist/lib/reporter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../src/lib/reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAwBzB,gFAAgF;AAEhF,MAAM,UAAU,cAAc,CAAC,KAAkB;IAC/C,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAA;IAE3B,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAC9C,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAA;QACvB,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAA;QACpD,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QAEjE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAA;QAC7C,OAAO,CAAC,GAAG,CACT,gBAAgB,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,iBAAiB,SAAS,CAAC,CAAC,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,EAAE,CAC9H,CAAA;QACD,OAAO,CAAC,GAAG,CAAC,gBAAgB,SAAS,eAAe,MAAM,IAAI,CAAC,CAAA;QAE/D,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../src/lib/reporter.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/validate.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare function hasTestFunctions(code: string): boolean;
|
|
2
|
+
export declare function hasPlaceholderBodies(code: string): boolean;
|
|
2
3
|
export declare function enrichNoTestsError(output: string): string;
|
|
3
4
|
export declare function isZeroTestsOutput(raw: string): boolean;
|
|
4
5
|
export declare function parsePassCount(output: string): number;
|
|
@@ -14,4 +15,28 @@ export declare function sanitizeMocksContent(raw: string): {
|
|
|
14
15
|
export declare function deduplicateViMocks(code: string): string;
|
|
15
16
|
export declare function buildStructureBrokenMessage(initialError: string, currentError: string): string;
|
|
16
17
|
export declare function buildRegressionMessage(initialError: string, currentError: string, baselinePass: number, currentPass: number): string;
|
|
18
|
+
export type PatchOpType = 'REPLACE_TEST' | 'DELETE_TEST' | 'ADD_AFTER_DESCRIBE' | 'ADD_IMPORT' | 'ADD_AFTER_IMPORTS' | 'REPLACE';
|
|
19
|
+
export interface PatchOperation {
|
|
20
|
+
type: PatchOpType;
|
|
21
|
+
anchor: string;
|
|
22
|
+
content: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function parsePatch(patchOutput: string): PatchOperation[];
|
|
25
|
+
export declare function applyPatch(existingCode: string, ops: PatchOperation[]): string | null;
|
|
26
|
+
export declare function tryApplyPatch(existingCode: string, patchOutput: string): string | null;
|
|
27
|
+
export type MockPatchOpType = 'REPLACE' | 'APPEND_EXPORT' | 'ADD_TO_BEFOREEACH';
|
|
28
|
+
export interface MockPatchOperation {
|
|
29
|
+
type: MockPatchOpType;
|
|
30
|
+
oldText: string;
|
|
31
|
+
newText: string;
|
|
32
|
+
}
|
|
33
|
+
export declare function parseMocksPatch(patchOutput: string): MockPatchOperation[];
|
|
34
|
+
export declare function applyMocksPatch(existing: string, ops: MockPatchOperation[]): {
|
|
35
|
+
result: string;
|
|
36
|
+
failedOps: MockPatchOperation[];
|
|
37
|
+
};
|
|
38
|
+
export declare function tryApplyMocksPatch(existing: string, patchOutput: string): {
|
|
39
|
+
result: string;
|
|
40
|
+
failedOps: MockPatchOperation[];
|
|
41
|
+
} | null;
|
|
17
42
|
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/lib/validate.ts"],"names":[],"mappings":"AAYA,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGtD;AAID,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAgBzD;AAID,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEtD;AAKD,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOrD;AAOD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAWzF;AAOD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAqC5E;AA0CD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAqDrF;AAOD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA2HvD;AAQD,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAiB9F;AAKD,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,GAClB,MAAM,CAcR"}
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/lib/validate.ts"],"names":[],"mappings":"AAYA,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGtD;AAID,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAI1D;AAID,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAgBzD;AAID,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEtD;AAKD,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOrD;AAOD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAWzF;AAOD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAqC5E;AA0CD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAqDrF;AAOD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA2HvD;AAQD,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAiB9F;AAKD,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,GAClB,MAAM,CAcR;AAMD,MAAM,MAAM,WAAW,GAAG,cAAc,GAAG,aAAa,GAAG,oBAAoB,GAAG,YAAY,GAAG,mBAAmB,GAAG,SAAS,CAAA;AAEhI,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,WAAW,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IAEd,OAAO,EAAE,MAAM,CAAA;CAChB;AAeD,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,EAAE,CAoDhE;AA6FD,wBAAgB,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,cAAc,EAAE,GAAG,MAAM,GAAG,IAAI,CAoLrF;AAgDD,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAItF;AAyBD,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,eAAe,GAAG,mBAAmB,CAAA;AAE/E,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,eAAe,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAmDzE;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,kBAAkB,EAAE,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,kBAAkB,EAAE,CAAA;CAAE,CA+ChI;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,kBAAkB,EAAE,CAAA;CAAE,GAAG,IAAI,CAIpI"}
|
package/dist/lib/validate.js
CHANGED
|
@@ -13,6 +13,13 @@ export function hasTestFunctions(code) {
|
|
|
13
13
|
const stripped = stripNonCode(code);
|
|
14
14
|
return /\b(?:it|test)\s*(?:\.(?:each|concurrent|skip|only))?\s*\(/.test(stripped);
|
|
15
15
|
}
|
|
16
|
+
// Returns true when the code contains placeholder test bodies — e.g. `{ // body }`.
|
|
17
|
+
// A placeholder passes vitest (no assertions = no failures) but produces zero value.
|
|
18
|
+
export function hasPlaceholderBodies(code) {
|
|
19
|
+
// Match an opening brace, optional whitespace/newline, a // comment that looks like
|
|
20
|
+
// a placeholder, then closing brace. Catches: { // body }, { // TODO }, { // implement }.
|
|
21
|
+
return /\{\s*\/\/\s*(body|todo|implement(?:ation)?|placeholder|stub|fill\s*in|your\s*code)\s*\}/i.test(code);
|
|
22
|
+
}
|
|
16
23
|
// If the runner output indicates "no tests found", replace it with a
|
|
17
24
|
// clear instruction so the AI knows exactly what went wrong.
|
|
18
25
|
export function enrichNoTestsError(output) {
|
|
@@ -394,4 +401,558 @@ export function buildRegressionMessage(initialError, currentError, baselinePass,
|
|
|
394
401
|
`Do NOT modify tests that were already passing.\n` +
|
|
395
402
|
`ONLY fix the test that was originally failing.`);
|
|
396
403
|
}
|
|
404
|
+
// Parses the model's patch output into a list of PatchOperation objects.
|
|
405
|
+
//
|
|
406
|
+
// Most operations have the form:
|
|
407
|
+
// // @@@ TYPE: "anchor"
|
|
408
|
+
// <content lines>
|
|
409
|
+
// // @@@ END
|
|
410
|
+
//
|
|
411
|
+
// REPLACE is different — it uses a WITH delimiter instead of an inline anchor:
|
|
412
|
+
// // @@@ REPLACE:
|
|
413
|
+
// <exact existing text to find, verbatim>
|
|
414
|
+
// // @@@ WITH:
|
|
415
|
+
// <replacement text>
|
|
416
|
+
// // @@@ END
|
|
417
|
+
export function parsePatch(patchOutput) {
|
|
418
|
+
const ops = [];
|
|
419
|
+
const lines = patchOutput.split('\n');
|
|
420
|
+
const headerRe = /^\/\/ @@@ (REPLACE_TEST|DELETE_TEST|ADD_AFTER_DESCRIBE|ADD_IMPORT|ADD_AFTER_IMPORTS|REPLACE):\s*(?:"(.*)")?/;
|
|
421
|
+
const withRe = /^\/\/ @@@ WITH:\s*$/;
|
|
422
|
+
const endRe = /^\/\/ @@@ END\s*$/;
|
|
423
|
+
let i = 0;
|
|
424
|
+
while (i < lines.length) {
|
|
425
|
+
const m = headerRe.exec(lines[i]);
|
|
426
|
+
if (!m) {
|
|
427
|
+
i++;
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
const type = m[1];
|
|
431
|
+
i++;
|
|
432
|
+
if (type === 'REPLACE') {
|
|
433
|
+
// Read old text until // @@@ WITH:
|
|
434
|
+
const oldLines = [];
|
|
435
|
+
while (i < lines.length && !withRe.test(lines[i]) && !endRe.test(lines[i])) {
|
|
436
|
+
oldLines.push(lines[i]);
|
|
437
|
+
i++;
|
|
438
|
+
}
|
|
439
|
+
if (!withRe.test(lines[i] ?? '')) {
|
|
440
|
+
i++;
|
|
441
|
+
continue;
|
|
442
|
+
} // malformed — skip
|
|
443
|
+
i++; // skip // @@@ WITH:
|
|
444
|
+
const newLines = [];
|
|
445
|
+
while (i < lines.length && !endRe.test(lines[i])) {
|
|
446
|
+
newLines.push(lines[i]);
|
|
447
|
+
i++;
|
|
448
|
+
}
|
|
449
|
+
i++; // skip // @@@ END
|
|
450
|
+
let anchor = oldLines.join('\n');
|
|
451
|
+
let content = newLines.join('\n');
|
|
452
|
+
if (anchor.startsWith('\n'))
|
|
453
|
+
anchor = anchor.slice(1);
|
|
454
|
+
if (anchor.endsWith('\n'))
|
|
455
|
+
anchor = anchor.slice(0, -1);
|
|
456
|
+
if (content.startsWith('\n'))
|
|
457
|
+
content = content.slice(1);
|
|
458
|
+
if (content.endsWith('\n'))
|
|
459
|
+
content = content.slice(0, -1);
|
|
460
|
+
ops.push({ type, anchor, content });
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
const anchor = m[2] ?? ''; // ADD_IMPORT / ADD_AFTER_IMPORTS have no anchor
|
|
464
|
+
const contentLines = [];
|
|
465
|
+
while (i < lines.length && !endRe.test(lines[i])) {
|
|
466
|
+
contentLines.push(lines[i]);
|
|
467
|
+
i++;
|
|
468
|
+
}
|
|
469
|
+
i++; // skip // @@@ END
|
|
470
|
+
let content = contentLines.join('\n');
|
|
471
|
+
if (content.startsWith('\n'))
|
|
472
|
+
content = content.slice(1);
|
|
473
|
+
if (content.endsWith('\n'))
|
|
474
|
+
content = content.slice(0, -1);
|
|
475
|
+
ops.push({ type, anchor, content });
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return ops;
|
|
479
|
+
}
|
|
480
|
+
// Finds the end of an it()/test()/describe() call starting at `startIdx` in `code`.
|
|
481
|
+
// `startIdx` must point to the opening `(` of the call.
|
|
482
|
+
// Returns the index just past the closing `)` (and optional `;`), or -1 on failure.
|
|
483
|
+
//
|
|
484
|
+
// Strategy: skip the string argument(s), find the function body `{`, track brace depth
|
|
485
|
+
// until it returns to 0, then consume the closing `)` and optional `;`.
|
|
486
|
+
// We do a simplified scan that handles string literals and template literals to avoid
|
|
487
|
+
// false brace counts inside quoted text.
|
|
488
|
+
function findCallEnd(code, startIdx) {
|
|
489
|
+
let i = startIdx; // points at the `(` of the call
|
|
490
|
+
let parenDepth = 0;
|
|
491
|
+
let braceDepth = 0;
|
|
492
|
+
let foundBrace = false;
|
|
493
|
+
while (i < code.length) {
|
|
494
|
+
const ch = code[i];
|
|
495
|
+
// Skip string literals to avoid false brace/paren counts inside strings
|
|
496
|
+
if (ch === '"' || ch === "'") {
|
|
497
|
+
const q = ch;
|
|
498
|
+
i++;
|
|
499
|
+
while (i < code.length) {
|
|
500
|
+
if (code[i] === '\\') {
|
|
501
|
+
i += 2;
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
if (code[i] === q) {
|
|
505
|
+
i++;
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
i++;
|
|
509
|
+
}
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
if (ch === '`') {
|
|
513
|
+
i++;
|
|
514
|
+
while (i < code.length) {
|
|
515
|
+
if (code[i] === '\\') {
|
|
516
|
+
i += 2;
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
if (code[i] === '`') {
|
|
520
|
+
i++;
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
// Skip ${...} expressions inside template literals (simplified: track braces)
|
|
524
|
+
if (code[i] === '$' && code[i + 1] === '{') {
|
|
525
|
+
i += 2;
|
|
526
|
+
let tDepth = 1;
|
|
527
|
+
while (i < code.length && tDepth > 0) {
|
|
528
|
+
if (code[i] === '{')
|
|
529
|
+
tDepth++;
|
|
530
|
+
else if (code[i] === '}')
|
|
531
|
+
tDepth--;
|
|
532
|
+
i++;
|
|
533
|
+
}
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
536
|
+
i++;
|
|
537
|
+
}
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
if (ch === '{') {
|
|
541
|
+
foundBrace = true;
|
|
542
|
+
braceDepth++;
|
|
543
|
+
i++;
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
if (ch === '}') {
|
|
547
|
+
if (foundBrace) {
|
|
548
|
+
braceDepth--;
|
|
549
|
+
if (braceDepth === 0) {
|
|
550
|
+
// We've closed the function body. Now consume the closing `)` and optional `;`
|
|
551
|
+
i++; // move past `}`
|
|
552
|
+
// skip whitespace/newlines
|
|
553
|
+
while (i < code.length && (code[i] === ' ' || code[i] === '\t' || code[i] === '\n' || code[i] === '\r'))
|
|
554
|
+
i++;
|
|
555
|
+
if (i < code.length && code[i] === ')') {
|
|
556
|
+
i++; // consume `)`
|
|
557
|
+
if (i < code.length && code[i] === ';')
|
|
558
|
+
i++; // consume optional `;`
|
|
559
|
+
}
|
|
560
|
+
return i;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
i++;
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
566
|
+
if (!foundBrace) {
|
|
567
|
+
// Before the opening brace we still count parens to handle nested calls in args
|
|
568
|
+
if (ch === '(')
|
|
569
|
+
parenDepth++;
|
|
570
|
+
else if (ch === ')') {
|
|
571
|
+
parenDepth--;
|
|
572
|
+
// If we hit -1 depth without ever finding a brace this is a call with no body (unlikely for tests)
|
|
573
|
+
if (parenDepth < 0)
|
|
574
|
+
return -1;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
i++;
|
|
578
|
+
}
|
|
579
|
+
return -1;
|
|
580
|
+
}
|
|
581
|
+
// Applies a list of PatchOperation objects to `existingCode` in order.
|
|
582
|
+
// Returns the modified string, or null if any anchor cannot be located.
|
|
583
|
+
export function applyPatch(existingCode, ops) {
|
|
584
|
+
let code = existingCode;
|
|
585
|
+
for (const op of ops) {
|
|
586
|
+
if (op.type === 'REPLACE') {
|
|
587
|
+
// General text replacement — same mechanism as the Edit tool.
|
|
588
|
+
// anchor = exact old text, content = replacement. First occurrence only.
|
|
589
|
+
if (!code.includes(op.anchor))
|
|
590
|
+
return null;
|
|
591
|
+
const idx = code.indexOf(op.anchor);
|
|
592
|
+
code = code.slice(0, idx) + op.content + code.slice(idx + op.anchor.length);
|
|
593
|
+
}
|
|
594
|
+
else if (op.type === 'REPLACE_TEST' || op.type === 'DELETE_TEST') {
|
|
595
|
+
const anchor = op.anchor;
|
|
596
|
+
// Try all four quote/keyword combos
|
|
597
|
+
const candidates = [
|
|
598
|
+
`it("${anchor}"`,
|
|
599
|
+
`it('${anchor}'`,
|
|
600
|
+
`test("${anchor}"`,
|
|
601
|
+
`test('${anchor}'`,
|
|
602
|
+
];
|
|
603
|
+
let foundIdx = -1;
|
|
604
|
+
for (const c of candidates) {
|
|
605
|
+
const idx = code.indexOf(c);
|
|
606
|
+
if (idx !== -1) {
|
|
607
|
+
foundIdx = idx;
|
|
608
|
+
break;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
if (foundIdx === -1)
|
|
612
|
+
return null;
|
|
613
|
+
// Find the opening `(` of the call — it's right after `it` or `test`
|
|
614
|
+
const parenIdx = code.indexOf('(', foundIdx);
|
|
615
|
+
if (parenIdx === -1)
|
|
616
|
+
return null;
|
|
617
|
+
const callEnd = findCallEnd(code, parenIdx);
|
|
618
|
+
if (callEnd === -1)
|
|
619
|
+
return null;
|
|
620
|
+
if (op.type === 'REPLACE_TEST') {
|
|
621
|
+
code = code.slice(0, foundIdx) + op.content + code.slice(callEnd);
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
// DELETE_TEST: also remove an immediately preceding blank line
|
|
625
|
+
let removeStart = foundIdx;
|
|
626
|
+
if (removeStart > 0 && code[removeStart - 1] === '\n') {
|
|
627
|
+
// Check if the line before is blank
|
|
628
|
+
const prevNewline = code.lastIndexOf('\n', removeStart - 2);
|
|
629
|
+
const prevLine = code.slice(prevNewline + 1, removeStart - 1);
|
|
630
|
+
if (prevLine.trim() === '')
|
|
631
|
+
removeStart = prevNewline + 1;
|
|
632
|
+
}
|
|
633
|
+
code = code.slice(0, removeStart) + code.slice(callEnd);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
else if (op.type === 'ADD_AFTER_DESCRIBE') {
|
|
637
|
+
const anchor = op.anchor;
|
|
638
|
+
const candidates = [
|
|
639
|
+
`describe("${anchor}"`,
|
|
640
|
+
`describe('${anchor}'`,
|
|
641
|
+
];
|
|
642
|
+
let foundIdx = -1;
|
|
643
|
+
for (const c of candidates) {
|
|
644
|
+
const idx = code.indexOf(c);
|
|
645
|
+
if (idx !== -1) {
|
|
646
|
+
foundIdx = idx;
|
|
647
|
+
break;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
if (foundIdx === -1)
|
|
651
|
+
return null;
|
|
652
|
+
// Find the opening `(` of the describe call
|
|
653
|
+
const parenIdx = code.indexOf('(', foundIdx);
|
|
654
|
+
if (parenIdx === -1)
|
|
655
|
+
return null;
|
|
656
|
+
// Walk from parenIdx to find the LAST closing `})` of the describe block.
|
|
657
|
+
// We track brace depth from the first `{` we encounter inside the describe args.
|
|
658
|
+
let i = parenIdx;
|
|
659
|
+
let braceDepth = 0;
|
|
660
|
+
let lastClosePos = -1; // position of the `}` that closes the describe body
|
|
661
|
+
// Skip string literal for the describe name argument
|
|
662
|
+
// The describe call looks like: describe("name", () => { ... })
|
|
663
|
+
// We need to find the function body brace
|
|
664
|
+
let foundBrace = false;
|
|
665
|
+
while (i < code.length) {
|
|
666
|
+
const ch = code[i];
|
|
667
|
+
// Skip string literals
|
|
668
|
+
if (ch === '"' || ch === "'") {
|
|
669
|
+
const q = ch;
|
|
670
|
+
i++;
|
|
671
|
+
while (i < code.length) {
|
|
672
|
+
if (code[i] === '\\') {
|
|
673
|
+
i += 2;
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
if (code[i] === q) {
|
|
677
|
+
i++;
|
|
678
|
+
break;
|
|
679
|
+
}
|
|
680
|
+
i++;
|
|
681
|
+
}
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
if (ch === '`') {
|
|
685
|
+
i++;
|
|
686
|
+
while (i < code.length) {
|
|
687
|
+
if (code[i] === '\\') {
|
|
688
|
+
i += 2;
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
if (code[i] === '`') {
|
|
692
|
+
i++;
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
if (code[i] === '$' && code[i + 1] === '{') {
|
|
696
|
+
i += 2;
|
|
697
|
+
let tDepth = 1;
|
|
698
|
+
while (i < code.length && tDepth > 0) {
|
|
699
|
+
if (code[i] === '{')
|
|
700
|
+
tDepth++;
|
|
701
|
+
else if (code[i] === '}')
|
|
702
|
+
tDepth--;
|
|
703
|
+
i++;
|
|
704
|
+
}
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
i++;
|
|
708
|
+
}
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
if (ch === '{') {
|
|
712
|
+
foundBrace = true;
|
|
713
|
+
braceDepth++;
|
|
714
|
+
i++;
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
if (ch === '}') {
|
|
718
|
+
if (foundBrace) {
|
|
719
|
+
braceDepth--;
|
|
720
|
+
if (braceDepth === 0) {
|
|
721
|
+
lastClosePos = i;
|
|
722
|
+
break;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
i++;
|
|
726
|
+
continue;
|
|
727
|
+
}
|
|
728
|
+
i++;
|
|
729
|
+
}
|
|
730
|
+
if (lastClosePos === -1)
|
|
731
|
+
return null;
|
|
732
|
+
// Insert content immediately before the closing `}`
|
|
733
|
+
// Add a newline after content so the `}` is on its own line
|
|
734
|
+
const insertion = '\n' + op.content + '\n';
|
|
735
|
+
code = code.slice(0, lastClosePos) + insertion + code.slice(lastClosePos);
|
|
736
|
+
}
|
|
737
|
+
else if (op.type === 'ADD_IMPORT') {
|
|
738
|
+
// Find the last `import ` line in the file
|
|
739
|
+
const lines = code.split('\n');
|
|
740
|
+
let lastImportLineIdx = -1;
|
|
741
|
+
for (let idx = 0; idx < lines.length; idx++) {
|
|
742
|
+
if (/^\s*import\s/.test(lines[idx]))
|
|
743
|
+
lastImportLineIdx = idx;
|
|
744
|
+
}
|
|
745
|
+
const importLines = op.content.split('\n');
|
|
746
|
+
if (lastImportLineIdx === -1) {
|
|
747
|
+
lines.unshift(...importLines);
|
|
748
|
+
}
|
|
749
|
+
else {
|
|
750
|
+
lines.splice(lastImportLineIdx + 1, 0, ...importLines);
|
|
751
|
+
}
|
|
752
|
+
code = lines.join('\n');
|
|
753
|
+
}
|
|
754
|
+
else if (op.type === 'ADD_AFTER_IMPORTS') {
|
|
755
|
+
// Like ADD_IMPORT but inserts a blank line before the block — for vi.mock() calls
|
|
756
|
+
// and other module-level statements that follow imports
|
|
757
|
+
const lines = code.split('\n');
|
|
758
|
+
let lastImportLineIdx = -1;
|
|
759
|
+
for (let idx = 0; idx < lines.length; idx++) {
|
|
760
|
+
if (/^\s*import\s/.test(lines[idx]))
|
|
761
|
+
lastImportLineIdx = idx;
|
|
762
|
+
}
|
|
763
|
+
const contentLines = ['', ...op.content.split('\n')];
|
|
764
|
+
if (lastImportLineIdx === -1) {
|
|
765
|
+
lines.unshift(...contentLines);
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
lines.splice(lastImportLineIdx + 1, 0, ...contentLines);
|
|
769
|
+
}
|
|
770
|
+
code = lines.join('\n');
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
// Collapse gaps left by DELETE_TEST: runs of 3+ newlines (2+ consecutive blank lines)
|
|
774
|
+
// down to exactly 2 newlines (1 blank line). Safe — never affects content.
|
|
775
|
+
code = code.replace(/\n{3,}/g, '\n\n');
|
|
776
|
+
// Remove describe blocks that became empty shells (no it/test calls anywhere inside).
|
|
777
|
+
// Repeat until stable — outer empties are caught after inner empties are removed.
|
|
778
|
+
code = removeEmptyDescribeBlocks(code);
|
|
779
|
+
return code;
|
|
780
|
+
}
|
|
781
|
+
// Removes describe() blocks whose body contains no it() or test() calls at any depth.
|
|
782
|
+
// Iterates until stable to handle nested empty blocks (inner removed first, then outer).
|
|
783
|
+
function removeEmptyDescribeBlocks(code) {
|
|
784
|
+
let prev = '';
|
|
785
|
+
while (code !== prev) {
|
|
786
|
+
prev = code;
|
|
787
|
+
const lines = code.split('\n');
|
|
788
|
+
const out = [];
|
|
789
|
+
let i = 0;
|
|
790
|
+
while (i < lines.length) {
|
|
791
|
+
const line = lines[i];
|
|
792
|
+
if (/^\s*describe\s*\(/.test(line) && line.trimEnd().endsWith('{')) {
|
|
793
|
+
// Collect the full block by tracking brace depth.
|
|
794
|
+
// Note: braces in string literals may cause a false depth count, but that
|
|
795
|
+
// only risks keeping a block we should remove — never deleting a live one,
|
|
796
|
+
// because we require it()/test() to be absent in ALL collected lines.
|
|
797
|
+
let depth = 1;
|
|
798
|
+
let j = i + 1;
|
|
799
|
+
const bodyLines = [];
|
|
800
|
+
while (j < lines.length && depth > 0) {
|
|
801
|
+
const l = lines[j];
|
|
802
|
+
for (const ch of l) {
|
|
803
|
+
if (ch === '{')
|
|
804
|
+
depth++;
|
|
805
|
+
else if (ch === '}')
|
|
806
|
+
depth--;
|
|
807
|
+
}
|
|
808
|
+
if (depth > 0)
|
|
809
|
+
bodyLines.push(l);
|
|
810
|
+
j++;
|
|
811
|
+
}
|
|
812
|
+
const hasTests = bodyLines.some(l => /\b(?:it|test)\s*\(/.test(l));
|
|
813
|
+
if (!hasTests) {
|
|
814
|
+
// Skip the whole block (opening line through closing line)
|
|
815
|
+
i = j;
|
|
816
|
+
// Consume a trailing blank line so deletions don't stack up
|
|
817
|
+
if (i < lines.length && !lines[i].trim())
|
|
818
|
+
i++;
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
out.push(line);
|
|
823
|
+
i++;
|
|
824
|
+
}
|
|
825
|
+
code = out.join('\n');
|
|
826
|
+
}
|
|
827
|
+
return code;
|
|
828
|
+
}
|
|
829
|
+
// Convenience wrapper: parses then applies. Returns null if no ops parsed or apply fails.
|
|
830
|
+
export function tryApplyPatch(existingCode, patchOutput) {
|
|
831
|
+
const ops = parsePatch(patchOutput);
|
|
832
|
+
if (ops.length === 0)
|
|
833
|
+
return null;
|
|
834
|
+
return applyPatch(existingCode, ops);
|
|
835
|
+
}
|
|
836
|
+
export function parseMocksPatch(patchOutput) {
|
|
837
|
+
const ops = [];
|
|
838
|
+
const lines = patchOutput.split('\n');
|
|
839
|
+
const headerRe = /^\/\/ @@@ (REPLACE|APPEND_EXPORT|ADD_TO_BEFOREEACH):\s*$/;
|
|
840
|
+
const withRe = /^\/\/ @@@ WITH:\s*$/;
|
|
841
|
+
const endRe = /^\/\/ @@@ END\s*$/;
|
|
842
|
+
let i = 0;
|
|
843
|
+
while (i < lines.length) {
|
|
844
|
+
const m = headerRe.exec(lines[i]);
|
|
845
|
+
if (!m) {
|
|
846
|
+
i++;
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
const type = m[1];
|
|
850
|
+
i++;
|
|
851
|
+
if (type === 'REPLACE') {
|
|
852
|
+
const oldLines = [];
|
|
853
|
+
while (i < lines.length && !withRe.test(lines[i]) && !endRe.test(lines[i])) {
|
|
854
|
+
oldLines.push(lines[i]);
|
|
855
|
+
i++;
|
|
856
|
+
}
|
|
857
|
+
if (!withRe.test(lines[i] ?? '')) {
|
|
858
|
+
i++;
|
|
859
|
+
continue;
|
|
860
|
+
}
|
|
861
|
+
i++; // skip // @@@ WITH:
|
|
862
|
+
const newLines = [];
|
|
863
|
+
while (i < lines.length && !endRe.test(lines[i])) {
|
|
864
|
+
newLines.push(lines[i]);
|
|
865
|
+
i++;
|
|
866
|
+
}
|
|
867
|
+
i++; // skip // @@@ END
|
|
868
|
+
let oldText = oldLines.join('\n');
|
|
869
|
+
let newText = newLines.join('\n');
|
|
870
|
+
if (oldText.startsWith('\n'))
|
|
871
|
+
oldText = oldText.slice(1);
|
|
872
|
+
if (oldText.endsWith('\n'))
|
|
873
|
+
oldText = oldText.slice(0, -1);
|
|
874
|
+
if (newText.startsWith('\n'))
|
|
875
|
+
newText = newText.slice(1);
|
|
876
|
+
if (newText.endsWith('\n'))
|
|
877
|
+
newText = newText.slice(0, -1);
|
|
878
|
+
ops.push({ type, oldText, newText });
|
|
879
|
+
}
|
|
880
|
+
else {
|
|
881
|
+
// APPEND_EXPORT and ADD_TO_BEFOREEACH — just content, no WITH: block
|
|
882
|
+
const contentLines = [];
|
|
883
|
+
while (i < lines.length && !endRe.test(lines[i])) {
|
|
884
|
+
contentLines.push(lines[i]);
|
|
885
|
+
i++;
|
|
886
|
+
}
|
|
887
|
+
i++; // skip // @@@ END
|
|
888
|
+
let newText = contentLines.join('\n');
|
|
889
|
+
if (newText.startsWith('\n'))
|
|
890
|
+
newText = newText.slice(1);
|
|
891
|
+
if (newText.endsWith('\n'))
|
|
892
|
+
newText = newText.slice(0, -1);
|
|
893
|
+
ops.push({ type, oldText: '', newText });
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
return ops;
|
|
897
|
+
}
|
|
898
|
+
export function applyMocksPatch(existing, ops) {
|
|
899
|
+
let code = existing;
|
|
900
|
+
const failedOps = [];
|
|
901
|
+
for (const op of ops) {
|
|
902
|
+
if (op.type === 'REPLACE') {
|
|
903
|
+
if (!code.includes(op.oldText)) {
|
|
904
|
+
failedOps.push(op);
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
// Replace only the first occurrence — same as Edit tool
|
|
908
|
+
code = code.slice(0, code.indexOf(op.oldText)) + op.newText + code.slice(code.indexOf(op.oldText) + op.oldText.length);
|
|
909
|
+
}
|
|
910
|
+
else if (op.type === 'APPEND_EXPORT') {
|
|
911
|
+
// Insert before the last beforeEach block, or at end of file if none
|
|
912
|
+
const beforeEachIdx = code.lastIndexOf('\nbeforeEach(');
|
|
913
|
+
if (beforeEachIdx !== -1) {
|
|
914
|
+
code = code.slice(0, beforeEachIdx) + '\n\n' + op.newText.trim() + code.slice(beforeEachIdx);
|
|
915
|
+
}
|
|
916
|
+
else {
|
|
917
|
+
code = code.trimEnd() + '\n\n' + op.newText.trim();
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
else if (op.type === 'ADD_TO_BEFOREEACH') {
|
|
921
|
+
// Find the last beforeEach and insert before its closing brace
|
|
922
|
+
const beIdx = code.lastIndexOf('\nbeforeEach(');
|
|
923
|
+
if (beIdx === -1) {
|
|
924
|
+
failedOps.push(op);
|
|
925
|
+
continue;
|
|
926
|
+
}
|
|
927
|
+
// Find the closing }) of that beforeEach by tracking brace depth
|
|
928
|
+
let depth = 0;
|
|
929
|
+
let closeIdx = -1;
|
|
930
|
+
for (let i = beIdx + 1; i < code.length; i++) {
|
|
931
|
+
if (code[i] === '{')
|
|
932
|
+
depth++;
|
|
933
|
+
else if (code[i] === '}') {
|
|
934
|
+
depth--;
|
|
935
|
+
if (depth === 0) {
|
|
936
|
+
closeIdx = i;
|
|
937
|
+
break;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
if (closeIdx === -1) {
|
|
942
|
+
failedOps.push(op);
|
|
943
|
+
continue;
|
|
944
|
+
}
|
|
945
|
+
const indent = ' ';
|
|
946
|
+
const lines = op.newText.trim().split('\n').map(l => indent + l).join('\n');
|
|
947
|
+
code = code.slice(0, closeIdx) + '\n' + lines + '\n' + code.slice(closeIdx);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
return { result: code, failedOps };
|
|
951
|
+
}
|
|
952
|
+
export function tryApplyMocksPatch(existing, patchOutput) {
|
|
953
|
+
const ops = parseMocksPatch(patchOutput);
|
|
954
|
+
if (ops.length === 0)
|
|
955
|
+
return null;
|
|
956
|
+
return applyMocksPatch(existing, ops);
|
|
957
|
+
}
|
|
397
958
|
//# sourceMappingURL=validate.js.map
|