afterbefore 0.1.7 → 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/dist/cli.js +150 -132
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -63
- package/dist/index.js +147 -129
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -109,6 +109,7 @@ var Logger = class {
|
|
|
109
109
|
return;
|
|
110
110
|
}
|
|
111
111
|
this.spinner.text = text;
|
|
112
|
+
this.spinner.render();
|
|
112
113
|
}
|
|
113
114
|
completePipeline(finished = false) {
|
|
114
115
|
this.pipelineActive = false;
|
|
@@ -432,12 +433,6 @@ function isGlobalVisualFile(filePath) {
|
|
|
432
433
|
const p = filePath.replace(/^src\//, "");
|
|
433
434
|
return GLOBAL_FILES.has(p) || /globals?\.(css|scss)$/.test(p);
|
|
434
435
|
}
|
|
435
|
-
var VISUAL_CATEGORIES = /* @__PURE__ */ new Set([
|
|
436
|
-
"page",
|
|
437
|
-
"component",
|
|
438
|
-
"style",
|
|
439
|
-
"layout"
|
|
440
|
-
]);
|
|
441
436
|
function classifyFile(filePath) {
|
|
442
437
|
const p = filePath.replace(/^src\//, "");
|
|
443
438
|
if (/\.(test|spec)\.[tj]sx?$/.test(p) || /^tests?\//.test(p) || /\/__tests__\//.test(p) || p.includes(".test.") || p.includes(".spec.")) {
|
|
@@ -474,9 +469,6 @@ function classifyFiles(files) {
|
|
|
474
469
|
category: classifyFile(f.path)
|
|
475
470
|
}));
|
|
476
471
|
}
|
|
477
|
-
function filterVisuallyRelevant(files) {
|
|
478
|
-
return files.filter((f) => VISUAL_CATEGORIES.has(f.category));
|
|
479
|
-
}
|
|
480
472
|
|
|
481
473
|
// src/stages/graph.ts
|
|
482
474
|
import { readdirSync, readFileSync as readFileSync4 } from "fs";
|
|
@@ -728,6 +720,14 @@ function getLayoutDir(filePath) {
|
|
|
728
720
|
return match[1];
|
|
729
721
|
}
|
|
730
722
|
|
|
723
|
+
// src/utils/path.ts
|
|
724
|
+
function normalizePath(filePath) {
|
|
725
|
+
return filePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
726
|
+
}
|
|
727
|
+
function sanitizeLabel(label, maxLength = 40) {
|
|
728
|
+
return label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, maxLength);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
731
|
// src/stages/impact.ts
|
|
732
732
|
var MAX_DEPTH = 3;
|
|
733
733
|
function findAffectedRoutes(changedFiles, graph, projectRoot, maxRoutes = 0) {
|
|
@@ -795,6 +795,28 @@ function findAffectedRoutes(changedFiles, graph, projectRoot, maxRoutes = 0) {
|
|
|
795
795
|
logger.dim(`Found ${routes.length} affected route(s)`);
|
|
796
796
|
return routes;
|
|
797
797
|
}
|
|
798
|
+
function findRoutesForFile(file, graph) {
|
|
799
|
+
const routes = /* @__PURE__ */ new Set();
|
|
800
|
+
const start = normalizePath(file);
|
|
801
|
+
const queue = [{ path: start, depth: 0 }];
|
|
802
|
+
const visited = /* @__PURE__ */ new Set([start]);
|
|
803
|
+
while (queue.length > 0) {
|
|
804
|
+
const { path, depth } = queue.shift();
|
|
805
|
+
if (isPageFile(path)) {
|
|
806
|
+
const route = pagePathToRoute(path);
|
|
807
|
+
if (route) routes.add(route);
|
|
808
|
+
}
|
|
809
|
+
if (depth >= MAX_DEPTH) continue;
|
|
810
|
+
const importers = graph.reverse.get(path);
|
|
811
|
+
if (!importers) continue;
|
|
812
|
+
for (const importer of importers) {
|
|
813
|
+
if (visited.has(importer)) continue;
|
|
814
|
+
visited.add(importer);
|
|
815
|
+
queue.push({ path: importer, depth: depth + 1 });
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
return Array.from(routes);
|
|
819
|
+
}
|
|
798
820
|
|
|
799
821
|
// src/stages/worktree.ts
|
|
800
822
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -969,7 +991,7 @@ async function detectTabs(page, maxTabs) {
|
|
|
969
991
|
return allTabs.filter((t) => !t.selected).slice(0, maxTabs);
|
|
970
992
|
}
|
|
971
993
|
function sanitizeTabLabel(label) {
|
|
972
|
-
return label
|
|
994
|
+
return sanitizeLabel(label);
|
|
973
995
|
}
|
|
974
996
|
|
|
975
997
|
// src/utils/sections.ts
|
|
@@ -1061,7 +1083,7 @@ async function cleanupSectionTags(page) {
|
|
|
1061
1083
|
});
|
|
1062
1084
|
}
|
|
1063
1085
|
function sanitizeSectionLabel(label) {
|
|
1064
|
-
return label
|
|
1086
|
+
return sanitizeLabel(label);
|
|
1065
1087
|
}
|
|
1066
1088
|
|
|
1067
1089
|
// src/stages/capture.ts
|
|
@@ -1077,11 +1099,9 @@ async function launchBrowser() {
|
|
|
1077
1099
|
}
|
|
1078
1100
|
}
|
|
1079
1101
|
var MAX_COMPONENT_INSTANCES_PER_SOURCE = 20;
|
|
1080
|
-
function normalizePath(filePath) {
|
|
1081
|
-
return filePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
1082
|
-
}
|
|
1083
1102
|
function sanitizeComponentLabel(label) {
|
|
1084
|
-
|
|
1103
|
+
const noExt = label.replace(/\.[a-z0-9]+$/i, "");
|
|
1104
|
+
return sanitizeLabel(noExt, 60);
|
|
1085
1105
|
}
|
|
1086
1106
|
function groupBySource(instances) {
|
|
1087
1107
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -1396,6 +1416,88 @@ async function captureAutoSections(afterPage, beforePage, parentPrefix, parentLa
|
|
|
1396
1416
|
await cleanupSectionTags(afterPage);
|
|
1397
1417
|
await cleanupSectionTags(beforePage);
|
|
1398
1418
|
}
|
|
1419
|
+
async function captureAutoTabs(afterPage, beforePage, task, beforeUrl, afterUrl, outputDir, options, settle, results) {
|
|
1420
|
+
const tabs = await detectTabs(afterPage, options.maxTabsPerRoute);
|
|
1421
|
+
const usedPrefixes = /* @__PURE__ */ new Set();
|
|
1422
|
+
for (const tab of tabs) {
|
|
1423
|
+
let slug = sanitizeTabLabel(tab.label);
|
|
1424
|
+
if (!slug) continue;
|
|
1425
|
+
if (usedPrefixes.has(slug)) {
|
|
1426
|
+
let suffix = 2;
|
|
1427
|
+
while (usedPrefixes.has(`${slug}-${suffix}`)) suffix++;
|
|
1428
|
+
slug = `${slug}-${suffix}`;
|
|
1429
|
+
}
|
|
1430
|
+
usedPrefixes.add(slug);
|
|
1431
|
+
const tabPrefix = `${task.prefix}~${slug}`;
|
|
1432
|
+
const tabLabel = `${task.label} [${tab.label}]`;
|
|
1433
|
+
const tabBeforePath = join6(outputDir, `${tabPrefix}-before.png`);
|
|
1434
|
+
const tabAfterPath = join6(outputDir, `${tabPrefix}-after.png`);
|
|
1435
|
+
try {
|
|
1436
|
+
const afterUrlBefore = afterPage.url();
|
|
1437
|
+
await afterPage.getByRole("tab", { name: tab.label }).first().click();
|
|
1438
|
+
await settle(afterPage);
|
|
1439
|
+
if (afterPage.url() !== afterUrlBefore) {
|
|
1440
|
+
await afterPage.goBack({ waitUntil: "networkidle" });
|
|
1441
|
+
await settle(afterPage);
|
|
1442
|
+
continue;
|
|
1443
|
+
}
|
|
1444
|
+
await afterPage.screenshot({ path: tabAfterPath, fullPage: true });
|
|
1445
|
+
try {
|
|
1446
|
+
const beforeUrlBefore = beforePage.url();
|
|
1447
|
+
await beforePage.getByRole("tab", { name: tab.label }).first().click({ timeout: 2e3 });
|
|
1448
|
+
await settle(beforePage);
|
|
1449
|
+
if (beforePage.url() !== beforeUrlBefore) {
|
|
1450
|
+
await beforePage.goBack({ waitUntil: "networkidle" });
|
|
1451
|
+
await settle(beforePage);
|
|
1452
|
+
await beforePage.screenshot({ path: tabBeforePath, fullPage: true });
|
|
1453
|
+
} else {
|
|
1454
|
+
await beforePage.screenshot({ path: tabBeforePath, fullPage: true });
|
|
1455
|
+
}
|
|
1456
|
+
} catch {
|
|
1457
|
+
await beforePage.screenshot({ path: tabBeforePath, fullPage: true });
|
|
1458
|
+
}
|
|
1459
|
+
results.push({
|
|
1460
|
+
route: tabLabel,
|
|
1461
|
+
prefix: tabPrefix,
|
|
1462
|
+
beforePath: tabBeforePath,
|
|
1463
|
+
afterPath: tabAfterPath
|
|
1464
|
+
});
|
|
1465
|
+
if ((task.changedComponents?.length ?? 0) > 0) {
|
|
1466
|
+
await captureComponentInstances(
|
|
1467
|
+
afterPage,
|
|
1468
|
+
beforePage,
|
|
1469
|
+
task.changedComponents,
|
|
1470
|
+
tabPrefix,
|
|
1471
|
+
tabLabel,
|
|
1472
|
+
outputDir,
|
|
1473
|
+
tabBeforePath,
|
|
1474
|
+
tabAfterPath,
|
|
1475
|
+
results
|
|
1476
|
+
);
|
|
1477
|
+
}
|
|
1478
|
+
if (options.autoSections && !task.skipAutoSections) {
|
|
1479
|
+
await captureAutoSections(
|
|
1480
|
+
afterPage,
|
|
1481
|
+
beforePage,
|
|
1482
|
+
tabPrefix,
|
|
1483
|
+
tabLabel,
|
|
1484
|
+
outputDir,
|
|
1485
|
+
options,
|
|
1486
|
+
settle,
|
|
1487
|
+
results
|
|
1488
|
+
);
|
|
1489
|
+
}
|
|
1490
|
+
} catch {
|
|
1491
|
+
logger.dim(` Skipped tab "${tab.label}" on ${task.route}`);
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
if (tabs.length > 0) {
|
|
1495
|
+
await Promise.all([
|
|
1496
|
+
beforePage.goto(`${beforeUrl}${task.route}`, { waitUntil: "networkidle" }),
|
|
1497
|
+
afterPage.goto(`${afterUrl}${task.route}`, { waitUntil: "networkidle" })
|
|
1498
|
+
]);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1399
1501
|
async function captureRoutes(tasks, beforeUrl, afterUrl, outputDir, options) {
|
|
1400
1502
|
const browser = options.browser ?? await launchBrowser();
|
|
1401
1503
|
const ownsBrowser = !options.browser;
|
|
@@ -1478,86 +1580,17 @@ async function captureRoutes(tasks, beforeUrl, afterUrl, outputDir, options) {
|
|
|
1478
1580
|
);
|
|
1479
1581
|
}
|
|
1480
1582
|
if (options.autoTabs && !task.actions && !task.selector && !task.skipAutoTabs) {
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
const tabPrefix = `${task.prefix}~${slug}`;
|
|
1493
|
-
const tabLabel = `${task.label} [${tab.label}]`;
|
|
1494
|
-
const tabBeforePath = join6(outputDir, `${tabPrefix}-before.png`);
|
|
1495
|
-
const tabAfterPath = join6(outputDir, `${tabPrefix}-after.png`);
|
|
1496
|
-
try {
|
|
1497
|
-
const afterUrlBefore = afterPage.url();
|
|
1498
|
-
await afterPage.getByRole("tab", { name: tab.label }).first().click();
|
|
1499
|
-
await settle(afterPage);
|
|
1500
|
-
if (afterPage.url() !== afterUrlBefore) {
|
|
1501
|
-
await afterPage.goBack({ waitUntil: "networkidle" });
|
|
1502
|
-
await settle(afterPage);
|
|
1503
|
-
continue;
|
|
1504
|
-
}
|
|
1505
|
-
await afterPage.screenshot({ path: tabAfterPath, fullPage: true });
|
|
1506
|
-
try {
|
|
1507
|
-
const beforeUrlBefore = beforePage.url();
|
|
1508
|
-
await beforePage.getByRole("tab", { name: tab.label }).first().click({ timeout: 2e3 });
|
|
1509
|
-
await settle(beforePage);
|
|
1510
|
-
if (beforePage.url() !== beforeUrlBefore) {
|
|
1511
|
-
await beforePage.goBack({ waitUntil: "networkidle" });
|
|
1512
|
-
await settle(beforePage);
|
|
1513
|
-
await beforePage.screenshot({ path: tabBeforePath, fullPage: true });
|
|
1514
|
-
} else {
|
|
1515
|
-
await beforePage.screenshot({ path: tabBeforePath, fullPage: true });
|
|
1516
|
-
}
|
|
1517
|
-
} catch {
|
|
1518
|
-
await beforePage.screenshot({ path: tabBeforePath, fullPage: true });
|
|
1519
|
-
}
|
|
1520
|
-
results.push({
|
|
1521
|
-
route: tabLabel,
|
|
1522
|
-
prefix: tabPrefix,
|
|
1523
|
-
beforePath: tabBeforePath,
|
|
1524
|
-
afterPath: tabAfterPath
|
|
1525
|
-
});
|
|
1526
|
-
if ((task.changedComponents?.length ?? 0) > 0) {
|
|
1527
|
-
await captureComponentInstances(
|
|
1528
|
-
afterPage,
|
|
1529
|
-
beforePage,
|
|
1530
|
-
task.changedComponents,
|
|
1531
|
-
tabPrefix,
|
|
1532
|
-
tabLabel,
|
|
1533
|
-
outputDir,
|
|
1534
|
-
tabBeforePath,
|
|
1535
|
-
tabAfterPath,
|
|
1536
|
-
results
|
|
1537
|
-
);
|
|
1538
|
-
}
|
|
1539
|
-
if (options.autoSections && !task.skipAutoSections) {
|
|
1540
|
-
await captureAutoSections(
|
|
1541
|
-
afterPage,
|
|
1542
|
-
beforePage,
|
|
1543
|
-
tabPrefix,
|
|
1544
|
-
tabLabel,
|
|
1545
|
-
outputDir,
|
|
1546
|
-
options,
|
|
1547
|
-
settle,
|
|
1548
|
-
results
|
|
1549
|
-
);
|
|
1550
|
-
}
|
|
1551
|
-
} catch {
|
|
1552
|
-
logger.dim(` Skipped tab "${tab.label}" on ${task.route}`);
|
|
1553
|
-
}
|
|
1554
|
-
}
|
|
1555
|
-
if (tabs.length > 0) {
|
|
1556
|
-
await Promise.all([
|
|
1557
|
-
beforePage.goto(`${beforeUrl}${task.route}`, { waitUntil: "networkidle" }),
|
|
1558
|
-
afterPage.goto(`${afterUrl}${task.route}`, { waitUntil: "networkidle" })
|
|
1559
|
-
]);
|
|
1560
|
-
}
|
|
1583
|
+
await captureAutoTabs(
|
|
1584
|
+
afterPage,
|
|
1585
|
+
beforePage,
|
|
1586
|
+
task,
|
|
1587
|
+
beforeUrl,
|
|
1588
|
+
afterUrl,
|
|
1589
|
+
outputDir,
|
|
1590
|
+
options,
|
|
1591
|
+
settle,
|
|
1592
|
+
results
|
|
1593
|
+
);
|
|
1561
1594
|
}
|
|
1562
1595
|
}
|
|
1563
1596
|
await Promise.all([beforeCtx.close(), afterCtx.close()]);
|
|
@@ -1592,18 +1625,30 @@ function normalizeDimensions(img1, img2) {
|
|
|
1592
1625
|
return [pad(img1), pad(img2)];
|
|
1593
1626
|
}
|
|
1594
1627
|
async function generateComposite(beforePath, afterPath, outputPath, browser, bgColor) {
|
|
1595
|
-
const
|
|
1596
|
-
const
|
|
1628
|
+
const beforeBuf = readFileSync5(beforePath);
|
|
1629
|
+
const afterBuf = readFileSync5(afterPath);
|
|
1630
|
+
const beforeUri = `data:image/png;base64,${beforeBuf.toString("base64")}`;
|
|
1631
|
+
const afterUri = `data:image/png;base64,${afterBuf.toString("base64")}`;
|
|
1632
|
+
const beforePng = PNG.sync.read(beforeBuf);
|
|
1633
|
+
const afterPng = PNG.sync.read(afterBuf);
|
|
1634
|
+
const imgW = Math.max(beforePng.width, afterPng.width);
|
|
1635
|
+
const imgH = Math.max(beforePng.height, afterPng.height);
|
|
1636
|
+
const PADDING = 120;
|
|
1637
|
+
const GAP = 80;
|
|
1638
|
+
const LABEL_H = 70;
|
|
1639
|
+
const canvasW = Math.max(600, Math.min(2400, imgW * 2 + GAP + PADDING * 2));
|
|
1640
|
+
const canvasH = Math.max(300, Math.min(2400, imgH + LABEL_H + PADDING * 2));
|
|
1641
|
+
const maxImgH = canvasH - PADDING * 2 - LABEL_H;
|
|
1597
1642
|
const page = await browser.newPage({
|
|
1598
|
-
viewport: { width:
|
|
1643
|
+
viewport: { width: canvasW, height: canvasH },
|
|
1599
1644
|
deviceScaleFactor: 1
|
|
1600
1645
|
});
|
|
1601
1646
|
const html = `<!DOCTYPE html>
|
|
1602
1647
|
<html><head><style>
|
|
1603
1648
|
* { margin: 0; box-sizing: border-box; }
|
|
1604
|
-
body { background: ${bgColor}; display: flex; justify-content: center; align-items: center; width:
|
|
1649
|
+
body { background: ${bgColor}; display: flex; justify-content: center; align-items: center; width: ${canvasW}px; height: ${canvasH}px; padding: ${PADDING}px; gap: ${GAP}px; overflow: hidden; }
|
|
1605
1650
|
.col { flex: 1; display: flex; flex-direction: column; align-items: center; min-width: 0; max-height: 100%; }
|
|
1606
|
-
img { width: 100%; max-height:
|
|
1651
|
+
img { width: 100%; max-height: ${maxImgH}px; object-fit: contain; }
|
|
1607
1652
|
.label { margin-top: 40px; font: 500 30px/1 system-ui, sans-serif; flex-shrink: 0; }
|
|
1608
1653
|
.before { color: #888; }
|
|
1609
1654
|
.after { color: #22c55e; }
|
|
@@ -1788,36 +1833,10 @@ function routeToPrefix(route) {
|
|
|
1788
1833
|
if (route === "/") return "_root";
|
|
1789
1834
|
return route.replace(/^\//, "").replace(/\//g, "-");
|
|
1790
1835
|
}
|
|
1791
|
-
var ROUTE_IMPACT_MAX_DEPTH = 3;
|
|
1792
|
-
function normalizePath2(filePath) {
|
|
1793
|
-
return filePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
1794
|
-
}
|
|
1795
|
-
function findRoutesForChangedFile(changedFile, graph) {
|
|
1796
|
-
const routes = /* @__PURE__ */ new Set();
|
|
1797
|
-
const start = normalizePath2(changedFile);
|
|
1798
|
-
const queue = [{ file: start, depth: 0 }];
|
|
1799
|
-
const visited = /* @__PURE__ */ new Set([start]);
|
|
1800
|
-
while (queue.length > 0) {
|
|
1801
|
-
const { file, depth } = queue.shift();
|
|
1802
|
-
if (isPageFile(file)) {
|
|
1803
|
-
const route = pagePathToRoute(file);
|
|
1804
|
-
if (route) routes.add(route);
|
|
1805
|
-
}
|
|
1806
|
-
if (depth >= ROUTE_IMPACT_MAX_DEPTH) continue;
|
|
1807
|
-
const importers = graph.reverse.get(file);
|
|
1808
|
-
if (!importers) continue;
|
|
1809
|
-
for (const importer of importers) {
|
|
1810
|
-
if (visited.has(importer)) continue;
|
|
1811
|
-
visited.add(importer);
|
|
1812
|
-
queue.push({ file: importer, depth: depth + 1 });
|
|
1813
|
-
}
|
|
1814
|
-
}
|
|
1815
|
-
return Array.from(routes);
|
|
1816
|
-
}
|
|
1817
1836
|
function mapRouteToChangedComponents(changedComponentFiles, graph) {
|
|
1818
1837
|
const routeMap = /* @__PURE__ */ new Map();
|
|
1819
1838
|
for (const componentPath of changedComponentFiles) {
|
|
1820
|
-
const routes =
|
|
1839
|
+
const routes = findRoutesForFile(componentPath, graph);
|
|
1821
1840
|
for (const route of routes) {
|
|
1822
1841
|
const next = routeMap.get(route) ?? [];
|
|
1823
1842
|
next.push(componentPath);
|
|
@@ -1860,7 +1879,7 @@ async function runPipeline(options) {
|
|
|
1860
1879
|
const outputDir = resolve4(cwd, output, sessionName);
|
|
1861
1880
|
const startTime = Date.now();
|
|
1862
1881
|
try {
|
|
1863
|
-
const version = true ? "0.1.
|
|
1882
|
+
const version = true ? "0.1.9" : "dev";
|
|
1864
1883
|
console.log(`
|
|
1865
1884
|
afterbefore v${version} \xB7 Comparing against ${base}
|
|
1866
1885
|
`);
|
|
@@ -1875,7 +1894,6 @@ afterbefore v${version} \xB7 Comparing against ${base}
|
|
|
1875
1894
|
return;
|
|
1876
1895
|
}
|
|
1877
1896
|
const classified = classifyFiles(diffFiles);
|
|
1878
|
-
const visualFiles = filterVisuallyRelevant(classified);
|
|
1879
1897
|
const impactfulFiles = classified.filter(
|
|
1880
1898
|
(f) => f.category !== "test" && f.category !== "other"
|
|
1881
1899
|
);
|
|
@@ -2006,7 +2024,7 @@ afterbefore v${version} \xB7 Comparing against ${base}
|
|
|
2006
2024
|
var program = new Command();
|
|
2007
2025
|
program.name("afterbefore").description(
|
|
2008
2026
|
"Automatic before/after screenshot capture for PRs. Git diff is the config."
|
|
2009
|
-
).version("0.1.
|
|
2027
|
+
).version("0.1.9").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(
|
|
2010
2028
|
"--threshold <percent>",
|
|
2011
2029
|
"Diff threshold percentage (changes below this are ignored)",
|
|
2012
2030
|
"0.1"
|
|
@@ -2014,7 +2032,7 @@ program.name("afterbefore").description(
|
|
|
2014
2032
|
"--max-routes <count>",
|
|
2015
2033
|
"Maximum routes to capture (0 = unlimited)",
|
|
2016
2034
|
"6"
|
|
2017
|
-
).option("--width <pixels>", "Viewport width", "1280").option("--height <pixels>", "Viewport height", "720").option("--device <name>", 'Playwright device descriptor (e.g. "iPhone 14")').option("--delay <ms>", "Extra wait time (ms) after page load", "0").option("--no-auto-tabs", "Disable auto-detection of ARIA tab states").option("--max-tabs <count>", "Max auto-detected tabs per route", "5").option("--auto-sections", "
|
|
2035
|
+
).option("--width <pixels>", "Viewport width", "1280").option("--height <pixels>", "Viewport height", "720").option("--device <name>", 'Playwright device descriptor (e.g. "iPhone 14")').option("--delay <ms>", "Extra wait time (ms) after page load", "0").option("--no-auto-tabs", "Disable auto-detection of ARIA tab states").option("--max-tabs <count>", "Max auto-detected tabs per route", "5").option("--no-auto-sections", "Disable auto-detection of heading-labeled sections").option("--max-sections <count>", "Max auto-detected sections per page state", "10").action(async (opts) => {
|
|
2018
2036
|
const cwd = process.cwd();
|
|
2019
2037
|
if (!isGitRepo(cwd)) {
|
|
2020
2038
|
logger.error("Not a git repository. Run this from inside a git repo.");
|
|
@@ -2032,7 +2050,7 @@ program.name("afterbefore").description(
|
|
|
2032
2050
|
delay: parseInt(opts.delay, 10),
|
|
2033
2051
|
autoTabs: opts.autoTabs,
|
|
2034
2052
|
maxTabsPerRoute: parseInt(opts.maxTabs, 10),
|
|
2035
|
-
autoSections: opts.autoSections
|
|
2053
|
+
autoSections: opts.autoSections,
|
|
2036
2054
|
maxSectionsPerRoute: parseInt(opts.maxSections, 10),
|
|
2037
2055
|
cwd
|
|
2038
2056
|
};
|