afterbefore 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -4,6 +4,7 @@
4
4
  import { Command } from "commander";
5
5
 
6
6
  // src/logger.ts
7
+ import { writeFileSync } from "fs";
7
8
  import chalk from "chalk";
8
9
  import ora from "ora";
9
10
  var BAR_WIDTH = 20;
@@ -14,7 +15,13 @@ var Logger = class {
14
15
  lastStep = 0;
15
16
  lastLabel = "";
16
17
  pipelineActive = false;
18
+ logBuffer = [];
19
+ log(level, message) {
20
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
21
+ this.logBuffer.push(`${ts} [${level}] ${message}`);
22
+ }
17
23
  info(message) {
24
+ this.log("info", message);
18
25
  if (this.pipelineActive) return;
19
26
  if (this.spinner) {
20
27
  this.spinner.clear();
@@ -25,6 +32,7 @@ var Logger = class {
25
32
  }
26
33
  }
27
34
  success(message) {
35
+ this.log("ok", message);
28
36
  if (this.pipelineActive) return;
29
37
  if (this.spinner) {
30
38
  this.spinner.clear();
@@ -35,6 +43,7 @@ var Logger = class {
35
43
  }
36
44
  }
37
45
  warn(message) {
46
+ this.log("warn", message);
38
47
  if (this.spinner) {
39
48
  this.spinner.clear();
40
49
  console.log(chalk.yellow("\u26A0"), message);
@@ -44,6 +53,7 @@ var Logger = class {
44
53
  }
45
54
  }
46
55
  error(message) {
56
+ this.log("error", message);
47
57
  if (this.spinner) {
48
58
  this.spinner.clear();
49
59
  console.error(chalk.red("\u2716"), message);
@@ -53,6 +63,7 @@ var Logger = class {
53
63
  }
54
64
  }
55
65
  dim(message) {
66
+ this.log("debug", message);
56
67
  if (this.pipelineActive) return;
57
68
  if (this.spinner) {
58
69
  this.spinner.clear();
@@ -63,6 +74,7 @@ var Logger = class {
63
74
  }
64
75
  }
65
76
  spin(message) {
77
+ this.log("info", message);
66
78
  this.clearSpinner();
67
79
  this.spinner = ora(message).start();
68
80
  return this.spinner;
@@ -76,6 +88,7 @@ var Logger = class {
76
88
  this.lastLabel = "";
77
89
  this.pipelineActive = true;
78
90
  this.clearSpinner();
91
+ this.log("info", `Pipeline started (${total} steps)`);
79
92
  const text = this.renderPipeline(0, "Starting...");
80
93
  this.spinner = ora({
81
94
  text: chalk.dim(text),
@@ -86,6 +99,7 @@ var Logger = class {
86
99
  if (step === this.lastStep && label === this.lastLabel) return;
87
100
  this.lastStep = step;
88
101
  this.lastLabel = label;
102
+ this.log("step", `${step}/${this.pipelineTotal} ${label}`);
89
103
  const text = chalk.dim(this.renderPipeline(step, label));
90
104
  if (!this.spinner) {
91
105
  this.spinner = ora({
@@ -98,28 +112,33 @@ var Logger = class {
98
112
  }
99
113
  completePipeline(finished = false) {
100
114
  this.pipelineActive = false;
115
+ this.log("info", `Pipeline ${finished ? "completed" : "stopped"}`);
101
116
  if (this.spinner) {
102
117
  if (finished) {
103
118
  this.spinner.stop();
104
- const bar = "\u2588".repeat(BAR_WIDTH);
119
+ const bar = "#".repeat(BAR_WIDTH);
105
120
  console.log(`${chalk.green("\u2714")} ${bar}`);
106
121
  } else {
107
122
  this.spinner.stop();
108
123
  }
109
124
  this.spinner = null;
110
125
  } else if (finished) {
111
- const bar = "\u2588".repeat(BAR_WIDTH);
126
+ const bar = "#".repeat(BAR_WIDTH);
112
127
  console.log(`${chalk.green("\u2714")} ${bar}`);
113
128
  }
114
129
  this.pipelineTotal = 0;
115
130
  this.lastStep = 0;
116
131
  this.lastLabel = "";
117
132
  }
133
+ writeLogFile(filePath) {
134
+ if (this.logBuffer.length === 0) return;
135
+ writeFileSync(filePath, this.logBuffer.join("\n") + "\n", "utf-8");
136
+ }
118
137
  renderPipeline(step, label) {
119
138
  const total = this.pipelineTotal || 1;
120
139
  const clampedStep = Math.max(0, Math.min(step, total));
121
140
  const filled = Math.round(clampedStep / total * BAR_WIDTH);
122
- const bar = "\u2588".repeat(filled) + "\u2591".repeat(BAR_WIDTH - filled);
141
+ const bar = "#".repeat(filled) + "-".repeat(BAR_WIDTH - filled);
123
142
  return ` ${bar} ${clampedStep}/${total} ${label}`;
124
143
  }
125
144
  clearSpinner() {
@@ -1311,13 +1330,19 @@ async function captureRoutes(tasks, beforeUrl, afterUrl, outputDir, options) {
1311
1330
  tagChangedComponentInstances(afterPage, changedComponents),
1312
1331
  tagChangedComponentInstances(beforePage, changedComponents)
1313
1332
  ]);
1333
+ logger.dim(` Component detection on ${task.route}: ${changedComponents.length} source(s), ${afterInstances.length} after / ${beforeInstances.length} before instance(s)`);
1314
1334
  const afterBySource = groupBySource(afterInstances);
1315
1335
  const beforeBySource = groupBySource(beforeInstances);
1316
1336
  for (const source of changedComponents) {
1317
1337
  const afterList = afterBySource.get(source) ?? [];
1318
1338
  const beforeList = beforeBySource.get(source) ?? [];
1319
1339
  const pairCount = Math.min(afterList.length, beforeList.length);
1320
- if (pairCount === 0) continue;
1340
+ if (pairCount === 0) {
1341
+ if (afterList.length > 0 || beforeList.length > 0) {
1342
+ logger.dim(` ${source}: ${afterList.length} after / ${beforeList.length} before (skipping unpaired)`);
1343
+ }
1344
+ continue;
1345
+ }
1321
1346
  for (let pairIndex = 0; pairIndex < pairCount; pairIndex++) {
1322
1347
  const afterInstance = afterList[pairIndex];
1323
1348
  const beforeInstance = beforeList[pairIndex];
@@ -1325,13 +1350,6 @@ async function captureRoutes(tasks, beforeUrl, afterUrl, outputDir, options) {
1325
1350
  const itemSlug = `${sourceSlug}-${pairIndex + 1}`;
1326
1351
  const componentName = afterInstance.name || beforeInstance.name || source.split("/").pop() || source;
1327
1352
  const baseLabel = `${task.label} [${componentName} #${pairIndex + 1}]`;
1328
- const contextPrefix = `${task.prefix}~cmp.${itemSlug}~context`;
1329
- results.push({
1330
- route: `${baseLabel} [context]`,
1331
- prefix: contextPrefix,
1332
- beforePath,
1333
- afterPath
1334
- });
1335
1353
  const parentPrefix = `${task.prefix}~cmp.${itemSlug}~parent`;
1336
1354
  const parentBeforePath = join6(outputDir, `${parentPrefix}-before.png`);
1337
1355
  const parentAfterPath = join6(outputDir, `${parentPrefix}-after.png`);
@@ -1382,6 +1400,15 @@ async function captureRoutes(tasks, beforeUrl, afterUrl, outputDir, options) {
1382
1400
  afterPath: componentAfterPath
1383
1401
  });
1384
1402
  }
1403
+ if (parentBeforeOk && parentAfterOk || componentBeforeOk && componentAfterOk) {
1404
+ const contextPrefix = `${task.prefix}~cmp.${itemSlug}~context`;
1405
+ results.push({
1406
+ route: `${baseLabel} [context]`,
1407
+ prefix: contextPrefix,
1408
+ beforePath,
1409
+ afterPath
1410
+ });
1411
+ }
1385
1412
  }
1386
1413
  }
1387
1414
  }
@@ -1571,7 +1598,7 @@ async function compareScreenshots(captures, outputDir, threshold = 0.1, options)
1571
1598
  }
1572
1599
 
1573
1600
  // src/stages/report.ts
1574
- import { writeFileSync } from "fs";
1601
+ import { writeFileSync as writeFileSync2 } from "fs";
1575
1602
  import { join as join8 } from "path";
1576
1603
  import { execSync as execSync4 } from "child_process";
1577
1604
 
@@ -1744,11 +1771,11 @@ async function generateReport(results, outputDir, options) {
1744
1771
  await ensureDir(outputDir);
1745
1772
  const summaryMd = generateSummaryMd(results);
1746
1773
  const summaryPath = join8(outputDir, "summary.md");
1747
- writeFileSync(summaryPath, summaryMd, "utf-8");
1774
+ writeFileSync2(summaryPath, summaryMd, "utf-8");
1748
1775
  logger.success(`Written summary to ${summaryPath}`);
1749
1776
  const reportHtml = generateReportHtml(results, outputDir);
1750
1777
  const indexPath = join8(outputDir, "index.html");
1751
- writeFileSync(indexPath, reportHtml, "utf-8");
1778
+ writeFileSync2(indexPath, reportHtml, "utf-8");
1752
1779
  logger.success(`Written report to ${indexPath}`);
1753
1780
  if (options.post) {
1754
1781
  const prNumber = findPrNumber();
@@ -1851,7 +1878,7 @@ async function runPipeline(options) {
1851
1878
  const outputDir = resolve4(cwd, output, sessionName);
1852
1879
  const startTime = Date.now();
1853
1880
  try {
1854
- const version = true ? "0.1.4" : "dev";
1881
+ const version = true ? "0.1.6" : "dev";
1855
1882
  console.log(`
1856
1883
  afterbefore v${version} \xB7 Comparing against ${base}
1857
1884
  `);
@@ -1989,6 +2016,10 @@ afterbefore v${version} \xB7 Comparing against ${base}
1989
2016
  openInBrowser(resolve4(outputDir, "index.html"));
1990
2017
  }
1991
2018
  } finally {
2019
+ try {
2020
+ logger.writeLogFile(resolve4(outputDir, "debug.log"));
2021
+ } catch {
2022
+ }
1992
2023
  await cleanupRegistry.runAll();
1993
2024
  }
1994
2025
  }
@@ -1997,7 +2028,7 @@ afterbefore v${version} \xB7 Comparing against ${base}
1997
2028
  var program = new Command();
1998
2029
  program.name("afterbefore").description(
1999
2030
  "Automatic before/after screenshot capture for PRs. Git diff is the config."
2000
- ).version("0.1.4").option("--base <ref>", "Base branch or ref to compare against", "main").option("--output <dir>", "Output directory for screenshots", ".afterbefore").option("--post", "Post results as a PR comment via gh CLI", false).option(
2031
+ ).version("0.1.6").option("--base <ref>", "Base branch or ref to compare against", "main").option("--output <dir>", "Output directory for screenshots", ".afterbefore").option("--post", "Post results as a PR comment via gh CLI", false).option(
2001
2032
  "--threshold <percent>",
2002
2033
  "Diff threshold percentage (changes below this are ignored)",
2003
2034
  "0.1"