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 +47 -16
- package/dist/cli.js.map +1 -1
- package/dist/index.js +46 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -6
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 = "
|
|
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 = "
|
|
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 = "
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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"
|