deflake 1.2.40 → 1.2.42

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.
Files changed (2) hide show
  1. package/cli.js +45 -6
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -101,6 +101,18 @@ async function main() {
101
101
  break;
102
102
  }
103
103
 
104
+ // Detect compilation errors: exit code != 0 but no test artifacts
105
+ // This means the code has syntax errors and tests didn't even run
106
+ const postArtifacts = detectFailures();
107
+ if (postArtifacts.length === 0 && rerun.code !== 0) {
108
+ console.log(`${C.RED}⚠️ Compilation error detected (tests did not run). Rolling back all changes...${C.RESET}`);
109
+ for (const [filePath, originalContent] of backups) {
110
+ fs.writeFileSync(filePath, originalContent);
111
+ console.log(` ${C.YELLOW}↩️ Restored: ${path.basename(filePath)}${C.RESET}`);
112
+ }
113
+ break;
114
+ }
115
+
104
116
  if (attempt === MAX_ATTEMPTS) {
105
117
  console.log(`\n${C.YELLOW}⚠️ Max attempts (${MAX_ATTEMPTS}) reached. Some failures may persist.${C.RESET}`);
106
118
  }
@@ -341,16 +353,13 @@ async function applyFix(res, loc, backups = new Map()) {
341
353
  } else {
342
354
  // REPLACE action
343
355
  // SMART: Handle multi-line expression replacement
344
- // If the original line starts a multi-line expression (has unclosed parens)
345
- // and the replacement is a complete statement, remove continuation lines
346
356
  const origOpen = (originalLine.match(/\(/g) || []).length;
347
357
  const origClose = (originalLine.match(/\)/g) || []).length;
348
358
  const replOpen = (cleanLine.match(/\(/g) || []).length;
349
359
  const replClose = (cleanLine.match(/\)/g) || []).length;
350
360
 
361
+ // Case A: Target line is the OPENER of a multi-line expression
351
362
  if (origOpen > origClose && replOpen <= replClose) {
352
- // Original has unclosed parens, replacement is complete
353
- // Find the closing of the multi-line expression
354
363
  let depth = origOpen - origClose;
355
364
  let endIdx = idx;
356
365
  for (let j = idx + 1; j < lines.length && depth > 0; j++) {
@@ -360,8 +369,7 @@ async function applyFix(res, loc, backups = new Map()) {
360
369
  endIdx = j;
361
370
  }
362
371
  if (endIdx > idx) {
363
- const removedLines = endIdx - idx;
364
- console.log(` ${C.GRAY}🔧 Expanding REPLACE to cover ${removedLines + 1}-line expression (lines ${p.line}-${endIdx + 1})${C.RESET}`);
372
+ console.log(` ${C.GRAY}🔧 Expanding REPLACE to cover ${endIdx - idx + 1}-line expression (lines ${p.line}-${endIdx + 1})${C.RESET}`);
365
373
  for (let j = idx; j <= endIdx; j++) {
366
374
  console.log(` ${C.RED}- Line ${j + 1}: ${lines[j].trim()}${C.RESET}`);
367
375
  }
@@ -372,6 +380,37 @@ async function applyFix(res, loc, backups = new Map()) {
372
380
  console.log(` ${C.RED}- Line ${p.line}: ${originalLine.trim()}${C.RESET}`);
373
381
  console.log(` ${C.GREEN}+ Line ${p.line}: ${cleanLine.trim()}${C.RESET}`);
374
382
  }
383
+ // Case B: Target line is a MIDDLE/CONTINUATION line of a multi-line expression
384
+ // Detect: replacement is a complete assignment (this.x = ...; ) but current line is just a string
385
+ } else if (/^\s*this\.\w+\s*=/.test(cleanLine) && cleanLine.trim().endsWith(';') && !/^\s*this\./.test(originalLine)) {
386
+ // Search BACKWARD for the opening line (this.xxx = page.locator()
387
+ let startIdx = idx;
388
+ for (let j = idx - 1; j >= 0; j--) {
389
+ startIdx = j;
390
+ const lo = (lines[j].match(/\(/g) || []).length;
391
+ const lc = (lines[j].match(/\)/g) || []).length;
392
+ if (lo > lc) break; // Found the opener
393
+ }
394
+ // Search FORWARD for the closing );
395
+ let endIdx = idx;
396
+ let depth = 0;
397
+ for (let j = startIdx; j < lines.length; j++) {
398
+ const lo = (lines[j].match(/\(/g) || []).length;
399
+ const lc = (lines[j].match(/\)/g) || []).length;
400
+ depth += lo - lc;
401
+ endIdx = j;
402
+ if (depth <= 0 && j >= idx) break;
403
+ }
404
+ const totalLines = endIdx - startIdx + 1;
405
+ console.log(` ${C.GRAY}🔧 Expanding REPLACE to cover ${totalLines}-line expression (lines ${startIdx + 1}-${endIdx + 1})${C.RESET}`);
406
+ for (let j = startIdx; j <= endIdx; j++) {
407
+ console.log(` ${C.RED}- Line ${j + 1}: ${lines[j].trim()}${C.RESET}`);
408
+ }
409
+ // Use indentation from the opener line
410
+ const openerIndent = lines[startIdx].match(/^\s*/)[0];
411
+ const expandedClean = openerIndent + cleanLine.trim();
412
+ lines.splice(startIdx, totalLines, expandedClean);
413
+ console.log(` ${C.GREEN}+ Line ${startIdx + 1}: ${expandedClean.trim()}${C.RESET}`);
375
414
  } else {
376
415
  lines[idx] = cleanLine;
377
416
  console.log(` ${C.RED}- Line ${p.line}: ${originalLine.trim()}${C.RESET}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deflake",
3
- "version": "1.2.40",
3
+ "version": "1.2.42",
4
4
  "description": "AI-powered self-healing tool for Playwright, Cypress, and WebdriverIO tests.",
5
5
  "main": "client.js",
6
6
  "bin": {