afterbefore 0.1.13 → 0.1.14
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 +20 -17
- package/dist/cli.js.map +1 -1
- package/dist/index.js +19 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/pipeline.ts","../src/logger.ts","../src/cleanup.ts","../src/config.ts","../src/utils/fs.ts","../src/utils/port.ts","../src/utils/bgcolor.ts","../src/utils/git.ts","../src/stages/diff.ts","../src/stages/classify.ts","../src/stages/graph.ts","../src/stages/resolve.ts","../src/utils/nextjs.ts","../src/utils/path.ts","../src/stages/impact.ts","../src/stages/worktree.ts","../src/errors.ts","../src/utils/pm.ts","../src/stages/server.ts","../src/stages/capture.ts","../src/utils/tabs.ts","../src/utils/sections.ts","../src/stages/compare.ts","../src/stages/report.ts","../src/templates/summary.md.ts"],"sourcesContent":["import { resolve } from \"path\";\nimport { unlinkSync } from \"fs\";\nimport type { PipelineOptions, AffectedRoute, AfterbeforeConfig, CaptureTask, ImportGraph } from \"./types.js\";\nimport { logger } from \"./logger.js\";\nimport { cleanupRegistry } from \"./cleanup.js\";\nimport { loadConfig } from \"./config.js\";\nimport { ensureDir } from \"./utils/fs.js\";\nimport { findAvailablePort } from \"./utils/port.js\";\nimport { detectBgColor } from \"./utils/bgcolor.js\";\nimport { getGitDiff, getCurrentBranch } from \"./utils/git.js\";\nimport { getChangedFiles } from \"./stages/diff.js\";\nimport { classifyFiles, isGlobalVisualFile } from \"./stages/classify.js\";\nimport { buildImportGraph } from \"./stages/graph.js\";\nimport { findAffectedRoutes, findRoutesForFile } from \"./stages/impact.js\";\nimport { isPageFile, pagePathToRoute } from \"./utils/nextjs.js\";\nimport { createWorktree } from \"./stages/worktree.js\";\nimport { startServer, stopServer } from \"./stages/server.js\";\nimport { captureRoutes, launchBrowser } from \"./stages/capture.js\";\nimport { compareScreenshots } from \"./stages/compare.js\";\nimport { generateReport } from \"./stages/report.js\";\nimport { generateSummaryMd } from \"./templates/summary.md.js\";\ndeclare const __VERSION__: string;\n\nfunction generateSessionName(cwd: string): string {\n const branch = getCurrentBranch(cwd);\n const name = branch.replace(/^(feat|fix|perf|chore|refactor|docs|style|test|ci|build)\\//, \"\");\n const date = new Date().toISOString().slice(0, 10);\n return `${name}_${date}`;\n}\n\nfunction routeToPrefix(route: string): string {\n if (route === \"/\") return \"_root\";\n return route.replace(/^\\//, \"\").replace(/\\//g, \"-\");\n}\n\nfunction mapRouteToChangedComponents(\n changedComponentFiles: string[],\n graph: ImportGraph\n): Map<string, string[]> {\n const routeMap = new Map<string, string[]>();\n\n for (const componentPath of changedComponentFiles) {\n const routes = findRoutesForFile(componentPath, graph);\n for (const route of routes) {\n const next = routeMap.get(route) ?? [];\n next.push(componentPath);\n routeMap.set(route, next);\n }\n }\n\n for (const [route, files] of routeMap.entries()) {\n routeMap.set(route, Array.from(new Set(files)).sort((a, b) => a.localeCompare(b)));\n }\n\n return routeMap;\n}\n\nfunction expandRoutes(\n routes: AffectedRoute[],\n config: AfterbeforeConfig | null,\n routeComponentMap: Map<string, string[]>\n): CaptureTask[] {\n const tasks: CaptureTask[] = [];\n for (const r of routes) {\n const scenarios = config?.scenarios?.[r.route] ?? [];\n const changedComponents = routeComponentMap.get(r.route) ?? [];\n // Default capture — skip auto-tabs if route has manual scenarios\n tasks.push({\n route: r.route,\n label: r.route,\n prefix: routeToPrefix(r.route),\n skipAutoTabs: scenarios.length > 0,\n skipAutoSections: scenarios.length > 0,\n changedComponents,\n });\n // Scenario captures\n for (const s of scenarios) {\n tasks.push({\n route: r.route,\n label: `${r.route} [${s.name}]`,\n prefix: `${routeToPrefix(r.route)}~${s.name}`,\n actions: s.actions,\n selector: s.selector,\n });\n }\n }\n return tasks;\n}\n\nexport async function runPipeline(options: PipelineOptions): Promise<void> {\n const { base, output, post, cwd } = options;\n const sessionName = generateSessionName(cwd);\n const outputDir = resolve(cwd, output, sessionName);\n const startTime = Date.now();\n\n try {\n // Print banner\n const version = typeof __VERSION__ !== \"undefined\" ? __VERSION__ : \"dev\";\n console.log(`\\nafterbefore v${version} \\u00b7 Comparing against ${base}\\n`);\n\n // Load optional config (scenarios)\n const config = await loadConfig(cwd);\n\n logger.startPipeline(8);\n\n // 1. Analyze diff\n logger.pipeline(1, \"Analyzing diff...\");\n const diffFiles = getChangedFiles(base, cwd);\n const gitDiff = getGitDiff(base, cwd);\n\n if (diffFiles.length === 0) {\n logger.completePipeline();\n logger.success(\"No changed files detected. Nothing to do.\");\n return;\n }\n\n // 2. Classify + build import graph (overlap worktree creation)\n const classified = classifyFiles(diffFiles);\n const impactfulFiles = classified.filter(\n (f) => f.category !== \"test\" && f.category !== \"other\"\n );\n\n if (impactfulFiles.length === 0) {\n logger.completePipeline();\n logger.success(\n \"No visually relevant changes detected (only test/other files changed).\"\n );\n return;\n }\n\n // Start worktree creation early (it's the slowest step: git checkout + npm install)\n // We'll await it later once we confirm there are routes to capture.\n logger.pipeline(2, \"Building import graph...\");\n const worktreePromise = createWorktree(base, cwd);\n\n const graph = await buildImportGraph(cwd);\n\n // 3. Find affected routes\n logger.pipeline(3, \"Finding affected routes...\");\n const changedPaths = impactfulFiles.map((f) => f.path);\n let affectedRoutes = findAffectedRoutes(changedPaths, graph, cwd, options.maxRoutes);\n const changedComponentFiles = impactfulFiles\n .filter((f) => f.category === \"component\")\n .map((f) => f.path);\n const routeComponentMap = mapRouteToChangedComponents(changedComponentFiles, graph);\n\n // Global config/style files affect every page but don't appear in the import graph\n if (affectedRoutes.length === 0) {\n const hasGlobalChanges = impactfulFiles.some((f) => isGlobalVisualFile(f.path));\n if (hasGlobalChanges) {\n const allRoutes: AffectedRoute[] = [];\n for (const file of graph.forward.keys()) {\n if (!isPageFile(file)) continue;\n const route = pagePathToRoute(file);\n if (route === null) continue;\n allRoutes.push({\n pagePath: file,\n route,\n reason: \"transitive\",\n depth: 0,\n triggerChain: [file],\n });\n }\n allRoutes.sort((a, b) => a.route.localeCompare(b.route));\n if (options.maxRoutes > 0 && allRoutes.length > options.maxRoutes) {\n affectedRoutes = allRoutes.slice(0, options.maxRoutes);\n } else {\n affectedRoutes = allRoutes;\n }\n }\n }\n\n if (affectedRoutes.length === 0) {\n // Clean up the worktree we started eagerly\n worktreePromise.then((w) => w.cleanup()).catch(() => {});\n logger.completePipeline();\n logger.success(\n \"No affected routes found. Changed files don't impact any pages.\"\n );\n return;\n }\n\n\n // 4. Wait for worktree\n logger.pipeline(4, \"Setting up worktree...\");\n const worktree = await worktreePromise;\n\n // 5. Start servers + browser in parallel\n logger.pipeline(5, \"Starting servers...\");\n await ensureDir(outputDir);\n\n const beforePort = await findAvailablePort();\n const afterPort = await findAvailablePort(new Set([beforePort]));\n\n const [beforeServer, afterServer, browser] = await Promise.all([\n startServer(worktree.path, beforePort),\n startServer(cwd, afterPort),\n launchBrowser(),\n ]);\n cleanupRegistry.register(() => stopServer(beforeServer));\n cleanupRegistry.register(() => stopServer(afterServer));\n cleanupRegistry.register(() => browser.close());\n\n // 6. Capture screenshots\n logger.pipeline(6, \"Capturing screenshots...\");\n const tasks = expandRoutes(affectedRoutes, config, routeComponentMap);\n const captures = await captureRoutes(\n tasks,\n beforeServer.url,\n afterServer.url,\n outputDir,\n {\n browser,\n width: options.width,\n height: options.height,\n device: options.device,\n delay: options.delay,\n autoTabs: options.autoTabs,\n maxTabsPerRoute: options.maxTabsPerRoute,\n autoSections: options.autoSections,\n maxSectionsPerRoute: options.maxSectionsPerRoute,\n onProgress: (i, label) =>\n logger.pipeline(6, `Capturing ${label} (${i}/${tasks.length})...`),\n }\n );\n\n // 7. Compare screenshots\n logger.pipeline(7, \"Comparing screenshots...\");\n const bgColor = detectBgColor(cwd);\n const allResults = await compareScreenshots(captures, outputDir, options.threshold, { bgColor });\n\n // Filter out unchanged sub-captures (tabs, sections, components)\n // Sub-captures have `~` in their prefix; main route captures are always kept\n const results = allResults.filter((r) => {\n const isSubCapture = r.prefix.includes(\"~\");\n if (isSubCapture && !r.changed) {\n // Clean up files for unchanged sub-captures\n try { unlinkSync(r.beforePath); } catch {}\n try { unlinkSync(r.afterPath); } catch {}\n try { unlinkSync(r.comparePath); } catch {}\n return false;\n }\n return true;\n });\n\n // 8. Generate report\n logger.pipeline(8, \"Generating report...\");\n await generateReport(results, outputDir, { post });\n\n // Print AI-friendly summary to stdout\n const summary = generateSummaryMd(results, gitDiff);\n logger.completePipeline(true);\n console.log(\"\\n\" + summary);\n\n // Final summary\n const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);\n const changedCount = results.filter((r) => r.changed).length;\n logger.success(\n `Done in ${elapsed}s \\u2014 ${results.length} route(s) captured, ${changedCount} with visual changes`\n );\n } finally {\n // Write debug log to output folder (best-effort)\n try { logger.writeLogFile(resolve(outputDir, \"debug.log\")); } catch {}\n await cleanupRegistry.runAll();\n }\n}\n","import { writeFileSync } from \"fs\";\nimport chalk from \"chalk\";\nimport ora, { type Ora } from \"ora\";\n\nconst BAR_WIDTH = 20;\nconst BAR_SPINNER = { interval: 80, frames: [\" \"] };\n\nexport class Logger {\n private spinner: Ora | null = null;\n private pipelineTotal = 0;\n private lastStep = 0;\n private lastLabel = \"\";\n private pipelineActive = false;\n private logBuffer: string[] = [];\n\n private log(level: string, message: string): void {\n const ts = new Date().toISOString();\n this.logBuffer.push(`${ts} [${level}] ${message}`);\n }\n\n info(message: string): void {\n this.log(\"info\", message);\n if (this.pipelineActive) return;\n if (this.spinner) {\n this.spinner.clear();\n console.log(chalk.blue(\"ℹ\"), message);\n this.spinner.render();\n } else {\n console.log(chalk.blue(\"ℹ\"), message);\n }\n }\n\n success(message: string): void {\n this.log(\"ok\", message);\n if (this.pipelineActive) return;\n if (this.spinner) {\n this.spinner.clear();\n console.log(chalk.green(\"✔\"), message);\n this.spinner.render();\n } else {\n console.log(chalk.green(\"✔\"), message);\n }\n }\n\n warn(message: string): void {\n this.log(\"warn\", message);\n if (this.spinner) {\n this.spinner.clear();\n console.log(chalk.yellow(\"⚠\"), message);\n this.spinner.render();\n } else {\n console.log(chalk.yellow(\"⚠\"), message);\n }\n }\n\n error(message: string): void {\n this.log(\"error\", message);\n if (this.spinner) {\n this.spinner.clear();\n console.error(chalk.red(\"✖\"), message);\n this.spinner.render();\n } else {\n console.error(chalk.red(\"✖\"), message);\n }\n }\n\n dim(message: string): void {\n this.log(\"debug\", message);\n if (this.pipelineActive) return;\n if (this.spinner) {\n this.spinner.clear();\n console.log(chalk.dim(message));\n this.spinner.render();\n } else {\n console.log(chalk.dim(message));\n }\n }\n\n spin(message: string): Ora {\n this.log(\"info\", message);\n this.clearSpinner();\n this.spinner = ora(message).start();\n return this.spinner;\n }\n\n stopSpinner(): void {\n this.clearSpinner();\n }\n\n startPipeline(total: number): void {\n this.pipelineTotal = total;\n this.lastStep = 0;\n this.lastLabel = \"\";\n this.pipelineActive = true;\n this.clearSpinner();\n\n this.log(\"info\", `Pipeline started (${total} steps)`);\n const text = this.renderPipeline(0, \"Starting...\");\n this.spinner = ora({\n text: chalk.dim(text),\n spinner: BAR_SPINNER,\n }).start();\n }\n\n pipeline(step: number, label: string): void {\n if (step === this.lastStep && label === this.lastLabel) return;\n this.lastStep = step;\n this.lastLabel = label;\n this.log(\"step\", `${step}/${this.pipelineTotal} ${label}`);\n\n const text = chalk.dim(this.renderPipeline(step, label));\n if (!this.spinner) {\n this.spinner = ora({\n text,\n spinner: BAR_SPINNER,\n }).start();\n return;\n }\n\n this.spinner.text = text;\n this.spinner.render();\n }\n\n completePipeline(finished = false): void {\n this.pipelineActive = false;\n this.log(\"info\", `Pipeline ${finished ? \"completed\" : \"stopped\"}`);\n if (this.spinner) {\n if (finished) {\n this.spinner.stop();\n const bar = \"\\u2588\".repeat(BAR_WIDTH);\n console.log(`${chalk.green(\"✔\")} ${bar}`);\n } else {\n this.spinner.stop();\n }\n this.spinner = null;\n } else if (finished) {\n const bar = \"\\u2588\".repeat(BAR_WIDTH);\n console.log(`${chalk.green(\"✔\")} ${bar}`);\n }\n this.pipelineTotal = 0;\n this.lastStep = 0;\n this.lastLabel = \"\";\n }\n\n writeLogFile(filePath: string): void {\n if (this.logBuffer.length === 0) return;\n writeFileSync(filePath, this.logBuffer.join(\"\\n\") + \"\\n\", \"utf-8\");\n }\n\n private renderPipeline(step: number, label: string): string {\n const total = this.pipelineTotal || 1;\n const clampedStep = Math.max(0, Math.min(step, total));\n const filled = Math.round((clampedStep / total) * BAR_WIDTH);\n const bar = \"\\u2588\".repeat(filled) + \"\\u2591\".repeat(BAR_WIDTH - filled);\n return ` ${bar} ${clampedStep}/${total} ${label}`;\n }\n\n private clearSpinner(): void {\n if (this.spinner) {\n this.spinner.stop();\n this.spinner = null;\n }\n }\n}\n\nexport const logger = new Logger();\n","import { logger } from \"./logger.js\";\n\ntype CleanupFn = () => Promise<void> | void;\n\nexport class CleanupRegistry {\n private cleanups: CleanupFn[] = [];\n private registered = false;\n\n register(fn: CleanupFn): void {\n this.cleanups.push(fn);\n this.ensureSignalHandlers();\n }\n\n async runAll(): Promise<void> {\n // LIFO order — tear down in reverse\n const fns = [...this.cleanups].reverse();\n this.cleanups = [];\n\n for (const fn of fns) {\n try {\n await fn();\n } catch (err) {\n logger.warn(\n `Cleanup failed: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n }\n\n private ensureSignalHandlers(): void {\n if (this.registered) return;\n this.registered = true;\n\n const handler = async (signal: string) => {\n logger.dim(`\\nReceived ${signal}, cleaning up...`);\n await this.runAll();\n process.exit(signal === \"SIGINT\" ? 130 : 143);\n };\n\n process.on(\"SIGINT\", () => handler(\"SIGINT\"));\n process.on(\"SIGTERM\", () => handler(\"SIGTERM\"));\n process.on(\"exit\", () => {\n // Synchronous fallback — can't await here but catches missed cleanups\n if (this.cleanups.length > 0) {\n for (const fn of [...this.cleanups].reverse()) {\n try {\n fn();\n } catch {\n // Best effort\n }\n }\n this.cleanups = [];\n }\n });\n }\n}\n\nexport const cleanupRegistry = new CleanupRegistry();\n","import { resolve } from \"path\";\nimport { existsSync, readFileSync } from \"fs\";\nimport { pathToFileURL } from \"url\";\nimport { logger } from \"./logger.js\";\nimport type { AfterbeforeConfig } from \"./types.js\";\n\nconst CONFIG_FILES = [\n \"afterbefore.config.json\",\n \"afterbefore.config.js\",\n \"afterbefore.config.mjs\",\n];\n\nexport async function loadConfig(\n cwd: string\n): Promise<AfterbeforeConfig | null> {\n for (const name of CONFIG_FILES) {\n const filePath = resolve(cwd, name);\n if (!existsSync(filePath)) continue;\n\n logger.dim(` Config: ${name}`);\n\n if (name.endsWith(\".json\")) {\n const raw = readFileSync(filePath, \"utf-8\");\n return JSON.parse(raw) as AfterbeforeConfig;\n }\n\n // .js / .mjs — dynamic import\n const mod = await import(pathToFileURL(filePath).href);\n return (mod.default ?? mod) as AfterbeforeConfig;\n }\n\n return null;\n}\n","import { mkdir } from \"fs/promises\";\n\nexport async function ensureDir(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true });\n}\n","import { createServer } from \"net\";\n\nfunction findPort(): Promise<number> {\n return new Promise((resolve, reject) => {\n const server = createServer();\n server.listen(0, () => {\n const addr = server.address();\n if (!addr || typeof addr === \"string\") {\n server.close();\n reject(new Error(\"Failed to get port\"));\n return;\n }\n const port = addr.port;\n server.close(() => resolve(port));\n });\n server.on(\"error\", reject);\n });\n}\n\nexport async function findAvailablePort(\n exclude?: Set<number>\n): Promise<number> {\n for (let i = 0; i < 5; i++) {\n const port = await findPort();\n if (!exclude || !exclude.has(port)) return port;\n }\n throw new Error(\"Failed to find available port after 5 attempts\");\n}\n","import { existsSync, readFileSync } from \"fs\";\nimport { resolve } from \"path\";\n\nconst DEFAULT_BG = \"#0a0a0a\";\n\nconst GLOBAL_CSS_PATHS = [\n \"app/globals.css\",\n \"src/app/globals.css\",\n \"styles/globals.css\",\n \"src/styles/globals.css\",\n \"app/global.css\",\n \"src/app/global.css\",\n];\n\n/**\n * Convert HSL values to hex color.\n * Handles \"0 0% 3.9%\" (space-separated HSL without hsl() wrapper, common in shadcn/Tailwind v4).\n */\nfunction hslToHex(h: number, s: number, l: number): string {\n s /= 100;\n l /= 100;\n const a = s * Math.min(l, 1 - l);\n const f = (n: number) => {\n const k = (n + h / 30) % 12;\n const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);\n return Math.round(255 * color)\n .toString(16)\n .padStart(2, \"0\");\n };\n return `#${f(0)}${f(8)}${f(4)}`;\n}\n\n/**\n * Try to parse a CSS value as a color and return a hex string.\n * Handles: hex (#fff, #ffffff), hsl(), bare HSL values (shadcn style), rgb().\n */\nfunction parseColorValue(raw: string): string | null {\n const v = raw.trim();\n\n // Already hex\n if (/^#[0-9a-fA-F]{3,8}$/.test(v)) {\n if (v.length === 4) {\n // #abc → #aabbcc\n return `#${v[1]}${v[1]}${v[2]}${v[2]}${v[3]}${v[3]}`;\n }\n return v.slice(0, 7); // strip alpha if present\n }\n\n // hsl(210, 50%, 10%) or hsl(210 50% 10%)\n const hslMatch = v.match(\n /^hsl\\(\\s*([\\d.]+)[,\\s]+\\s*([\\d.]+)%[,\\s]+\\s*([\\d.]+)%/\n );\n if (hslMatch) {\n return hslToHex(\n parseFloat(hslMatch[1]),\n parseFloat(hslMatch[2]),\n parseFloat(hslMatch[3])\n );\n }\n\n // Bare HSL: \"210 50% 10%\" (shadcn / Tailwind v4 style)\n const bareHsl = v.match(/^([\\d.]+)\\s+([\\d.]+)%\\s+([\\d.]+)%$/);\n if (bareHsl) {\n return hslToHex(\n parseFloat(bareHsl[1]),\n parseFloat(bareHsl[2]),\n parseFloat(bareHsl[3])\n );\n }\n\n // rgb(10, 10, 10) or rgb(10 10 10)\n const rgbMatch = v.match(\n /^rgb\\(\\s*([\\d.]+)[,\\s]+\\s*([\\d.]+)[,\\s]+\\s*([\\d.]+)/\n );\n if (rgbMatch) {\n const toHex = (n: string) =>\n Math.round(parseFloat(n)).toString(16).padStart(2, \"0\");\n return `#${toHex(rgbMatch[1])}${toHex(rgbMatch[2])}${toHex(rgbMatch[3])}`;\n }\n\n return null;\n}\n\n/**\n * Detect the project's background color from global CSS.\n *\n * Looks for:\n * 1. `--background: <value>` CSS variable in :root or .dark\n * 2. `background-color:` or `background:` on body/:root\n *\n * Falls back to #0a0a0a.\n */\nexport function detectBgColor(cwd: string): string {\n for (const relPath of GLOBAL_CSS_PATHS) {\n const absPath = resolve(cwd, relPath);\n if (!existsSync(absPath)) continue;\n\n const css = readFileSync(absPath, \"utf-8\");\n\n // Look for --background variable (prefer .dark block, then :root)\n // Match inside .dark { ... } block first\n const darkBlock = css.match(/\\.dark\\s*\\{([^}]+)\\}/);\n if (darkBlock) {\n const bgVar = darkBlock[1].match(/--background\\s*:\\s*([^;]+)/);\n if (bgVar) {\n const color = parseColorValue(bgVar[1]);\n if (color) return color;\n }\n }\n\n // Then :root block\n const rootBlock = css.match(/:root\\s*\\{([^}]+)\\}/);\n if (rootBlock) {\n const bgVar = rootBlock[1].match(/--background\\s*:\\s*([^;]+)/);\n if (bgVar) {\n const color = parseColorValue(bgVar[1]);\n if (color) return color;\n }\n }\n\n // Look for any --background definition anywhere in the file\n const anyBgVar = css.match(/--background\\s*:\\s*([^;]+)/);\n if (anyBgVar) {\n const color = parseColorValue(anyBgVar[1]);\n if (color) return color;\n }\n\n // Direct background on body\n const bodyBg = css.match(\n /body\\s*\\{[^}]*?background(?:-color)?\\s*:\\s*([^;]+)/\n );\n if (bodyBg) {\n const val = bodyBg[1].trim();\n // Skip var() references (we already checked the variable)\n if (!val.startsWith(\"var(\")) {\n const color = parseColorValue(val);\n if (color) return color;\n }\n }\n\n // Found a CSS file but couldn't extract a color — stop searching\n break;\n }\n\n return DEFAULT_BG;\n}\n","import { execSync } from \"child_process\";\n\nexport function git(args: string, cwd?: string): string {\n return execSync(`git ${args}`, {\n cwd,\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }).trim();\n}\n\nexport function isGitRepo(cwd?: string): boolean {\n try {\n git(\"rev-parse --is-inside-work-tree\", cwd);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function getMergeBase(base: string, cwd?: string): string {\n return git(`merge-base ${base} HEAD`, cwd);\n}\n\nexport function getDiffNameStatus(base: string, cwd?: string): string {\n const mergeBase = getMergeBase(base, cwd);\n return git(`diff --name-status ${mergeBase}`, cwd);\n}\n\nexport function getGitDiff(base: string, cwd?: string): string {\n const mergeBase = getMergeBase(base, cwd);\n return git(`diff ${mergeBase}`, cwd);\n}\n\nexport function getCurrentBranch(cwd?: string): string {\n return git(\"rev-parse --abbrev-ref HEAD\", cwd);\n}\n","import type { DiffFile, DiffStatus } from \"../types.js\";\nimport { getDiffNameStatus } from \"../utils/git.js\";\n\nconst VALID_STATUSES = new Set([\"A\", \"M\", \"D\", \"R\", \"C\"]);\n\n/**\n * Parse git diff --name-status output into structured DiffFile array.\n */\nexport function parseDiffOutput(raw: string): DiffFile[] {\n if (!raw.trim()) return [];\n\n const results: DiffFile[] = [];\n\n for (const line of raw.trim().split(\"\\n\")) {\n const parts = line.split(\"\\t\");\n const statusRaw = parts[0].charAt(0);\n\n if (!VALID_STATUSES.has(statusRaw)) continue;\n\n const status = statusRaw as DiffStatus;\n\n if (status === \"R\" || status === \"C\") {\n results.push({ status, oldPath: parts[1], path: parts[2] });\n } else {\n results.push({ status, path: parts[1] });\n }\n }\n\n return results;\n}\n\n/**\n * Get changed files between base and HEAD.\n */\nexport function getChangedFiles(base: string, cwd?: string): DiffFile[] {\n const raw = getDiffNameStatus(base, cwd);\n return parseDiffOutput(raw);\n}\n","import type { ClassifiedFile, DiffFile, FileCategory } from \"../types.js\";\n\n/** Files that affect every page globally (not connected via imports) */\nconst GLOBAL_FILES = new Set([\n \"tailwind.config.ts\",\n \"tailwind.config.js\",\n \"postcss.config.js\",\n \"postcss.config.mjs\",\n \"postcss.config.cjs\",\n]);\n\n/**\n * Check if a file is a global config/style file that visually affects all pages\n * but won't appear in the import graph.\n */\nexport function isGlobalVisualFile(filePath: string): boolean {\n const p = filePath.replace(/^src\\//, \"\");\n return GLOBAL_FILES.has(p) || /globals?\\.(css|scss)$/.test(p);\n}\n\n/** Categories that can affect visual output */\nconst VISUAL_CATEGORIES: Set<FileCategory> = new Set([\n \"page\",\n \"component\",\n \"style\",\n \"layout\",\n]);\n\n/**\n * Classify a file path into a category based on patterns.\n */\nexport function classifyFile(filePath: string): FileCategory {\n const p = filePath.replace(/^src\\//, \"\");\n\n // Test files\n if (\n /\\.(test|spec)\\.[tj]sx?$/.test(p) ||\n /^tests?\\//.test(p) ||\n /\\/__tests__\\//.test(p) ||\n p.includes(\".test.\") ||\n p.includes(\".spec.\")\n ) {\n return \"test\";\n }\n\n // Config files\n if (\n /^(tsconfig|next\\.config|tailwind\\.config|postcss\\.config|\\.eslint|\\.prettier|vitest\\.config|jest\\.config)/.test(\n p\n ) ||\n p === \"package.json\" ||\n p === \"package-lock.json\" ||\n p.endsWith(\".config.ts\") ||\n p.endsWith(\".config.js\") ||\n p.endsWith(\".config.mjs\")\n ) {\n return \"config\";\n }\n\n // Style files\n if (/\\.(css|scss|sass|less)$/.test(p) || p === \"tailwind.config.ts\") {\n return \"style\";\n }\n\n // Layout files (Next.js app router)\n if (/\\/layout\\.[tj]sx?$/.test(p) || /^app\\/layout\\.[tj]sx?$/.test(p)) {\n return \"layout\";\n }\n\n // Page files (Next.js app router)\n if (/\\/page\\.[tj]sx?$/.test(p) || /^app\\/page\\.[tj]sx?$/.test(p)) {\n return \"page\";\n }\n\n // Component files\n if (\n /^(components|app)\\//.test(p) &&\n /\\.[tj]sx?$/.test(p)\n ) {\n return \"component\";\n }\n\n // Utility files (lib, utils, hooks, etc.)\n if (\n /^(lib|utils|hooks|helpers|services|api)\\//.test(p) &&\n /\\.[tj]sx?$/.test(p)\n ) {\n return \"utility\";\n }\n\n // TypeScript/JavaScript files not in known dirs\n if (/\\.[tj]sx?$/.test(p)) {\n return \"component\";\n }\n\n return \"other\";\n}\n\n/**\n * Classify all diff files and return with categories.\n */\nexport function classifyFiles(files: DiffFile[]): ClassifiedFile[] {\n return files.map((f) => ({\n ...f,\n category: classifyFile(f.path),\n }));\n}\n\n/**\n * Filter to only visually relevant files.\n */\nexport function filterVisuallyRelevant(\n files: ClassifiedFile[]\n): ClassifiedFile[] {\n return files.filter((f) => VISUAL_CATEGORIES.has(f.category));\n}\n","import { readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join, relative } from \"node:path\";\nimport { init, parse } from \"es-module-lexer\";\nimport type { ImportGraph } from \"../types.js\";\nimport { logger } from \"../logger.js\";\nimport { createResolver } from \"./resolve.js\";\n\nconst SOURCE_EXTENSIONS = new Set([\".ts\", \".tsx\", \".js\", \".jsx\"]);\nconst SOURCE_DIRS = [\"app\", \"src\", \"components\", \"lib\"];\n\n/**\n * Recursively collect all source files from a directory.\n */\nfunction collectFiles(dir: string): string[] {\n const results: string[] = [];\n\n let entries;\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return results;\n }\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n // Skip node_modules, .next, dist, etc.\n if (entry.name.startsWith(\".\") || entry.name === \"node_modules\" || entry.name === \"dist\") {\n continue;\n }\n results.push(...collectFiles(fullPath));\n } else if (entry.isFile()) {\n const ext = entry.name.slice(entry.name.lastIndexOf(\".\"));\n if (SOURCE_EXTENSIONS.has(ext)) {\n results.push(fullPath);\n }\n }\n }\n\n return results;\n}\n\n/**\n * Parse imports from a file using es-module-lexer.\n */\nfunction parseImports(filePath: string): string[] {\n let source: string;\n try {\n source = readFileSync(filePath, \"utf-8\");\n } catch {\n return [];\n }\n\n try {\n // Strip TypeScript-specific syntax that es-module-lexer can't handle\n // Replace type imports/exports so the lexer can parse the rest\n const cleaned = source\n .replace(/import\\s+type\\s+/g, \"import \")\n .replace(/export\\s+type\\s+/g, \"export \");\n\n const [imports] = parse(cleaned);\n return imports\n .map((imp) => imp.n)\n .filter((n): n is string => n !== undefined && n !== \"\");\n } catch {\n // If parsing fails, fall back to regex-based extraction\n const specifiers: string[] = [];\n const importRegex = /(?:import|from)\\s+[\"']([^\"']+)[\"']/g;\n let match;\n while ((match = importRegex.exec(source)) !== null) {\n specifiers.push(match[1]);\n }\n return specifiers;\n }\n}\n\n/**\n * Build a bidirectional import graph for the project.\n */\nexport async function buildImportGraph(projectRoot: string): Promise<ImportGraph> {\n await init;\n\n const resolve = createResolver(projectRoot);\n\n // Collect all source files\n const allFiles: string[] = [];\n for (const dir of SOURCE_DIRS) {\n const fullDir = join(projectRoot, dir);\n allFiles.push(...collectFiles(fullDir));\n }\n\n // Deduplicate (src/ might contain app/, components/, lib/)\n const fileSet = new Set(allFiles);\n\n const forward = new Map<string, Set<string>>();\n const reverse = new Map<string, Set<string>>();\n\n for (const filePath of fileSet) {\n const relPath = relative(projectRoot, filePath);\n const specifiers = parseImports(filePath);\n\n const deps = new Set<string>();\n\n for (const spec of specifiers) {\n const resolved = resolve(spec, filePath);\n if (!resolved) continue;\n\n const relResolved = relative(projectRoot, resolved);\n deps.add(relResolved);\n\n // Add reverse edge\n if (!reverse.has(relResolved)) {\n reverse.set(relResolved, new Set());\n }\n reverse.get(relResolved)!.add(relPath);\n }\n\n forward.set(relPath, deps);\n }\n\n logger.dim(`Import graph: ${fileSet.size} files, ${countEdges(forward)} edges`);\n\n return { forward, reverse };\n}\n\nfunction countEdges(forward: Map<string, Set<string>>): number {\n let count = 0;\n for (const deps of forward.values()) {\n count += deps.size;\n }\n return count;\n}\n","import { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { resolve, dirname, join } from \"node:path\";\nimport { logger } from \"../logger.js\";\n\ninterface PathMapping {\n prefix: string;\n targets: string[];\n}\n\nconst EXTENSIONS = [\".ts\", \".tsx\", \".js\", \".jsx\"];\n\n/**\n * Create an import specifier resolver for a project using its tsconfig.json paths.\n */\nexport function createResolver(\n projectRoot: string\n): (specifier: string, fromFile: string) => string | null {\n const mappings = loadPathMappings(projectRoot);\n const existsCache = new Map<string, boolean>();\n\n function cachedExists(p: string): boolean {\n const cached = existsCache.get(p);\n if (cached !== undefined) return cached;\n const result = existsSync(p);\n existsCache.set(p, result);\n return result;\n }\n\n function tryResolve(candidate: string): string | null {\n // Exact file\n if (cachedExists(candidate) && !isDirectory(candidate)) return candidate;\n\n // Try extensions\n for (const ext of EXTENSIONS) {\n const withExt = candidate + ext;\n if (cachedExists(withExt)) return withExt;\n }\n\n // Try index files\n for (const ext of EXTENSIONS) {\n const indexFile = join(candidate, `index${ext}`);\n if (cachedExists(indexFile)) return indexFile;\n }\n\n return null;\n }\n\n function isDirectory(p: string): boolean {\n try {\n return statSync(p).isDirectory();\n } catch {\n return false;\n }\n }\n\n return (specifier: string, fromFile: string): string | null => {\n // Relative imports\n if (specifier.startsWith(\".\")) {\n const dir = dirname(fromFile);\n const candidate = resolve(dir, specifier);\n return tryResolve(candidate);\n }\n\n // Try each path mapping\n for (const mapping of mappings) {\n if (!specifier.startsWith(mapping.prefix)) continue;\n\n const rest = specifier.slice(mapping.prefix.length);\n\n for (const target of mapping.targets) {\n const candidate = resolve(projectRoot, target + rest);\n const result = tryResolve(candidate);\n if (result) return result;\n }\n }\n\n // Not a relative or aliased import (node_module etc.)\n return null;\n };\n}\n\nfunction loadPathMappings(projectRoot: string): PathMapping[] {\n const tsconfigPath = join(projectRoot, \"tsconfig.json\");\n if (!existsSync(tsconfigPath)) {\n logger.dim(\"No tsconfig.json found, skipping path alias resolution\");\n return [];\n }\n\n try {\n const raw = readFileSync(tsconfigPath, \"utf-8\");\n const cleaned = stripJsonComments(raw);\n const config = JSON.parse(cleaned);\n\n const paths = config?.compilerOptions?.paths;\n if (!paths) return [];\n\n const baseUrl = config?.compilerOptions?.baseUrl || \".\";\n const mappings: PathMapping[] = [];\n\n for (const [pattern, targets] of Object.entries(paths)) {\n // Convert \"@ /*\" → prefix \"@/\"\n const prefix = pattern.replace(/\\*$/, \"\");\n const resolvedTargets = (targets as string[]).map((t) =>\n t.replace(/\\*$/, \"\")\n );\n // Prepend baseUrl to targets\n const absoluteTargets = resolvedTargets.map((t) =>\n join(baseUrl, t)\n );\n mappings.push({ prefix, targets: absoluteTargets });\n }\n\n return mappings;\n } catch (e) {\n logger.warn(`Failed to parse tsconfig.json: ${e}`);\n return [];\n }\n}\n\n/**\n * Strip JSON comments (// and /* */) and trailing commas while respecting string boundaries.\n * tsconfig.json is JSONC — standard JSON.parse chokes on comments and trailing commas.\n */\nfunction stripJsonComments(input: string): string {\n let result = \"\";\n let i = 0;\n const len = input.length;\n\n while (i < len) {\n const ch = input[i];\n\n // String literal — copy verbatim, including any /* or // inside\n if (ch === '\"') {\n let j = i + 1;\n while (j < len) {\n if (input[j] === \"\\\\\") {\n j += 2; // skip escaped char\n } else if (input[j] === '\"') {\n j++;\n break;\n } else {\n j++;\n }\n }\n result += input.slice(i, j);\n i = j;\n continue;\n }\n\n // Line comment\n if (ch === \"/\" && input[i + 1] === \"/\") {\n i += 2;\n while (i < len && input[i] !== \"\\n\") i++;\n continue;\n }\n\n // Block comment\n if (ch === \"/\" && input[i + 1] === \"*\") {\n i += 2;\n while (i < len && !(input[i] === \"*\" && input[i + 1] === \"/\")) i++;\n i += 2; // skip closing */\n continue;\n }\n\n // Trailing comma before } or ]\n if (ch === \",\") {\n let j = i + 1;\n while (j < len && /\\s/.test(input[j])) j++;\n if (input[j] === \"}\" || input[j] === \"]\") {\n i = j; // skip comma, land on closing bracket\n continue;\n }\n }\n\n result += ch;\n i++;\n }\n\n return result;\n}\n","import { logger } from \"../logger.js\";\n\n/**\n * Convert a Next.js app router page path to a URL route.\n * e.g. \"app/about/page.tsx\" → \"/about\"\n * \"app/(marketing)/pricing/page.tsx\" → \"/pricing\"\n * \"app/page.tsx\" → \"/\"\n */\nexport function pagePathToRoute(pagePath: string): string | null {\n // Strip leading src/ if present\n let p = pagePath.replace(/^src\\//, \"\");\n\n // Must be under app/ and end with page.tsx/jsx/ts/js\n const match = p.match(/^app\\/(.*)\\/page\\.[tj]sx?$/);\n if (!match) {\n // Root page\n if (/^app\\/page\\.[tj]sx?$/.test(p)) return \"/\";\n return null;\n }\n\n let route = match[1];\n\n // Check for dynamic segments\n if (/\\[.*\\]/.test(route)) {\n logger.warn(`Skipping dynamic route: /${route} (dynamic segments not supported)`);\n return null;\n }\n\n // Strip route groups like (marketing)\n route = route\n .split(\"/\")\n .filter((seg) => !seg.startsWith(\"(\"))\n .join(\"/\");\n\n return `/${route}` || \"/\";\n}\n\n/**\n * Check if a file path is a Next.js page file.\n */\nexport function isPageFile(filePath: string): boolean {\n const p = filePath.replace(/^src\\//, \"\");\n return /^app\\/(.+\\/)?page\\.[tj]sx?$/.test(p);\n}\n\n/**\n * Check if a file path is a Next.js layout file.\n */\nexport function isLayoutFile(filePath: string): boolean {\n const p = filePath.replace(/^src\\//, \"\");\n return /^app\\/(.+\\/)?layout\\.[tj]sx?$/.test(p);\n}\n\n/**\n * Get the directory a layout file covers.\n * e.g. \"app/about/layout.tsx\" → \"app/about/\"\n * \"app/layout.tsx\" → \"app/\"\n */\nexport function getLayoutDir(filePath: string): string {\n const p = filePath.replace(/^src\\//, \"\");\n const match = p.match(/^(app\\/.*\\/)layout\\.[tj]sx?$/);\n if (!match) return \"app/\"; // root layout\n return match[1];\n}\n","/**\n * Normalize a file path: forward slashes, strip leading ./\n */\nexport function normalizePath(filePath: string): string {\n return filePath.replace(/\\\\/g, \"/\").replace(/^\\.\\//, \"\");\n}\n\n/**\n * Convert a label to a filename-safe slug.\n * \"Components & Hooks\" -> \"components-hooks\"\n */\nexport function sanitizeLabel(label: string, maxLength: number = 40): string {\n return label\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\")\n .slice(0, maxLength);\n}\n","import type { AffectedRoute, ImportGraph } from \"../types.js\";\nimport {\n isPageFile,\n isLayoutFile,\n getLayoutDir,\n pagePathToRoute,\n} from \"../utils/nextjs.js\";\nimport { normalizePath } from \"../utils/path.js\";\nimport { logger } from \"../logger.js\";\n\nconst MAX_DEPTH = 3;\n\n/**\n * BFS up the reverse import graph to find affected page routes.\n * Also handles layout files — a layout change affects all pages\n * in the same directory and its descendants.\n *\n * Tracks the trigger chain (source file → ... → page) for each route.\n */\nexport function findAffectedRoutes(\n changedFiles: string[],\n graph: ImportGraph,\n projectRoot: string,\n maxRoutes: number = 0\n): AffectedRoute[] {\n const routeMap = new Map<string, AffectedRoute>();\n\n for (const file of changedFiles) {\n // BFS from this changed file up the reverse graph.\n // Track the path from source to each discovered node.\n const visited = new Set<string>();\n const queue: Array<{ path: string; depth: number; chain: string[] }> = [\n { path: file, depth: 0, chain: [file] },\n ];\n visited.add(file);\n\n while (queue.length > 0) {\n const { path, depth, chain } = queue.shift()!;\n\n // Check if this is a page file\n if (isPageFile(path)) {\n const route = pagePathToRoute(path);\n if (route !== null && !routeMap.has(route)) {\n routeMap.set(route, {\n pagePath: path,\n route,\n reason: depth === 0 ? \"direct\" : \"transitive\",\n depth,\n triggerChain: chain,\n });\n }\n }\n\n // Don't BFS beyond max depth\n if (depth >= MAX_DEPTH) continue;\n\n // Walk reverse edges\n const importers = graph.reverse.get(path);\n if (!importers) continue;\n\n for (const importer of importers) {\n if (visited.has(importer)) continue;\n visited.add(importer);\n queue.push({ path: importer, depth: depth + 1, chain: [...chain, importer] });\n }\n }\n }\n\n // Handle layout files: a layout wraps all pages in its directory subtree\n // via Next.js filesystem convention (no import required)\n for (const file of changedFiles) {\n if (!isLayoutFile(file)) continue;\n const layoutDir = getLayoutDir(file);\n\n for (const knownFile of graph.forward.keys()) {\n if (knownFile.startsWith(layoutDir) && isPageFile(knownFile)) {\n const route = pagePathToRoute(knownFile);\n if (route !== null && !routeMap.has(route)) {\n routeMap.set(route, {\n pagePath: knownFile,\n route,\n reason: \"transitive\",\n depth: 1,\n triggerChain: [file, knownFile],\n });\n }\n }\n }\n }\n\n const routes = Array.from(routeMap.values()).sort(\n (a, b) => a.depth - b.depth\n );\n\n if (maxRoutes > 0 && routes.length > maxRoutes) {\n const skipped = routes.length - maxRoutes;\n logger.warn(\n `Limiting to ${maxRoutes} route(s), skipping ${skipped} deeper route(s). Use --max-routes 0 for unlimited.`\n );\n const limited = routes.slice(0, maxRoutes);\n logger.dim(`Found ${limited.length} affected route(s) (of ${routes.length} total)`);\n return limited;\n }\n\n logger.dim(`Found ${routes.length} affected route(s)`);\n return routes;\n}\n\n/**\n * Find routes reachable from a single file via reverse import graph BFS.\n */\nexport function findRoutesForFile(\n file: string,\n graph: ImportGraph\n): string[] {\n const routes = new Set<string>();\n const start = normalizePath(file);\n const queue: Array<{ path: string; depth: number }> = [{ path: start, depth: 0 }];\n const visited = new Set<string>([start]);\n\n while (queue.length > 0) {\n const { path, depth } = queue.shift()!;\n if (isPageFile(path)) {\n const route = pagePathToRoute(path);\n if (route) routes.add(route);\n }\n if (depth >= MAX_DEPTH) continue;\n const importers = graph.reverse.get(path);\n if (!importers) continue;\n for (const importer of importers) {\n if (visited.has(importer)) continue;\n visited.add(importer);\n queue.push({ path: importer, depth: depth + 1 });\n }\n }\n return Array.from(routes);\n}\n","import { exec as execCb, execSync } from \"child_process\";\nimport { promisify } from \"util\";\nimport { rm, mkdtemp } from \"fs/promises\";\nimport { join } from \"path\";\nimport { tmpdir } from \"os\";\nimport { logger } from \"../logger.js\";\nimport { AfterbeforeError } from \"../errors.js\";\nimport { cleanupRegistry } from \"../cleanup.js\";\nimport { detectPackageManager } from \"../utils/pm.js\";\nimport type { WorktreeInfo } from \"../types.js\";\n\nconst exec = promisify(execCb);\n\nexport async function createWorktree(\n base: string,\n cwd: string\n): Promise<WorktreeInfo> {\n const worktreeDir = await mkdtemp(join(tmpdir(), \"afterbefore-wt-\"));\n\n logger.dim(`Creating worktree for ${base} at ${worktreeDir}`);\n try {\n await exec(`git worktree add \"${worktreeDir}\" \"${base}\"`, { cwd });\n } catch (err) {\n throw new AfterbeforeError(\n `Failed to create worktree for ref \"${base}\".`,\n `Make sure the branch/ref \"${base}\" exists. Run \"git branch -a\" to see available refs.`\n );\n }\n\n const pm = detectPackageManager(cwd);\n logger.dim(`Installing dependencies with ${pm}`);\n await exec(`${pm} install`, { cwd: worktreeDir });\n\n const cleanup = async () => {\n logger.dim(`Cleaning up worktree at ${worktreeDir}`);\n try {\n execSync(`git worktree remove --force \"${worktreeDir}\"`, {\n cwd,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n } catch {\n try {\n await rm(worktreeDir, { recursive: true, force: true });\n execSync(\"git worktree prune\", {\n cwd,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n } catch {\n logger.warn(`Failed to clean up worktree at ${worktreeDir}`);\n }\n }\n };\n\n cleanupRegistry.register(cleanup);\n\n return { path: worktreeDir, ref: base, cleanup };\n}\n","export class AfterbeforeError extends Error {\n constructor(\n message: string,\n public readonly suggestion: string\n ) {\n super(message);\n this.name = \"AfterbeforeError\";\n }\n}\n","import { existsSync } from \"fs\";\nimport { join } from \"path\";\n\nexport type PackageManager = \"npm\" | \"pnpm\" | \"yarn\" | \"bun\";\n\nexport function detectPackageManager(dir: string): PackageManager {\n if (existsSync(join(dir, \"bun.lockb\")) || existsSync(join(dir, \"bun.lock\")))\n return \"bun\";\n if (existsSync(join(dir, \"pnpm-lock.yaml\"))) return \"pnpm\";\n if (existsSync(join(dir, \"yarn.lock\"))) return \"yarn\";\n return \"npm\";\n}\n\n/** Get the exec command for running a package binary (like `next dev`) */\nexport function pmExec(pm: PackageManager): string {\n switch (pm) {\n case \"bun\":\n return \"bunx\";\n case \"pnpm\":\n return \"pnpm exec\";\n case \"yarn\":\n return \"yarn\";\n default:\n return \"npx\";\n }\n}\n","import { spawn } from \"child_process\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { logger } from \"../logger.js\";\nimport { AfterbeforeError } from \"../errors.js\";\nimport { detectPackageManager, pmExec } from \"../utils/pm.js\";\nimport type { ServerInfo } from \"../types.js\";\n\nfunction waitForServer(url: string, timeoutMs: number): Promise<void> {\n const start = Date.now();\n\n return new Promise((resolve, reject) => {\n const poll = async () => {\n if (Date.now() - start > timeoutMs) {\n reject(\n new AfterbeforeError(\n `Server at ${url} did not respond within ${timeoutMs / 1000}s.`,\n `Try running \"next dev\" manually to check for errors. Also check if port ${url.split(\":\").pop()} is already in use.`\n )\n );\n return;\n }\n\n try {\n await fetch(url);\n resolve();\n } catch {\n setTimeout(poll, 150);\n }\n };\n\n poll();\n });\n}\n\nexport async function startServer(\n projectDir: string,\n port: number\n): Promise<ServerInfo> {\n const url = `http://localhost:${port}`;\n const pm = detectPackageManager(projectDir);\n const exec = pmExec(pm);\n const [cmd, ...baseArgs] = exec.split(\" \");\n\n // Check for stale .next/dev/lock — another dev server may be running\n const lockFile = join(projectDir, \".next\", \"dev\", \"lock\");\n if (existsSync(lockFile)) {\n throw new AfterbeforeError(\n `Another Next.js dev server is running in ${projectDir} (.next/dev/lock exists).`,\n `Stop the other dev server first, or delete .next/dev/lock if it's stale.`\n );\n }\n\n logger.info(`Starting Next.js dev server on ${url} (using ${pm})`);\n\n const child = spawn(cmd, [...baseArgs, \"next\", \"dev\", \"-p\", String(port)], {\n cwd: projectDir,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n detached: true,\n });\n\n // Log stderr for debugging but don't fail on it\n child.stderr?.on(\"data\", (data: Buffer) => {\n const msg = data.toString().trim();\n if (msg) logger.dim(`[next:${port}] ${msg}`);\n });\n\n await waitForServer(url, 60_000);\n logger.success(`Server ready at ${url}`);\n\n return { port, process: child, url };\n}\n\nexport async function stopServer(server: ServerInfo): Promise<void> {\n logger.dim(`Stopping server on port ${server.port}`);\n const pid = server.process.pid;\n\n // Kill the entire process group (spawned with detached: true) so that\n // grandchild processes like `next dev` don't survive as zombies.\n try {\n process.kill(-pid!, \"SIGTERM\");\n } catch {\n // Process group may already be dead\n }\n\n // Wait briefly for graceful shutdown\n await new Promise<void>((resolve) => {\n const timeout = setTimeout(() => {\n try {\n process.kill(-pid!, \"SIGKILL\");\n } catch {\n // Already dead\n }\n resolve();\n }, 5_000);\n\n server.process.on(\"exit\", () => {\n clearTimeout(timeout);\n resolve();\n });\n });\n}\n","import { join } from \"path\";\nimport { execSync } from \"child_process\";\nimport { chromium, devices, type Browser, type BrowserContext, type Page } from \"playwright\";\nimport { logger } from \"../logger.js\";\nimport type { CaptureResult, CaptureOptions, CaptureTask, ScenarioAction } from \"../types.js\";\nimport { detectTabs, sanitizeTabLabel } from \"../utils/tabs.js\";\nimport { detectSections, tagSectionOnPage, cleanupSectionTags, sanitizeSectionLabel, hideSectionHeading, showSectionHeading } from \"../utils/sections.js\";\nimport { normalizePath, sanitizeLabel } from \"../utils/path.js\";\n\nexport async function launchBrowser(): Promise<Browser> {\n try {\n return await chromium.launch();\n } catch {\n logger.dim(\"Chromium not found, installing...\");\n execSync(\"npx playwright install chromium\", {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n return await chromium.launch();\n }\n}\n\ninterface TaggedComponentInstance {\n source: string;\n name: string;\n index: number;\n componentKey: string;\n parentKey: string;\n}\n\nconst MAX_COMPONENT_INSTANCES_PER_SOURCE = 5;\n\nfunction sanitizeComponentLabel(label: string): string {\n const noExt = label.replace(/\\.[a-z0-9]+$/i, \"\");\n return sanitizeLabel(noExt, 60);\n}\n\nfunction groupBySource(\n instances: TaggedComponentInstance[]\n): Map<string, TaggedComponentInstance[]> {\n const map = new Map<string, TaggedComponentInstance[]>();\n for (const instance of instances) {\n const list = map.get(instance.source) ?? [];\n list.push(instance);\n map.set(instance.source, list);\n }\n for (const [source, list] of map.entries()) {\n list.sort((a, b) => a.index - b.index);\n map.set(source, list);\n }\n return map;\n}\n\nasync function captureByAttr(\n page: Page,\n attrName: string,\n attrValue: string,\n outPath: string\n): Promise<boolean> {\n try {\n const locator = page.locator(`[${attrName}=\"${attrValue}\"]`).first();\n if ((await locator.count()) === 0) return false;\n await locator.screenshot({ path: outPath });\n return true;\n } catch {\n return false;\n }\n}\n\nasync function tagChangedComponentInstances(\n page: Page,\n changedComponents: string[],\n maxPerSource: number = MAX_COMPONENT_INSTANCES_PER_SOURCE\n): Promise<TaggedComponentInstance[]> {\n const normalized = changedComponents.map(normalizePath);\n\n return page.evaluate(\n ({ changed, maxPerSource }) => {\n type Target = { original: string; variants: string[] };\n type Candidate = {\n el: Element;\n parent: Element;\n name: string;\n top: number;\n left: number;\n };\n type Tagged = {\n source: string;\n name: string;\n index: number;\n componentKey: string;\n parentKey: string;\n };\n\n const normalize = (p: string): string =>\n p.replace(/\\\\/g, \"/\").replace(/^\\.\\//, \"\");\n\n const targets: Target[] = changed.map((original) => {\n const norm = normalize(original);\n const noSrc = norm.replace(/^src\\//, \"\");\n return {\n original: norm,\n variants: Array.from(new Set([norm, noSrc, `src/${noSrc}`])),\n };\n });\n\n // Cleanup from previous runs on this page\n for (const el of document.querySelectorAll(\"[data-ab-comp-key]\")) {\n el.removeAttribute(\"data-ab-comp-key\");\n }\n for (const el of document.querySelectorAll(\"[data-ab-parent-key]\")) {\n el.removeAttribute(\"data-ab-parent-key\");\n }\n\n const getFiber = (el: Element): any => {\n const anyEl = el as any;\n for (const key in anyEl) {\n if (\n key.startsWith(\"__reactFiber$\") ||\n key.startsWith(\"__reactInternalInstance$\")\n ) {\n return anyEl[key];\n }\n }\n return null;\n };\n\n const matchSource = (rawSource: string): string | null => {\n const normalizedSource = normalize(rawSource);\n for (const target of targets) {\n for (const variant of target.variants) {\n if (\n normalizedSource === variant ||\n normalizedSource.endsWith(`/${variant}`)\n ) {\n return target.original;\n }\n }\n }\n return null;\n };\n\n const getComponentMatch = (\n el: Element\n ): { source: string; name: string } | null => {\n const fiber = getFiber(el);\n if (!fiber) return null;\n\n let current = fiber;\n while (current) {\n const sourceFile =\n current?._debugSource?.fileName ??\n current?.elementType?._debugSource?.fileName ??\n current?.type?._debugSource?.fileName;\n if (typeof sourceFile === \"string\") {\n const source = matchSource(sourceFile);\n if (source) {\n const type = current?.elementType ?? current?.type;\n const name =\n (typeof type === \"function\" &&\n (type.displayName || type.name)) ||\n (typeof type === \"string\" ? type : undefined) ||\n source.split(\"/\").pop() ||\n source;\n return { source, name };\n }\n }\n current = current.return;\n }\n\n return null;\n };\n\n const pickParentContainer = (el: Element): Element => {\n const ownRect = el.getBoundingClientRect();\n let parent: Element | null = el.parentElement;\n\n while (parent && parent !== document.body) {\n const rect = parent.getBoundingClientRect();\n if (\n rect.width >= ownRect.width * 1.15 ||\n rect.height >= ownRect.height * 1.15\n ) {\n return parent;\n }\n parent = parent.parentElement;\n }\n\n return el.parentElement ?? el;\n };\n\n const bySource = new Map<string, Candidate[]>();\n const elements = Array.from(document.querySelectorAll(\"body *\"));\n\n for (const el of elements) {\n const rect = el.getBoundingClientRect();\n if (rect.width < 4 || rect.height < 4) continue;\n if (rect.bottom < 0 || rect.right < 0) continue;\n\n const match = getComponentMatch(el);\n if (!match) continue;\n\n const parent = el.parentElement;\n if (parent) {\n const parentMatch = getComponentMatch(parent);\n if (parentMatch && parentMatch.source === match.source) {\n continue;\n }\n }\n\n const list = bySource.get(match.source) ?? [];\n list.push({\n el,\n parent: pickParentContainer(el),\n name: match.name,\n top: rect.top + window.scrollY,\n left: rect.left + window.scrollX,\n });\n bySource.set(match.source, list);\n }\n\n // Fallback: if fiber detection found nothing for a target, try heading match\n for (const target of targets) {\n if (bySource.has(target.original) && (bySource.get(target.original)!).length > 0) continue;\n\n const fileName = target.original.split(\"/\").pop() ?? \"\";\n const componentName = fileName.replace(/\\.[a-z0-9]+$/i, \"\");\n if (!componentName || componentName.length < 2) continue;\n\n const pattern = new RegExp(\"\\\\b\" + componentName + \"\\\\b\", \"i\");\n const headings = Array.from(document.querySelectorAll(\"h1, h2, h3, h4\"));\n\n for (const heading of headings) {\n const text = (heading.textContent ?? \"\").trim();\n if (!pattern.test(text)) continue;\n\n // Walk up to find section container (same pattern as sections.ts)\n let container: Element = heading;\n const headingTag = heading.tagName.toLowerCase();\n let walkParent: Element | null = heading.parentElement;\n\n while (walkParent && walkParent !== document.body) {\n const parentTag = walkParent.tagName.toLowerCase();\n if (parentTag === \"section\" || parentTag === \"article\") { container = walkParent; break; }\n if (walkParent.getAttribute(\"role\") === \"region\") { container = walkParent; break; }\n if (parentTag === \"body\" || parentTag === \"main\") break;\n const siblingHeadings = walkParent.querySelectorAll(`:scope > * ${headingTag}, :scope > ${headingTag}`);\n if (siblingHeadings.length > 1) break;\n container = walkParent;\n walkParent = walkParent.parentElement;\n }\n\n const rect = container.getBoundingClientRect();\n if (rect.width < 4 || rect.height < 4) continue;\n\n const list = bySource.get(target.original) ?? [];\n list.push({\n el: container,\n parent: pickParentContainer(container),\n name: componentName,\n top: rect.top + window.scrollY,\n left: rect.left + window.scrollX,\n });\n bySource.set(target.original, list);\n break; // Take first heading match per component\n }\n }\n\n const tagged: Tagged[] = [];\n\n for (let sourceIndex = 0; sourceIndex < targets.length; sourceIndex++) {\n const source = targets[sourceIndex].original;\n const list = bySource.get(source) ?? [];\n list.sort((a, b) => (a.top === b.top ? a.left - b.left : a.top - b.top));\n\n // Dimension-based dedup: keep first instance per distinct size (±2px)\n const deduped: Candidate[] = [];\n const seenDims: { w: number; h: number }[] = [];\n for (const candidate of list) {\n const rect = candidate.el.getBoundingClientRect();\n const isDuplicate = seenDims.some(\n (d) => Math.abs(d.w - rect.width) <= 2 && Math.abs(d.h - rect.height) <= 2\n );\n if (!isDuplicate) {\n seenDims.push({ w: rect.width, h: rect.height });\n deduped.push(candidate);\n }\n }\n\n const limit = Math.min(deduped.length, maxPerSource);\n for (let i = 0; i < limit; i++) {\n const instance = deduped[i];\n const componentKey = `ab-comp-${sourceIndex}-${i}`;\n const parentKey = `ab-parent-${sourceIndex}-${i}`;\n\n instance.el.setAttribute(\"data-ab-comp-key\", componentKey);\n instance.parent.setAttribute(\"data-ab-parent-key\", parentKey);\n\n tagged.push({\n source,\n name: instance.name,\n index: i,\n componentKey,\n parentKey,\n });\n }\n }\n\n return tagged;\n },\n { changed: normalized, maxPerSource }\n );\n}\n\nasync function captureComponentInstances(\n afterPage: Page,\n beforePage: Page,\n changedComponents: string[],\n capturePrefix: string,\n captureLabel: string,\n outputDir: string,\n results: CaptureResult[]\n): Promise<void> {\n const deduped = Array.from(new Set(changedComponents.map(normalizePath)));\n const [afterInstances, beforeInstances] = await Promise.all([\n tagChangedComponentInstances(afterPage, deduped),\n tagChangedComponentInstances(beforePage, deduped),\n ]);\n\n logger.dim(` Component detection on ${captureLabel}: ${deduped.length} source(s), ${afterInstances.length} after / ${beforeInstances.length} before instance(s)`);\n\n const afterBySource = groupBySource(afterInstances);\n const beforeBySource = groupBySource(beforeInstances);\n\n for (const source of deduped) {\n const afterList = afterBySource.get(source) ?? [];\n const beforeList = beforeBySource.get(source) ?? [];\n const pairCount = Math.min(afterList.length, beforeList.length);\n if (pairCount === 0) {\n if (afterList.length > 0 || beforeList.length > 0) {\n logger.dim(` ${source}: ${afterList.length} after / ${beforeList.length} before (skipping unpaired)`);\n }\n continue;\n }\n\n for (let pairIndex = 0; pairIndex < pairCount; pairIndex++) {\n const afterInstance = afterList[pairIndex];\n const beforeInstance = beforeList[pairIndex];\n const sourceSlug = sanitizeComponentLabel(source) || \"component\";\n const itemSlug = `${sourceSlug}-${pairIndex + 1}`;\n const componentName =\n afterInstance.name || beforeInstance.name || source.split(\"/\").pop() || source;\n const baseLabel = `${captureLabel} [${componentName} #${pairIndex + 1}]`;\n\n const parentPrefix = `${capturePrefix}~cmp.${itemSlug}~parent`;\n const parentBeforePath = join(outputDir, `${parentPrefix}-before.png`);\n const parentAfterPath = join(outputDir, `${parentPrefix}-after.png`);\n const [parentBeforeOk, parentAfterOk] = await Promise.all([\n captureByAttr(\n beforePage,\n \"data-ab-parent-key\",\n beforeInstance.parentKey,\n parentBeforePath\n ),\n captureByAttr(\n afterPage,\n \"data-ab-parent-key\",\n afterInstance.parentKey,\n parentAfterPath\n ),\n ]);\n if (parentBeforeOk && parentAfterOk) {\n results.push({\n route: `${baseLabel} [parent]`,\n prefix: parentPrefix,\n beforePath: parentBeforePath,\n afterPath: parentAfterPath,\n level: \"parent\",\n parentPrefix: capturePrefix,\n componentSource: source,\n componentName,\n });\n }\n\n const componentPrefix = `${capturePrefix}~cmp.${itemSlug}~component`;\n const componentBeforePath = join(outputDir, `${componentPrefix}-before.png`);\n const componentAfterPath = join(outputDir, `${componentPrefix}-after.png`);\n const [componentBeforeOk, componentAfterOk] = await Promise.all([\n captureByAttr(\n beforePage,\n \"data-ab-comp-key\",\n beforeInstance.componentKey,\n componentBeforePath\n ),\n captureByAttr(\n afterPage,\n \"data-ab-comp-key\",\n afterInstance.componentKey,\n componentAfterPath\n ),\n ]);\n if (componentBeforeOk && componentAfterOk) {\n results.push({\n route: `${baseLabel} [component]`,\n prefix: componentPrefix,\n beforePath: componentBeforePath,\n afterPath: componentAfterPath,\n level: \"component\",\n parentPrefix: capturePrefix,\n componentSource: source,\n componentName,\n });\n }\n\n }\n }\n}\n\nasync function captureAutoSections(\n afterPage: Page,\n beforePage: Page,\n parentPrefix: string,\n parentLabel: string,\n outputDir: string,\n options: CaptureOptions,\n settle: (page: Page) => Promise<void>,\n results: CaptureResult[]\n): Promise<void> {\n const sections = await detectSections(afterPage, options.maxSectionsPerRoute);\n if (sections.length === 0) return;\n\n const usedSlugs = new Set<string>();\n\n for (const section of sections) {\n let slug = sanitizeSectionLabel(section.label);\n if (!slug) continue;\n\n // Handle duplicate slugs\n if (usedSlugs.has(slug)) {\n let suffix = 2;\n while (usedSlugs.has(`${slug}-${suffix}`)) suffix++;\n slug = `${slug}-${suffix}`;\n }\n usedSlugs.add(slug);\n\n const sectionPrefix = `${parentPrefix}~s.${slug}`;\n const sectionLabel = `${parentLabel} [${section.label}]`;\n const sectionAfterPath = join(outputDir, `${sectionPrefix}-after.png`);\n const sectionBeforePath = join(outputDir, `${sectionPrefix}-before.png`);\n\n try {\n // Hide heading so section screenshot shows only the component content\n await hideSectionHeading(afterPage, section.index);\n\n // Screenshot section on after page\n const afterEl = afterPage.locator(`[data-ab-section=\"${section.index}\"]`).first();\n await afterEl.screenshot({ path: sectionAfterPath });\n\n await showSectionHeading(afterPage, section.index);\n\n // Try to find and tag matching section on before page\n const found = await tagSectionOnPage(beforePage, section.label, section.index);\n if (found) {\n await hideSectionHeading(beforePage, section.index);\n\n const beforeEl = beforePage.locator(`[data-ab-section=\"${section.index}\"]`).first();\n await beforeEl.screenshot({ path: sectionBeforePath });\n\n await showSectionHeading(beforePage, section.index);\n } else {\n // Section is new — skip (full-page already shows it)\n continue;\n }\n\n results.push({\n route: sectionLabel,\n prefix: sectionPrefix,\n beforePath: sectionBeforePath,\n afterPath: sectionAfterPath,\n level: \"section\",\n });\n } catch {\n // Element screenshot failed — skip silently\n }\n }\n\n // Clean up tags on both pages\n await cleanupSectionTags(afterPage);\n await cleanupSectionTags(beforePage);\n}\n\nasync function captureAutoTabs(\n afterPage: Page,\n beforePage: Page,\n task: CaptureTask,\n beforeUrl: string,\n afterUrl: string,\n outputDir: string,\n options: CaptureOptions,\n settle: (page: Page) => Promise<void>,\n results: CaptureResult[]\n): Promise<void> {\n const tabs = await detectTabs(afterPage, options.maxTabsPerRoute);\n const usedPrefixes = new Set<string>();\n\n for (const tab of tabs) {\n let slug = sanitizeTabLabel(tab.label);\n if (!slug) continue;\n\n // Handle duplicate slugs\n if (usedPrefixes.has(slug)) {\n let suffix = 2;\n while (usedPrefixes.has(`${slug}-${suffix}`)) suffix++;\n slug = `${slug}-${suffix}`;\n }\n usedPrefixes.add(slug);\n\n const tabPrefix = `${task.prefix}~${slug}`;\n const tabLabel = `${task.label} [${tab.label}]`;\n const tabBeforePath = join(outputDir, `${tabPrefix}-before.png`);\n const tabAfterPath = join(outputDir, `${tabPrefix}-after.png`);\n\n try {\n // Click tab on after page\n const afterUrlBefore = afterPage.url();\n await afterPage.getByRole(\"tab\", { name: tab.label }).first().click();\n await settle(afterPage);\n\n // Check if click navigated away\n if (afterPage.url() !== afterUrlBefore) {\n await afterPage.goBack({ waitUntil: \"networkidle\" });\n await settle(afterPage);\n continue;\n }\n\n await afterPage.screenshot({ path: tabAfterPath, fullPage: true });\n\n // Try clicking same tab on before page (may not exist)\n try {\n const beforeUrlBefore = beforePage.url();\n await beforePage.getByRole(\"tab\", { name: tab.label }).first().click({ timeout: 2000 });\n await settle(beforePage);\n\n if (beforePage.url() !== beforeUrlBefore) {\n await beforePage.goBack({ waitUntil: \"networkidle\" });\n await settle(beforePage);\n // Still capture — before stays in default state\n await beforePage.screenshot({ path: tabBeforePath, fullPage: true });\n } else {\n await beforePage.screenshot({ path: tabBeforePath, fullPage: true });\n }\n } catch {\n // Tab doesn't exist on before page — capture default state\n await beforePage.screenshot({ path: tabBeforePath, fullPage: true });\n }\n\n results.push({\n route: tabLabel,\n prefix: tabPrefix,\n beforePath: tabBeforePath,\n afterPath: tabAfterPath,\n level: \"tab\",\n });\n\n // Component detection within this tab view\n if ((task.changedComponents?.length ?? 0) > 0) {\n await captureComponentInstances(\n afterPage, beforePage,\n task.changedComponents!,\n tabPrefix, tabLabel,\n outputDir,\n results\n );\n }\n\n // Auto-detect sections within this tab view\n if (options.autoSections && !task.skipAutoSections) {\n await captureAutoSections(\n afterPage, beforePage,\n tabPrefix, tabLabel,\n outputDir, options, settle, results\n );\n }\n } catch {\n // Tab click failed — skip this tab silently\n logger.dim(` Skipped tab \"${tab.label}\" on ${task.route}`);\n }\n }\n\n // Navigate back to route to reset state for next task\n if (tabs.length > 0) {\n await Promise.all([\n beforePage.goto(`${beforeUrl}${task.route}`, { waitUntil: \"networkidle\" }),\n afterPage.goto(`${afterUrl}${task.route}`, { waitUntil: \"networkidle\" }),\n ]);\n }\n}\n\nasync function captureOneRoute(\n task: CaptureTask,\n beforeCtx: BrowserContext,\n afterCtx: BrowserContext,\n beforeUrl: string,\n afterUrl: string,\n outputDir: string,\n options: CaptureOptions\n): Promise<CaptureResult[]> {\n const results: CaptureResult[] = [];\n const [beforePage, afterPage] = await Promise.all([\n beforeCtx.newPage(),\n afterCtx.newPage(),\n ]);\n\n try {\n const beforePath = join(outputDir, `${task.prefix}-before.png`);\n const afterPath = join(outputDir, `${task.prefix}-after.png`);\n\n const settle = async (page: Page) => {\n await page.evaluate(\"document.fonts.ready\");\n if (options.delay > 0) {\n await page.waitForTimeout(options.delay);\n }\n };\n\n const performActions = async (page: Page, actions: ScenarioAction[]) => {\n for (const action of actions) {\n if (action.click) {\n await page.locator(action.click).first().click();\n }\n if (action.scroll) {\n await page.locator(action.scroll).first().scrollIntoViewIfNeeded();\n }\n if (action.wait && action.wait > 0) {\n await page.waitForTimeout(action.wait);\n }\n await settle(page);\n }\n };\n\n const screenshot = async (page: Page, path: string) => {\n if (task.selector) {\n const el = page.locator(task.selector).first();\n await el.screenshot({ path });\n } else {\n await page.screenshot({ path, fullPage: true });\n }\n };\n\n // Capture before and after in parallel\n await Promise.all([\n beforePage\n .goto(`${beforeUrl}${task.route}`, { waitUntil: \"networkidle\" })\n .then(() => settle(beforePage))\n .then(() => task.actions ? performActions(beforePage, task.actions) : undefined)\n .then(() => screenshot(beforePage, beforePath)),\n afterPage\n .goto(`${afterUrl}${task.route}`, { waitUntil: \"networkidle\" })\n .then(() => settle(afterPage))\n .then(() => task.actions ? performActions(afterPage, task.actions) : undefined)\n .then(() => screenshot(afterPage, afterPath)),\n ]);\n\n results.push({ route: task.label, prefix: task.prefix, beforePath, afterPath, level: \"page\" });\n\n // Per changed component: emit context/parent/component captures\n if (\n (task.changedComponents?.length ?? 0) > 0 &&\n !task.actions &&\n !task.selector\n ) {\n await captureComponentInstances(\n afterPage, beforePage,\n task.changedComponents!,\n task.prefix, task.label,\n outputDir,\n results\n );\n }\n\n // Auto-detect and capture section states (on default page view)\n if (options.autoSections && !task.actions && !task.selector && !task.skipAutoSections) {\n await captureAutoSections(\n afterPage, beforePage,\n task.prefix, task.label,\n outputDir, options, settle, results\n );\n }\n\n // Auto-detect and capture tab states\n if (options.autoTabs && !task.actions && !task.selector && !task.skipAutoTabs) {\n await captureAutoTabs(\n afterPage, beforePage,\n task, beforeUrl, afterUrl,\n outputDir, options, settle, results\n );\n }\n } finally {\n await Promise.all([beforePage.close(), afterPage.close()]);\n }\n\n return results;\n}\n\nconst BATCH_SIZE = 3;\n\nexport async function captureRoutes(\n tasks: CaptureTask[],\n beforeUrl: string,\n afterUrl: string,\n outputDir: string,\n options: CaptureOptions\n): Promise<CaptureResult[]> {\n const browser = options.browser ?? (await launchBrowser());\n const ownsBrowser = !options.browser;\n const results: CaptureResult[] = [];\n\n try {\n // Build viewport/device context options\n const device = options.device ? devices[options.device] : undefined;\n const contextOpts = device\n ? { ...device }\n : {\n viewport: { width: options.width, height: options.height },\n deviceScaleFactor: 2,\n };\n\n // Two contexts so before/after can be captured in parallel\n const [beforeCtx, afterCtx] = await Promise.all([\n browser.newContext(contextOpts),\n browser.newContext(contextOpts),\n ]);\n\n for (let batchStart = 0; batchStart < tasks.length; batchStart += BATCH_SIZE) {\n const batch = tasks.slice(batchStart, batchStart + BATCH_SIZE);\n const batchResults = await Promise.allSettled(\n batch.map((task, idx) => {\n options.onProgress?.(batchStart + idx + 1, task.label);\n return captureOneRoute(task, beforeCtx, afterCtx, beforeUrl, afterUrl, outputDir, options);\n })\n );\n for (const result of batchResults) {\n if (result.status === \"fulfilled\") {\n results.push(...result.value);\n } else {\n logger.dim(`Capture failed: ${result.reason}`);\n }\n }\n }\n\n await Promise.all([beforeCtx.close(), afterCtx.close()]);\n } finally {\n if (ownsBrowser) {\n await browser.close();\n }\n }\n\n return results;\n}\n","import type { Page } from \"playwright\";\nimport { sanitizeLabel } from \"./path.js\";\n\nexport interface DetectedTab {\n label: string;\n selected: boolean;\n}\n\n/**\n * Detect top-level ARIA tabs on the page.\n * Skips tablists nested inside tabpanels to prevent exponential explosion.\n * Returns only inactive tabs (up to maxTabs).\n */\nexport async function detectTabs(\n page: Page,\n maxTabs: number\n): Promise<DetectedTab[]> {\n const allTabs: DetectedTab[] = await page.evaluate(() => {\n const tablists = document.querySelectorAll('[role=\"tablist\"]');\n const results: { label: string; selected: boolean }[] = [];\n\n for (const tablist of tablists) {\n // Skip tablists nested inside a tabpanel\n if (tablist.closest('[role=\"tabpanel\"]')) continue;\n\n const tabs = tablist.querySelectorAll('[role=\"tab\"]');\n for (const tab of tabs) {\n const label = (tab.textContent ?? \"\").trim();\n if (!label) continue;\n const selected =\n tab.getAttribute(\"aria-selected\") === \"true\" ||\n tab.getAttribute(\"data-state\") === \"active\";\n results.push({ label, selected });\n }\n }\n return results;\n });\n\n // Return only inactive tabs, capped at maxTabs\n return allTabs.filter((t) => !t.selected).slice(0, maxTabs);\n}\n\n/**\n * Convert tab label to a filename-safe slug.\n * \"Components & Hooks\" → \"components-hooks\"\n */\nexport function sanitizeTabLabel(label: string): string {\n return sanitizeLabel(label);\n}\n","import type { Page } from \"playwright\";\nimport { sanitizeLabel } from \"./path.js\";\n\nexport interface DetectedSection {\n label: string;\n index: number;\n}\n\n/**\n * Detect heading-labeled sections on the page.\n * Finds h2/h3 headings, walks up to their section container, and tags each\n * with a `data-ab-section` attribute for reliable Playwright targeting.\n * Returns empty array if fewer than 3 headings found (not a showcase page).\n */\nexport async function detectSections(\n page: Page,\n maxSections: number\n): Promise<DetectedSection[]> {\n const sections: DetectedSection[] = await page.evaluate((max: number) => {\n const headings = Array.from(document.querySelectorAll(\"h2, h3\"));\n\n // Filter out headings with empty text\n const valid = headings.filter((h) => (h.textContent ?? \"\").trim().length > 0);\n if (valid.length < 3) return [];\n\n const results: { label: string; index: number }[] = [];\n const tagged = new Set<Element>();\n let idx = 0;\n\n for (const heading of valid) {\n if (idx >= max) break;\n\n // Walk up to find the section container\n const container = findSectionContainer(heading);\n if (!container || tagged.has(container)) continue;\n\n // Skip containers that are too large (>90% of page height)\n if (container.scrollHeight > document.documentElement.scrollHeight * 0.9) continue;\n\n container.setAttribute(\"data-ab-section\", String(idx));\n heading.setAttribute(\"data-ab-heading\", String(idx));\n tagged.add(container);\n\n results.push({\n label: (heading.textContent ?? \"\").trim(),\n index: idx,\n });\n idx++;\n }\n\n return results;\n\n function findSectionContainer(heading: Element): Element | null {\n let el: Element | null = heading;\n const headingTag = heading.tagName.toLowerCase();\n\n while (el && el !== document.body) {\n const parentEl: Element | null = el.parentElement;\n if (!parentEl) return el;\n\n // Stop at semantic section elements\n const parentTag = parentEl.tagName.toLowerCase();\n if (parentTag === \"section\" || parentTag === \"article\") return parentEl;\n if (parentEl.getAttribute(\"role\") === \"region\") return parentEl;\n\n // Stop at body/main — return current element\n if (parentTag === \"body\" || parentTag === \"main\") return el;\n\n // Stop when parent has multiple headings at the same level\n // (indicates current element IS the section)\n const siblingHeadings = parentEl.querySelectorAll(`:scope > * ${headingTag}, :scope > ${headingTag}`);\n if (siblingHeadings.length > 1) return el;\n\n el = parentEl;\n }\n\n return el;\n }\n }, maxSections);\n\n return sections;\n}\n\n/**\n * Find a section on the page by heading text and tag it with `data-ab-section`.\n * Used to match sections on the before page by heading text from the after page.\n * Returns false if heading not found.\n */\nexport async function tagSectionOnPage(\n page: Page,\n headingText: string,\n index: number\n): Promise<boolean> {\n return page.evaluate(\n ({ text, idx }: { text: string; idx: number }) => {\n const headings = Array.from(document.querySelectorAll(\"h2, h3\"));\n const match = headings.find(\n (h) => (h.textContent ?? \"\").trim() === text\n );\n if (!match) return false;\n\n // Walk up to find the section container (same logic as detectSections)\n let el: Element | null = match;\n const headingTag = match.tagName.toLowerCase();\n\n while (el && el !== document.body) {\n const parentEl: Element | null = el.parentElement;\n if (!parentEl) break;\n\n const parentTag = parentEl.tagName.toLowerCase();\n if (parentTag === \"section\" || parentTag === \"article\") {\n el = parentEl;\n break;\n }\n if (parentEl.getAttribute(\"role\") === \"region\") {\n el = parentEl;\n break;\n }\n if (parentTag === \"body\" || parentTag === \"main\") break;\n\n const siblingHeadings = parentEl.querySelectorAll(\n `:scope > * ${headingTag}, :scope > ${headingTag}`\n );\n if (siblingHeadings.length > 1) break;\n\n el = parentEl;\n }\n\n if (el) {\n el.setAttribute(\"data-ab-section\", String(idx));\n match.setAttribute(\"data-ab-heading\", String(idx));\n return true;\n }\n return false;\n },\n { text: headingText, idx: index }\n );\n}\n\n/**\n * Hide the heading that triggered a section detection.\n * Uses display:none to collapse the space it occupies.\n */\nexport async function hideSectionHeading(page: Page, sectionIndex: number): Promise<void> {\n await page.evaluate((idx: number) => {\n const heading = document.querySelector(`[data-ab-heading=\"${idx}\"]`);\n if (heading instanceof HTMLElement) {\n heading.style.display = \"none\";\n }\n }, sectionIndex);\n}\n\n/**\n * Restore a previously hidden section heading.\n */\nexport async function showSectionHeading(page: Page, sectionIndex: number): Promise<void> {\n await page.evaluate((idx: number) => {\n const heading = document.querySelector(`[data-ab-heading=\"${idx}\"]`);\n if (heading instanceof HTMLElement) {\n heading.style.display = \"\";\n }\n }, sectionIndex);\n}\n\n/**\n * Remove all `data-ab-section` and `data-ab-heading` attributes from the page.\n */\nexport async function cleanupSectionTags(page: Page): Promise<void> {\n await page.evaluate(() => {\n for (const el of document.querySelectorAll(\"[data-ab-section]\")) {\n el.removeAttribute(\"data-ab-section\");\n }\n for (const el of document.querySelectorAll(\"[data-ab-heading]\")) {\n el.removeAttribute(\"data-ab-heading\");\n if (el instanceof HTMLElement) {\n el.style.display = \"\";\n }\n }\n });\n}\n\n/**\n * Convert section label to a filename-safe slug.\n * \"Card Component\" → \"card-component\"\n */\nexport function sanitizeSectionLabel(label: string): string {\n return sanitizeLabel(label);\n}\n","import { join, dirname } from \"path\";\nimport { readFileSync, unlinkSync } from \"fs\";\nimport { ODiffServer } from \"odiff-bin\";\nimport sharp from \"sharp\";\nimport type { CaptureResult, CompareResult } from \"../types.js\";\n\nfunction readPngSize(path: string): { width: number; height: number } {\n const buf = readFileSync(path);\n return { width: buf.readUInt32BE(16), height: buf.readUInt32BE(20) };\n}\n\nasync function generateComposite(\n beforePath: string,\n afterPath: string,\n outputPath: string,\n bgColor: string\n): Promise<void> {\n const beforeSize = readPngSize(beforePath);\n const afterSize = readPngSize(afterPath);\n const imgW = Math.max(beforeSize.width, afterSize.width);\n const imgH = Math.max(beforeSize.height, afterSize.height);\n\n const PADDING = 120;\n const GAP = 80;\n const LABEL_H = 70;\n const canvasW = Math.max(600, Math.min(2400, imgW * 2 + GAP + PADDING * 2));\n const canvasH = Math.max(300, Math.min(2400, imgH + LABEL_H + PADDING * 2));\n const maxImgH = canvasH - PADDING * 2 - LABEL_H;\n const colW = Math.floor((canvasW - PADDING * 2 - GAP) / 2);\n\n const [beforeBuf, afterBuf] = await Promise.all([\n sharp(beforePath)\n .resize(colW, maxImgH, { fit: \"inside\" })\n .toBuffer({ resolveWithObject: true }),\n sharp(afterPath)\n .resize(colW, maxImgH, { fit: \"inside\" })\n .toBuffer({ resolveWithObject: true }),\n ]);\n\n const beforeLeft = PADDING + Math.floor((colW - beforeBuf.info.width) / 2);\n const beforeTop = PADDING + Math.floor((maxImgH - beforeBuf.info.height) / 2);\n const afterLeft = PADDING + colW + GAP + Math.floor((colW - afterBuf.info.width) / 2);\n const afterTop = PADDING + Math.floor((maxImgH - afterBuf.info.height) / 2);\n\n const labelY = PADDING + maxImgH + 50;\n const beforeLabelX = PADDING + Math.floor(colW / 2);\n const afterLabelX = PADDING + colW + GAP + Math.floor(colW / 2);\n\n const labelSvg = Buffer.from(\n `<svg width=\"${canvasW}\" height=\"${canvasH}\">\n <text x=\"${beforeLabelX}\" y=\"${labelY}\" text-anchor=\"middle\"\n font-family=\"system-ui, sans-serif\" font-size=\"30\" font-weight=\"500\"\n fill=\"#888\">Before</text>\n <text x=\"${afterLabelX}\" y=\"${labelY}\" text-anchor=\"middle\"\n font-family=\"system-ui, sans-serif\" font-size=\"30\" font-weight=\"500\"\n fill=\"#22c55e\">After</text>\n </svg>`\n );\n\n await sharp({\n create: {\n width: canvasW,\n height: canvasH,\n channels: 4,\n background: bgColor,\n },\n })\n .composite([\n { input: beforeBuf.data, left: beforeLeft, top: beforeTop },\n { input: afterBuf.data, left: afterLeft, top: afterTop },\n { input: labelSvg, left: 0, top: 0 },\n ])\n .png()\n .toFile(outputPath);\n}\n\nexport interface CompareOptions {\n bgColor: string;\n}\n\nasync function compareOne(\n capture: CaptureResult,\n outputDir: string,\n threshold: number,\n server: ODiffServer,\n options: CompareOptions\n): Promise<CompareResult> {\n const diffPath = join(outputDir, `${capture.prefix}-diff.png`);\n const dir = dirname(capture.beforePath);\n const comparePath = join(dir, `${capture.prefix}-compare.png`);\n\n const result = await server.compare(\n capture.beforePath,\n capture.afterPath,\n diffPath,\n { threshold: 0.1, antialiasing: true }\n );\n\n // Always clean up diff heatmap — it's an implementation artifact\n try { unlinkSync(diffPath); } catch {}\n\n let diffPixels = 0;\n let totalPixels = 0;\n let diffPercentage = 0;\n let changed = false;\n\n if (result.match) {\n // No change\n } else if (result.reason === \"pixel-diff\") {\n diffPixels = result.diffCount;\n diffPercentage = result.diffPercentage;\n changed = diffPercentage > threshold;\n } else {\n // layout-diff or file-not-exists — treat as changed\n diffPercentage = 100;\n changed = true;\n }\n\n await generateComposite(\n capture.beforePath,\n capture.afterPath,\n comparePath,\n options.bgColor\n );\n\n return {\n route: capture.route,\n prefix: capture.prefix,\n beforePath: capture.beforePath,\n afterPath: capture.afterPath,\n comparePath,\n diffPixels,\n totalPixels,\n diffPercentage,\n changed,\n };\n}\n\nexport async function compareScreenshots(\n captures: CaptureResult[],\n outputDir: string,\n threshold: number = 0.1,\n options: CompareOptions\n): Promise<CompareResult[]> {\n const server = new ODiffServer();\n try {\n const results: CompareResult[] = [];\n for (const capture of captures) {\n results.push(await compareOne(capture, outputDir, threshold, server, options));\n }\n return results;\n } finally {\n server.stop();\n }\n}\n","import { execSync } from \"child_process\";\nimport { logger } from \"../logger.js\";\nimport { generateSummaryMd } from \"../templates/summary.md.js\";\nimport type { CompareResult } from \"../types.js\";\n\nconst COMMENT_MARKER = \"<!-- afterbefore -->\";\n\nfunction findPrNumber(): string | null {\n try {\n const output = execSync(\"gh pr view --json number -q .number\", {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }).trim();\n return output || null;\n } catch {\n return null;\n }\n}\n\nfunction findExistingCommentId(prNumber: string): string | null {\n try {\n const output = execSync(\n `gh api repos/{owner}/{repo}/issues/${prNumber}/comments --jq '.[] | select(.body | contains(\"${COMMENT_MARKER}\")) | .id'`,\n { encoding: \"utf-8\", stdio: [\"pipe\", \"pipe\", \"pipe\"] }\n ).trim();\n const ids = output.split(\"\\n\").filter(Boolean);\n return ids[0] || null;\n } catch {\n return null;\n }\n}\n\nfunction postOrUpdateComment(prNumber: string, body: string): void {\n const existingId = findExistingCommentId(prNumber);\n\n if (existingId) {\n logger.info(`Updating existing PR comment (id: ${existingId})`);\n execSync(\n `gh api repos/{owner}/{repo}/issues/comments/${existingId} -X PATCH -f body=@-`,\n {\n input: body,\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }\n );\n } else {\n logger.info(`Creating new PR comment`);\n execSync(\n `gh api repos/{owner}/{repo}/issues/${prNumber}/comments -f body=@-`,\n {\n input: body,\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }\n );\n }\n}\n\nexport async function generateReport(\n results: CompareResult[],\n outputDir: string,\n options: { post: boolean }\n): Promise<void> {\n if (options.post) {\n const prNumber = findPrNumber();\n if (!prNumber) {\n logger.warn(\n \"Could not find PR number. Make sure you are on a branch with an open PR and `gh` is authenticated.\"\n );\n return;\n }\n\n const prSummary = generateSummaryMd(results, undefined, { includeFilePaths: false });\n postOrUpdateComment(prNumber, prSummary);\n logger.success(`Posted results to PR #${prNumber}`);\n }\n}\n","import type { CompareResult } from \"../types.js\";\n\ninterface SummaryOptions {\n includeFilePaths?: boolean;\n}\n\nexport function generateSummaryMd(results: CompareResult[], gitDiff?: string, options?: SummaryOptions): string {\n const includeFilePaths = options?.includeFilePaths ?? true;\n const changed = results.filter((r) => r.changed);\n const lines: string[] = [];\n\n lines.push(\"<!-- afterbefore -->\");\n lines.push(\"\");\n lines.push(\"## afterbefore Report\");\n lines.push(\"\");\n\n if (changed.length === 0) {\n lines.push(\"No visual changes detected.\");\n lines.push(\"\");\n lines.push(`${results.length} route(s) captured, all unchanged.`);\n return lines.join(\"\\n\");\n }\n\n lines.push(\n `${results.length} route(s) captured, **${changed.length}** with visual changes.`\n );\n lines.push(\"\");\n lines.push(\"| Route | Diff % | Status |\");\n lines.push(\"|-------|--------|--------|\");\n\n for (const r of results) {\n const status = r.changed ? \"Changed\" : \"Unchanged\";\n const pct = r.changed ? `${r.diffPercentage.toFixed(2)}%` : \"0%\";\n lines.push(`| \\`${r.route}\\` | ${pct} | ${status} |`);\n }\n\n if (gitDiff) {\n lines.push(\"\");\n lines.push(\"### Changed files\");\n lines.push(\"```diff\");\n lines.push(gitDiff);\n lines.push(\"```\");\n }\n\n if (includeFilePaths) {\n lines.push(\"\");\n lines.push(\"### Screenshots\");\n lines.push(\"| Route | Before | After | Compare |\");\n lines.push(\"|-------|--------|-------|---------|\");\n\n for (const r of changed) {\n lines.push(`| \\`${r.route}\\` | \\`${r.beforePath}\\` | \\`${r.afterPath}\\` | \\`${r.comparePath}\\` |`);\n }\n\n lines.push(\"\");\n lines.push(\"Review the before/after screenshots above to verify the visual changes match the code diff.\");\n }\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":["resolve","unlinkSync","resolve","existsSync","readFileSync","resolve","readFileSync","join","existsSync","readFileSync","resolve","existsSync","resolve","readFileSync","join","readFileSync","resolve","execSync","join","existsSync","join","join","execSync","existsSync","join","resolve","exec","join","existsSync","join","execSync","execSync","maxPerSource","join","join","dirname","readFileSync","execSync","execSync","resolve","unlinkSync"]}
|
|
1
|
+
{"version":3,"sources":["../src/pipeline.ts","../src/logger.ts","../src/cleanup.ts","../src/config.ts","../src/utils/fs.ts","../src/utils/port.ts","../src/utils/bgcolor.ts","../src/utils/git.ts","../src/stages/diff.ts","../src/stages/classify.ts","../src/stages/graph.ts","../src/stages/resolve.ts","../src/utils/nextjs.ts","../src/utils/path.ts","../src/stages/impact.ts","../src/stages/worktree.ts","../src/errors.ts","../src/utils/pm.ts","../src/stages/server.ts","../src/stages/capture.ts","../src/utils/tabs.ts","../src/utils/sections.ts","../src/stages/compare.ts","../src/stages/report.ts","../src/templates/summary.md.ts"],"sourcesContent":["import { resolve } from \"path\";\nimport { unlinkSync } from \"fs\";\nimport type { PipelineOptions, AffectedRoute, AfterbeforeConfig, CaptureTask, ImportGraph } from \"./types.js\";\nimport { logger } from \"./logger.js\";\nimport { cleanupRegistry } from \"./cleanup.js\";\nimport { loadConfig } from \"./config.js\";\nimport { ensureDir } from \"./utils/fs.js\";\nimport { findAvailablePort } from \"./utils/port.js\";\nimport { detectBgColor } from \"./utils/bgcolor.js\";\nimport { getGitDiff, getCurrentBranch } from \"./utils/git.js\";\nimport { getChangedFiles } from \"./stages/diff.js\";\nimport { classifyFiles, isGlobalVisualFile } from \"./stages/classify.js\";\nimport { buildImportGraph } from \"./stages/graph.js\";\nimport { findAffectedRoutes, findRoutesForFile } from \"./stages/impact.js\";\nimport { isPageFile, pagePathToRoute } from \"./utils/nextjs.js\";\nimport { createWorktree } from \"./stages/worktree.js\";\nimport { startServer, stopServer } from \"./stages/server.js\";\nimport { captureRoutes, launchBrowser } from \"./stages/capture.js\";\nimport { compareScreenshots } from \"./stages/compare.js\";\nimport { generateReport } from \"./stages/report.js\";\nimport { generateSummaryMd } from \"./templates/summary.md.js\";\ndeclare const __VERSION__: string;\n\nfunction generateSessionName(cwd: string): string {\n const branch = getCurrentBranch(cwd);\n const name = branch.replace(/^(feat|fix|perf|chore|refactor|docs|style|test|ci|build)\\//, \"\");\n const date = new Date().toISOString().slice(0, 10);\n return `${name}_${date}`;\n}\n\nfunction routeToPrefix(route: string): string {\n if (route === \"/\") return \"_root\";\n return route.replace(/^\\//, \"\").replace(/\\//g, \"-\");\n}\n\nfunction mapRouteToChangedComponents(\n changedComponentFiles: string[],\n graph: ImportGraph\n): Map<string, string[]> {\n const routeMap = new Map<string, string[]>();\n\n for (const componentPath of changedComponentFiles) {\n const routes = findRoutesForFile(componentPath, graph);\n for (const route of routes) {\n const next = routeMap.get(route) ?? [];\n next.push(componentPath);\n routeMap.set(route, next);\n }\n }\n\n for (const [route, files] of routeMap.entries()) {\n routeMap.set(route, Array.from(new Set(files)).sort((a, b) => a.localeCompare(b)));\n }\n\n return routeMap;\n}\n\nfunction expandRoutes(\n routes: AffectedRoute[],\n config: AfterbeforeConfig | null,\n routeComponentMap: Map<string, string[]>\n): CaptureTask[] {\n const tasks: CaptureTask[] = [];\n for (const r of routes) {\n const scenarios = config?.scenarios?.[r.route] ?? [];\n const changedComponents = routeComponentMap.get(r.route) ?? [];\n // Default capture — skip auto-tabs if route has manual scenarios\n tasks.push({\n route: r.route,\n label: r.route,\n prefix: routeToPrefix(r.route),\n skipAutoTabs: scenarios.length > 0,\n skipAutoSections: scenarios.length > 0,\n changedComponents,\n });\n // Scenario captures\n for (const s of scenarios) {\n tasks.push({\n route: r.route,\n label: `${r.route} [${s.name}]`,\n prefix: `${routeToPrefix(r.route)}~${s.name}`,\n actions: s.actions,\n selector: s.selector,\n });\n }\n }\n return tasks;\n}\n\nexport async function runPipeline(options: PipelineOptions): Promise<void> {\n const { base, output, post, cwd } = options;\n const sessionName = generateSessionName(cwd);\n const outputDir = resolve(cwd, output, sessionName);\n const startTime = Date.now();\n\n try {\n // Print banner\n const version = typeof __VERSION__ !== \"undefined\" ? __VERSION__ : \"dev\";\n console.log(`\\nafterbefore v${version} \\u00b7 Comparing against ${base}\\n`);\n\n // Load optional config (scenarios)\n const config = await loadConfig(cwd);\n\n logger.startPipeline(8);\n\n // 1. Analyze diff\n logger.pipeline(1, \"Analyzing diff...\");\n const diffFiles = getChangedFiles(base, cwd);\n const gitDiff = getGitDiff(base, cwd);\n\n if (diffFiles.length === 0) {\n logger.completePipeline();\n logger.success(\"No changed files detected. Nothing to do.\");\n return;\n }\n\n // 2. Classify + build import graph (overlap worktree creation)\n const classified = classifyFiles(diffFiles);\n const impactfulFiles = classified.filter(\n (f) => f.category !== \"test\" && f.category !== \"other\"\n );\n\n if (impactfulFiles.length === 0) {\n logger.completePipeline();\n logger.success(\n \"No visually relevant changes detected (only test/other files changed).\"\n );\n return;\n }\n\n // Start worktree creation early (it's the slowest step: git checkout + npm install)\n // We'll await it later once we confirm there are routes to capture.\n logger.pipeline(2, \"Building import graph...\");\n const worktreePromise = createWorktree(base, cwd);\n\n const graph = await buildImportGraph(cwd);\n\n // 3. Find affected routes\n logger.pipeline(3, \"Finding affected routes...\");\n const changedPaths = impactfulFiles.map((f) => f.path);\n let affectedRoutes = findAffectedRoutes(changedPaths, graph, cwd, options.maxRoutes);\n const changedComponentFiles = impactfulFiles\n .filter((f) => f.category === \"component\")\n .map((f) => f.path);\n const routeComponentMap = mapRouteToChangedComponents(changedComponentFiles, graph);\n\n // Global config/style files affect every page but don't appear in the import graph\n if (affectedRoutes.length === 0) {\n const hasGlobalChanges = impactfulFiles.some((f) => isGlobalVisualFile(f.path));\n if (hasGlobalChanges) {\n const allRoutes: AffectedRoute[] = [];\n for (const file of graph.forward.keys()) {\n if (!isPageFile(file)) continue;\n const route = pagePathToRoute(file);\n if (route === null) continue;\n allRoutes.push({\n pagePath: file,\n route,\n reason: \"transitive\",\n depth: 0,\n triggerChain: [file],\n });\n }\n allRoutes.sort((a, b) => a.route.localeCompare(b.route));\n if (options.maxRoutes > 0 && allRoutes.length > options.maxRoutes) {\n affectedRoutes = allRoutes.slice(0, options.maxRoutes);\n } else {\n affectedRoutes = allRoutes;\n }\n }\n }\n\n if (affectedRoutes.length === 0) {\n // Clean up the worktree we started eagerly\n worktreePromise.then((w) => w.cleanup()).catch(() => {});\n logger.completePipeline();\n logger.success(\n \"No affected routes found. Changed files don't impact any pages.\"\n );\n return;\n }\n\n\n // 4. Wait for worktree\n logger.pipeline(4, \"Setting up worktree...\");\n const worktree = await worktreePromise;\n\n // 5. Start servers + browser in parallel\n logger.pipeline(5, \"Starting servers...\");\n await ensureDir(outputDir);\n\n const beforePort = await findAvailablePort();\n const afterPort = await findAvailablePort(new Set([beforePort]));\n\n const [beforeServer, afterServer, browser] = await Promise.all([\n startServer(worktree.path, beforePort),\n startServer(cwd, afterPort),\n launchBrowser(),\n ]);\n cleanupRegistry.register(() => stopServer(beforeServer));\n cleanupRegistry.register(() => stopServer(afterServer));\n cleanupRegistry.register(() => browser.close());\n\n // 6. Capture screenshots\n logger.pipeline(6, \"Capturing screenshots...\");\n const tasks = expandRoutes(affectedRoutes, config, routeComponentMap);\n const captures = await captureRoutes(\n tasks,\n beforeServer.url,\n afterServer.url,\n outputDir,\n {\n browser,\n width: options.width,\n height: options.height,\n device: options.device,\n delay: options.delay,\n autoTabs: options.autoTabs,\n maxTabsPerRoute: options.maxTabsPerRoute,\n autoSections: options.autoSections,\n maxSectionsPerRoute: options.maxSectionsPerRoute,\n onProgress: (i, label) =>\n logger.pipeline(6, `Capturing ${label} (${i}/${tasks.length})...`),\n }\n );\n\n // 7. Compare screenshots\n logger.pipeline(7, \"Comparing screenshots...\");\n const bgColor = detectBgColor(cwd);\n const allResults = await compareScreenshots(captures, outputDir, options.threshold, { bgColor });\n\n // Filter out unchanged sub-captures (tabs, sections, components)\n // Sub-captures have `~` in their prefix; main route captures are always kept\n const results = allResults.filter((r) => {\n const isSubCapture = r.prefix.includes(\"~\");\n if (isSubCapture && !r.changed) {\n // Clean up files for unchanged sub-captures\n try { unlinkSync(r.beforePath); } catch {}\n try { unlinkSync(r.afterPath); } catch {}\n try { unlinkSync(r.comparePath); } catch {}\n return false;\n }\n return true;\n });\n\n // 8. Generate report\n logger.pipeline(8, \"Generating report...\");\n await generateReport(results, outputDir, { post });\n\n // Print AI-friendly summary to stdout\n const summary = generateSummaryMd(results, gitDiff);\n logger.completePipeline(true);\n console.log(\"\\n\" + summary);\n\n // Final summary\n const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);\n const changedCount = results.filter((r) => r.changed).length;\n logger.success(\n `Done in ${elapsed}s \\u2014 ${results.length} route(s) captured, ${changedCount} with visual changes`\n );\n } finally {\n // Write debug log to output folder (best-effort)\n try { logger.writeLogFile(resolve(outputDir, \"debug.log\")); } catch {}\n await cleanupRegistry.runAll();\n }\n}\n","import { writeFileSync } from \"fs\";\nimport chalk from \"chalk\";\nimport ora, { type Ora } from \"ora\";\n\nconst BAR_WIDTH = 20;\nconst BAR_SPINNER = { interval: 80, frames: [\" \"] };\n\nexport class Logger {\n private spinner: Ora | null = null;\n private pipelineTotal = 0;\n private lastStep = 0;\n private lastLabel = \"\";\n private pipelineActive = false;\n private logBuffer: string[] = [];\n\n private log(level: string, message: string): void {\n const ts = new Date().toISOString();\n this.logBuffer.push(`${ts} [${level}] ${message}`);\n }\n\n info(message: string): void {\n this.log(\"info\", message);\n if (this.pipelineActive) return;\n if (this.spinner) {\n this.spinner.clear();\n console.log(chalk.blue(\"ℹ\"), message);\n this.spinner.render();\n } else {\n console.log(chalk.blue(\"ℹ\"), message);\n }\n }\n\n success(message: string): void {\n this.log(\"ok\", message);\n if (this.pipelineActive) return;\n if (this.spinner) {\n this.spinner.clear();\n console.log(chalk.green(\"✔\"), message);\n this.spinner.render();\n } else {\n console.log(chalk.green(\"✔\"), message);\n }\n }\n\n warn(message: string): void {\n this.log(\"warn\", message);\n if (this.spinner) {\n this.spinner.clear();\n console.log(chalk.yellow(\"⚠\"), message);\n this.spinner.render();\n } else {\n console.log(chalk.yellow(\"⚠\"), message);\n }\n }\n\n error(message: string): void {\n this.log(\"error\", message);\n if (this.spinner) {\n this.spinner.clear();\n console.error(chalk.red(\"✖\"), message);\n this.spinner.render();\n } else {\n console.error(chalk.red(\"✖\"), message);\n }\n }\n\n dim(message: string): void {\n this.log(\"debug\", message);\n if (this.pipelineActive) return;\n if (this.spinner) {\n this.spinner.clear();\n console.log(chalk.dim(message));\n this.spinner.render();\n } else {\n console.log(chalk.dim(message));\n }\n }\n\n spin(message: string): Ora {\n this.log(\"info\", message);\n this.clearSpinner();\n this.spinner = ora(message).start();\n return this.spinner;\n }\n\n stopSpinner(): void {\n this.clearSpinner();\n }\n\n startPipeline(total: number): void {\n this.pipelineTotal = total;\n this.lastStep = 0;\n this.lastLabel = \"\";\n this.pipelineActive = true;\n this.clearSpinner();\n\n this.log(\"info\", `Pipeline started (${total} steps)`);\n const text = this.renderPipeline(0, \"Starting...\");\n this.spinner = ora({\n text: chalk.dim(text),\n spinner: BAR_SPINNER,\n }).start();\n }\n\n pipeline(step: number, label: string): void {\n if (step === this.lastStep && label === this.lastLabel) return;\n this.lastStep = step;\n this.lastLabel = label;\n this.log(\"step\", `${step}/${this.pipelineTotal} ${label}`);\n\n const text = chalk.dim(this.renderPipeline(step, label));\n if (!this.spinner) {\n this.spinner = ora({\n text,\n spinner: BAR_SPINNER,\n }).start();\n return;\n }\n\n this.spinner.text = text;\n this.spinner.render();\n }\n\n completePipeline(finished = false): void {\n this.pipelineActive = false;\n this.log(\"info\", `Pipeline ${finished ? \"completed\" : \"stopped\"}`);\n if (this.spinner) {\n if (finished) {\n this.spinner.stop();\n const bar = \"\\u2588\".repeat(BAR_WIDTH);\n console.log(`${chalk.green(\"✔\")} ${bar}`);\n } else {\n this.spinner.stop();\n }\n this.spinner = null;\n } else if (finished) {\n const bar = \"\\u2588\".repeat(BAR_WIDTH);\n console.log(`${chalk.green(\"✔\")} ${bar}`);\n }\n this.pipelineTotal = 0;\n this.lastStep = 0;\n this.lastLabel = \"\";\n }\n\n writeLogFile(filePath: string): void {\n if (this.logBuffer.length === 0) return;\n writeFileSync(filePath, this.logBuffer.join(\"\\n\") + \"\\n\", \"utf-8\");\n }\n\n private renderPipeline(step: number, label: string): string {\n const total = this.pipelineTotal || 1;\n const clampedStep = Math.max(0, Math.min(step, total));\n const filled = Math.round((clampedStep / total) * BAR_WIDTH);\n const bar = \"\\u2588\".repeat(filled) + \"\\u2591\".repeat(BAR_WIDTH - filled);\n return ` ${bar} ${clampedStep}/${total} ${label}`;\n }\n\n private clearSpinner(): void {\n if (this.spinner) {\n this.spinner.stop();\n this.spinner = null;\n }\n }\n}\n\nexport const logger = new Logger();\n","import { logger } from \"./logger.js\";\n\ntype CleanupFn = () => Promise<void> | void;\n\nexport class CleanupRegistry {\n private cleanups: CleanupFn[] = [];\n private registered = false;\n\n register(fn: CleanupFn): void {\n this.cleanups.push(fn);\n this.ensureSignalHandlers();\n }\n\n async runAll(): Promise<void> {\n // LIFO order — tear down in reverse\n const fns = [...this.cleanups].reverse();\n this.cleanups = [];\n\n for (const fn of fns) {\n try {\n await fn();\n } catch (err) {\n logger.warn(\n `Cleanup failed: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n }\n\n private ensureSignalHandlers(): void {\n if (this.registered) return;\n this.registered = true;\n\n const handler = async (signal: string) => {\n logger.dim(`\\nReceived ${signal}, cleaning up...`);\n await this.runAll();\n process.exit(signal === \"SIGINT\" ? 130 : 143);\n };\n\n process.on(\"SIGINT\", () => handler(\"SIGINT\"));\n process.on(\"SIGTERM\", () => handler(\"SIGTERM\"));\n process.on(\"exit\", () => {\n // Synchronous fallback — can't await here but catches missed cleanups\n if (this.cleanups.length > 0) {\n for (const fn of [...this.cleanups].reverse()) {\n try {\n fn();\n } catch {\n // Best effort\n }\n }\n this.cleanups = [];\n }\n });\n }\n}\n\nexport const cleanupRegistry = new CleanupRegistry();\n","import { resolve } from \"path\";\nimport { existsSync, readFileSync } from \"fs\";\nimport { pathToFileURL } from \"url\";\nimport { logger } from \"./logger.js\";\nimport type { AfterbeforeConfig } from \"./types.js\";\n\nconst CONFIG_FILES = [\n \"afterbefore.config.json\",\n \"afterbefore.config.js\",\n \"afterbefore.config.mjs\",\n];\n\nexport async function loadConfig(\n cwd: string\n): Promise<AfterbeforeConfig | null> {\n for (const name of CONFIG_FILES) {\n const filePath = resolve(cwd, name);\n if (!existsSync(filePath)) continue;\n\n logger.dim(` Config: ${name}`);\n\n if (name.endsWith(\".json\")) {\n const raw = readFileSync(filePath, \"utf-8\");\n return JSON.parse(raw) as AfterbeforeConfig;\n }\n\n // .js / .mjs — dynamic import\n const mod = await import(pathToFileURL(filePath).href);\n return (mod.default ?? mod) as AfterbeforeConfig;\n }\n\n return null;\n}\n","import { mkdir } from \"fs/promises\";\n\nexport async function ensureDir(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true });\n}\n","import { createServer } from \"net\";\n\nfunction findPort(): Promise<number> {\n return new Promise((resolve, reject) => {\n const server = createServer();\n server.listen(0, () => {\n const addr = server.address();\n if (!addr || typeof addr === \"string\") {\n server.close();\n reject(new Error(\"Failed to get port\"));\n return;\n }\n const port = addr.port;\n server.close(() => resolve(port));\n });\n server.on(\"error\", reject);\n });\n}\n\nexport async function findAvailablePort(\n exclude?: Set<number>\n): Promise<number> {\n for (let i = 0; i < 5; i++) {\n const port = await findPort();\n if (!exclude || !exclude.has(port)) return port;\n }\n throw new Error(\"Failed to find available port after 5 attempts\");\n}\n","import { existsSync, readFileSync } from \"fs\";\nimport { resolve } from \"path\";\n\nconst DEFAULT_BG = \"#0a0a0a\";\n\nconst GLOBAL_CSS_PATHS = [\n \"app/globals.css\",\n \"src/app/globals.css\",\n \"styles/globals.css\",\n \"src/styles/globals.css\",\n \"app/global.css\",\n \"src/app/global.css\",\n];\n\n/**\n * Convert HSL values to hex color.\n * Handles \"0 0% 3.9%\" (space-separated HSL without hsl() wrapper, common in shadcn/Tailwind v4).\n */\nfunction hslToHex(h: number, s: number, l: number): string {\n s /= 100;\n l /= 100;\n const a = s * Math.min(l, 1 - l);\n const f = (n: number) => {\n const k = (n + h / 30) % 12;\n const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);\n return Math.round(255 * color)\n .toString(16)\n .padStart(2, \"0\");\n };\n return `#${f(0)}${f(8)}${f(4)}`;\n}\n\n/**\n * Try to parse a CSS value as a color and return a hex string.\n * Handles: hex (#fff, #ffffff), hsl(), bare HSL values (shadcn style), rgb().\n */\nfunction parseColorValue(raw: string): string | null {\n const v = raw.trim();\n\n // Already hex\n if (/^#[0-9a-fA-F]{3,8}$/.test(v)) {\n if (v.length === 4) {\n // #abc → #aabbcc\n return `#${v[1]}${v[1]}${v[2]}${v[2]}${v[3]}${v[3]}`;\n }\n return v.slice(0, 7); // strip alpha if present\n }\n\n // hsl(210, 50%, 10%) or hsl(210 50% 10%)\n const hslMatch = v.match(\n /^hsl\\(\\s*([\\d.]+)[,\\s]+\\s*([\\d.]+)%[,\\s]+\\s*([\\d.]+)%/\n );\n if (hslMatch) {\n return hslToHex(\n parseFloat(hslMatch[1]),\n parseFloat(hslMatch[2]),\n parseFloat(hslMatch[3])\n );\n }\n\n // Bare HSL: \"210 50% 10%\" (shadcn / Tailwind v4 style)\n const bareHsl = v.match(/^([\\d.]+)\\s+([\\d.]+)%\\s+([\\d.]+)%$/);\n if (bareHsl) {\n return hslToHex(\n parseFloat(bareHsl[1]),\n parseFloat(bareHsl[2]),\n parseFloat(bareHsl[3])\n );\n }\n\n // rgb(10, 10, 10) or rgb(10 10 10)\n const rgbMatch = v.match(\n /^rgb\\(\\s*([\\d.]+)[,\\s]+\\s*([\\d.]+)[,\\s]+\\s*([\\d.]+)/\n );\n if (rgbMatch) {\n const toHex = (n: string) =>\n Math.round(parseFloat(n)).toString(16).padStart(2, \"0\");\n return `#${toHex(rgbMatch[1])}${toHex(rgbMatch[2])}${toHex(rgbMatch[3])}`;\n }\n\n return null;\n}\n\n/**\n * Detect the project's background color from global CSS.\n *\n * Looks for:\n * 1. `--background: <value>` CSS variable in :root or .dark\n * 2. `background-color:` or `background:` on body/:root\n *\n * Falls back to #0a0a0a.\n */\nexport function detectBgColor(cwd: string): string {\n for (const relPath of GLOBAL_CSS_PATHS) {\n const absPath = resolve(cwd, relPath);\n if (!existsSync(absPath)) continue;\n\n const css = readFileSync(absPath, \"utf-8\");\n\n // Look for --background variable (prefer .dark block, then :root)\n // Match inside .dark { ... } block first\n const darkBlock = css.match(/\\.dark\\s*\\{([^}]+)\\}/);\n if (darkBlock) {\n const bgVar = darkBlock[1].match(/--background\\s*:\\s*([^;]+)/);\n if (bgVar) {\n const color = parseColorValue(bgVar[1]);\n if (color) return color;\n }\n }\n\n // Then :root block\n const rootBlock = css.match(/:root\\s*\\{([^}]+)\\}/);\n if (rootBlock) {\n const bgVar = rootBlock[1].match(/--background\\s*:\\s*([^;]+)/);\n if (bgVar) {\n const color = parseColorValue(bgVar[1]);\n if (color) return color;\n }\n }\n\n // Look for any --background definition anywhere in the file\n const anyBgVar = css.match(/--background\\s*:\\s*([^;]+)/);\n if (anyBgVar) {\n const color = parseColorValue(anyBgVar[1]);\n if (color) return color;\n }\n\n // Direct background on body\n const bodyBg = css.match(\n /body\\s*\\{[^}]*?background(?:-color)?\\s*:\\s*([^;]+)/\n );\n if (bodyBg) {\n const val = bodyBg[1].trim();\n // Skip var() references (we already checked the variable)\n if (!val.startsWith(\"var(\")) {\n const color = parseColorValue(val);\n if (color) return color;\n }\n }\n\n // Found a CSS file but couldn't extract a color — stop searching\n break;\n }\n\n return DEFAULT_BG;\n}\n","import { execSync } from \"child_process\";\n\nexport function git(args: string, cwd?: string): string {\n return execSync(`git ${args}`, {\n cwd,\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }).trim();\n}\n\nexport function isGitRepo(cwd?: string): boolean {\n try {\n git(\"rev-parse --is-inside-work-tree\", cwd);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function getMergeBase(base: string, cwd?: string): string {\n return git(`merge-base ${base} HEAD`, cwd);\n}\n\nexport function getDiffNameStatus(base: string, cwd?: string): string {\n const mergeBase = getMergeBase(base, cwd);\n return git(`diff --name-status ${mergeBase}`, cwd);\n}\n\nexport function getGitDiff(base: string, cwd?: string): string {\n const mergeBase = getMergeBase(base, cwd);\n return git(`diff ${mergeBase}`, cwd);\n}\n\nexport function getCurrentBranch(cwd?: string): string {\n return git(\"rev-parse --abbrev-ref HEAD\", cwd);\n}\n","import type { DiffFile, DiffStatus } from \"../types.js\";\nimport { getDiffNameStatus } from \"../utils/git.js\";\n\nconst VALID_STATUSES = new Set([\"A\", \"M\", \"D\", \"R\", \"C\"]);\n\n/**\n * Parse git diff --name-status output into structured DiffFile array.\n */\nexport function parseDiffOutput(raw: string): DiffFile[] {\n if (!raw.trim()) return [];\n\n const results: DiffFile[] = [];\n\n for (const line of raw.trim().split(\"\\n\")) {\n const parts = line.split(\"\\t\");\n const statusRaw = parts[0].charAt(0);\n\n if (!VALID_STATUSES.has(statusRaw)) continue;\n\n const status = statusRaw as DiffStatus;\n\n if (status === \"R\" || status === \"C\") {\n results.push({ status, oldPath: parts[1], path: parts[2] });\n } else {\n results.push({ status, path: parts[1] });\n }\n }\n\n return results;\n}\n\n/**\n * Get changed files between base and HEAD.\n */\nexport function getChangedFiles(base: string, cwd?: string): DiffFile[] {\n const raw = getDiffNameStatus(base, cwd);\n return parseDiffOutput(raw);\n}\n","import type { ClassifiedFile, DiffFile, FileCategory } from \"../types.js\";\n\n/** Files that affect every page globally (not connected via imports) */\nconst GLOBAL_FILES = new Set([\n \"tailwind.config.ts\",\n \"tailwind.config.js\",\n \"postcss.config.js\",\n \"postcss.config.mjs\",\n \"postcss.config.cjs\",\n]);\n\n/**\n * Check if a file is a global config/style file that visually affects all pages\n * but won't appear in the import graph.\n */\nexport function isGlobalVisualFile(filePath: string): boolean {\n const p = filePath.replace(/^src\\//, \"\");\n return GLOBAL_FILES.has(p) || /globals?\\.(css|scss)$/.test(p);\n}\n\n/** Categories that can affect visual output */\nconst VISUAL_CATEGORIES: Set<FileCategory> = new Set([\n \"page\",\n \"component\",\n \"style\",\n \"layout\",\n]);\n\n/**\n * Classify a file path into a category based on patterns.\n */\nexport function classifyFile(filePath: string): FileCategory {\n const p = filePath.replace(/^src\\//, \"\");\n\n // Test files\n if (\n /\\.(test|spec)\\.[tj]sx?$/.test(p) ||\n /^tests?\\//.test(p) ||\n /\\/__tests__\\//.test(p) ||\n p.includes(\".test.\") ||\n p.includes(\".spec.\")\n ) {\n return \"test\";\n }\n\n // Config files\n if (\n /^(tsconfig|next\\.config|tailwind\\.config|postcss\\.config|\\.eslint|\\.prettier|vitest\\.config|jest\\.config)/.test(\n p\n ) ||\n p === \"package.json\" ||\n p === \"package-lock.json\" ||\n p.endsWith(\".config.ts\") ||\n p.endsWith(\".config.js\") ||\n p.endsWith(\".config.mjs\")\n ) {\n return \"config\";\n }\n\n // Style files\n if (/\\.(css|scss|sass|less)$/.test(p) || p === \"tailwind.config.ts\") {\n return \"style\";\n }\n\n // Layout files (Next.js app router)\n if (/\\/layout\\.[tj]sx?$/.test(p) || /^app\\/layout\\.[tj]sx?$/.test(p)) {\n return \"layout\";\n }\n\n // Page files (Next.js app router)\n if (/\\/page\\.[tj]sx?$/.test(p) || /^app\\/page\\.[tj]sx?$/.test(p)) {\n return \"page\";\n }\n\n // Component files\n if (\n /^(components|app)\\//.test(p) &&\n /\\.[tj]sx?$/.test(p)\n ) {\n return \"component\";\n }\n\n // Utility files (lib, utils, hooks, etc.)\n if (\n /^(lib|utils|hooks|helpers|services|api)\\//.test(p) &&\n /\\.[tj]sx?$/.test(p)\n ) {\n return \"utility\";\n }\n\n // TypeScript/JavaScript files not in known dirs\n if (/\\.[tj]sx?$/.test(p)) {\n return \"component\";\n }\n\n return \"other\";\n}\n\n/**\n * Classify all diff files and return with categories.\n */\nexport function classifyFiles(files: DiffFile[]): ClassifiedFile[] {\n return files.map((f) => ({\n ...f,\n category: classifyFile(f.path),\n }));\n}\n\n/**\n * Filter to only visually relevant files.\n */\nexport function filterVisuallyRelevant(\n files: ClassifiedFile[]\n): ClassifiedFile[] {\n return files.filter((f) => VISUAL_CATEGORIES.has(f.category));\n}\n","import { readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join, relative } from \"node:path\";\nimport { init, parse } from \"es-module-lexer\";\nimport type { ImportGraph } from \"../types.js\";\nimport { logger } from \"../logger.js\";\nimport { createResolver } from \"./resolve.js\";\n\nconst SOURCE_EXTENSIONS = new Set([\".ts\", \".tsx\", \".js\", \".jsx\"]);\nconst SOURCE_DIRS = [\"app\", \"src\", \"components\", \"lib\"];\n\n/**\n * Recursively collect all source files from a directory.\n */\nfunction collectFiles(dir: string): string[] {\n const results: string[] = [];\n\n let entries;\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return results;\n }\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n // Skip node_modules, .next, dist, etc.\n if (entry.name.startsWith(\".\") || entry.name === \"node_modules\" || entry.name === \"dist\") {\n continue;\n }\n results.push(...collectFiles(fullPath));\n } else if (entry.isFile()) {\n const ext = entry.name.slice(entry.name.lastIndexOf(\".\"));\n if (SOURCE_EXTENSIONS.has(ext)) {\n results.push(fullPath);\n }\n }\n }\n\n return results;\n}\n\n/**\n * Parse imports from a file using es-module-lexer.\n */\nfunction parseImports(filePath: string): string[] {\n let source: string;\n try {\n source = readFileSync(filePath, \"utf-8\");\n } catch {\n return [];\n }\n\n try {\n // Strip TypeScript-specific syntax that es-module-lexer can't handle\n // Replace type imports/exports so the lexer can parse the rest\n const cleaned = source\n .replace(/import\\s+type\\s+/g, \"import \")\n .replace(/export\\s+type\\s+/g, \"export \");\n\n const [imports] = parse(cleaned);\n return imports\n .map((imp) => imp.n)\n .filter((n): n is string => n !== undefined && n !== \"\");\n } catch {\n // If parsing fails, fall back to regex-based extraction\n const specifiers: string[] = [];\n const importRegex = /(?:import|from)\\s+[\"']([^\"']+)[\"']/g;\n let match;\n while ((match = importRegex.exec(source)) !== null) {\n specifiers.push(match[1]);\n }\n return specifiers;\n }\n}\n\n/**\n * Build a bidirectional import graph for the project.\n */\nexport async function buildImportGraph(projectRoot: string): Promise<ImportGraph> {\n await init;\n\n const resolve = createResolver(projectRoot);\n\n // Collect all source files\n const allFiles: string[] = [];\n for (const dir of SOURCE_DIRS) {\n const fullDir = join(projectRoot, dir);\n allFiles.push(...collectFiles(fullDir));\n }\n\n // Deduplicate (src/ might contain app/, components/, lib/)\n const fileSet = new Set(allFiles);\n\n const forward = new Map<string, Set<string>>();\n const reverse = new Map<string, Set<string>>();\n\n for (const filePath of fileSet) {\n const relPath = relative(projectRoot, filePath);\n const specifiers = parseImports(filePath);\n\n const deps = new Set<string>();\n\n for (const spec of specifiers) {\n const resolved = resolve(spec, filePath);\n if (!resolved) continue;\n\n const relResolved = relative(projectRoot, resolved);\n deps.add(relResolved);\n\n // Add reverse edge\n if (!reverse.has(relResolved)) {\n reverse.set(relResolved, new Set());\n }\n reverse.get(relResolved)!.add(relPath);\n }\n\n forward.set(relPath, deps);\n }\n\n logger.dim(`Import graph: ${fileSet.size} files, ${countEdges(forward)} edges`);\n\n return { forward, reverse };\n}\n\nfunction countEdges(forward: Map<string, Set<string>>): number {\n let count = 0;\n for (const deps of forward.values()) {\n count += deps.size;\n }\n return count;\n}\n","import { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { resolve, dirname, join } from \"node:path\";\nimport { logger } from \"../logger.js\";\n\ninterface PathMapping {\n prefix: string;\n targets: string[];\n}\n\nconst EXTENSIONS = [\".ts\", \".tsx\", \".js\", \".jsx\"];\n\n/**\n * Create an import specifier resolver for a project using its tsconfig.json paths.\n */\nexport function createResolver(\n projectRoot: string\n): (specifier: string, fromFile: string) => string | null {\n const mappings = loadPathMappings(projectRoot);\n const existsCache = new Map<string, boolean>();\n\n function cachedExists(p: string): boolean {\n const cached = existsCache.get(p);\n if (cached !== undefined) return cached;\n const result = existsSync(p);\n existsCache.set(p, result);\n return result;\n }\n\n function tryResolve(candidate: string): string | null {\n // Exact file\n if (cachedExists(candidate) && !isDirectory(candidate)) return candidate;\n\n // Try extensions\n for (const ext of EXTENSIONS) {\n const withExt = candidate + ext;\n if (cachedExists(withExt)) return withExt;\n }\n\n // Try index files\n for (const ext of EXTENSIONS) {\n const indexFile = join(candidate, `index${ext}`);\n if (cachedExists(indexFile)) return indexFile;\n }\n\n return null;\n }\n\n function isDirectory(p: string): boolean {\n try {\n return statSync(p).isDirectory();\n } catch {\n return false;\n }\n }\n\n return (specifier: string, fromFile: string): string | null => {\n // Relative imports\n if (specifier.startsWith(\".\")) {\n const dir = dirname(fromFile);\n const candidate = resolve(dir, specifier);\n return tryResolve(candidate);\n }\n\n // Try each path mapping\n for (const mapping of mappings) {\n if (!specifier.startsWith(mapping.prefix)) continue;\n\n const rest = specifier.slice(mapping.prefix.length);\n\n for (const target of mapping.targets) {\n const candidate = resolve(projectRoot, target + rest);\n const result = tryResolve(candidate);\n if (result) return result;\n }\n }\n\n // Not a relative or aliased import (node_module etc.)\n return null;\n };\n}\n\nfunction loadPathMappings(projectRoot: string): PathMapping[] {\n const tsconfigPath = join(projectRoot, \"tsconfig.json\");\n if (!existsSync(tsconfigPath)) {\n logger.dim(\"No tsconfig.json found, skipping path alias resolution\");\n return [];\n }\n\n try {\n const raw = readFileSync(tsconfigPath, \"utf-8\");\n const cleaned = stripJsonComments(raw);\n const config = JSON.parse(cleaned);\n\n const paths = config?.compilerOptions?.paths;\n if (!paths) return [];\n\n const baseUrl = config?.compilerOptions?.baseUrl || \".\";\n const mappings: PathMapping[] = [];\n\n for (const [pattern, targets] of Object.entries(paths)) {\n // Convert \"@ /*\" → prefix \"@/\"\n const prefix = pattern.replace(/\\*$/, \"\");\n const resolvedTargets = (targets as string[]).map((t) =>\n t.replace(/\\*$/, \"\")\n );\n // Prepend baseUrl to targets\n const absoluteTargets = resolvedTargets.map((t) =>\n join(baseUrl, t)\n );\n mappings.push({ prefix, targets: absoluteTargets });\n }\n\n return mappings;\n } catch (e) {\n logger.warn(`Failed to parse tsconfig.json: ${e}`);\n return [];\n }\n}\n\n/**\n * Strip JSON comments (// and /* */) and trailing commas while respecting string boundaries.\n * tsconfig.json is JSONC — standard JSON.parse chokes on comments and trailing commas.\n */\nfunction stripJsonComments(input: string): string {\n let result = \"\";\n let i = 0;\n const len = input.length;\n\n while (i < len) {\n const ch = input[i];\n\n // String literal — copy verbatim, including any /* or // inside\n if (ch === '\"') {\n let j = i + 1;\n while (j < len) {\n if (input[j] === \"\\\\\") {\n j += 2; // skip escaped char\n } else if (input[j] === '\"') {\n j++;\n break;\n } else {\n j++;\n }\n }\n result += input.slice(i, j);\n i = j;\n continue;\n }\n\n // Line comment\n if (ch === \"/\" && input[i + 1] === \"/\") {\n i += 2;\n while (i < len && input[i] !== \"\\n\") i++;\n continue;\n }\n\n // Block comment\n if (ch === \"/\" && input[i + 1] === \"*\") {\n i += 2;\n while (i < len && !(input[i] === \"*\" && input[i + 1] === \"/\")) i++;\n i += 2; // skip closing */\n continue;\n }\n\n // Trailing comma before } or ]\n if (ch === \",\") {\n let j = i + 1;\n while (j < len && /\\s/.test(input[j])) j++;\n if (input[j] === \"}\" || input[j] === \"]\") {\n i = j; // skip comma, land on closing bracket\n continue;\n }\n }\n\n result += ch;\n i++;\n }\n\n return result;\n}\n","import { logger } from \"../logger.js\";\n\n/**\n * Convert a Next.js app router page path to a URL route.\n * e.g. \"app/about/page.tsx\" → \"/about\"\n * \"app/(marketing)/pricing/page.tsx\" → \"/pricing\"\n * \"app/page.tsx\" → \"/\"\n */\nexport function pagePathToRoute(pagePath: string): string | null {\n // Strip leading src/ if present\n let p = pagePath.replace(/^src\\//, \"\");\n\n // Must be under app/ and end with page.tsx/jsx/ts/js\n const match = p.match(/^app\\/(.*)\\/page\\.[tj]sx?$/);\n if (!match) {\n // Root page\n if (/^app\\/page\\.[tj]sx?$/.test(p)) return \"/\";\n return null;\n }\n\n let route = match[1];\n\n // Check for dynamic segments\n if (/\\[.*\\]/.test(route)) {\n logger.warn(`Skipping dynamic route: /${route} (dynamic segments not supported)`);\n return null;\n }\n\n // Strip route groups like (marketing)\n route = route\n .split(\"/\")\n .filter((seg) => !seg.startsWith(\"(\"))\n .join(\"/\");\n\n return `/${route}` || \"/\";\n}\n\n/**\n * Check if a file path is a Next.js page file.\n */\nexport function isPageFile(filePath: string): boolean {\n const p = filePath.replace(/^src\\//, \"\");\n return /^app\\/(.+\\/)?page\\.[tj]sx?$/.test(p);\n}\n\n/**\n * Check if a file path is a Next.js layout file.\n */\nexport function isLayoutFile(filePath: string): boolean {\n const p = filePath.replace(/^src\\//, \"\");\n return /^app\\/(.+\\/)?layout\\.[tj]sx?$/.test(p);\n}\n\n/**\n * Get the directory a layout file covers.\n * e.g. \"app/about/layout.tsx\" → \"app/about/\"\n * \"app/layout.tsx\" → \"app/\"\n */\nexport function getLayoutDir(filePath: string): string {\n const p = filePath.replace(/^src\\//, \"\");\n const match = p.match(/^(app\\/.*\\/)layout\\.[tj]sx?$/);\n if (!match) return \"app/\"; // root layout\n return match[1];\n}\n","/**\n * Normalize a file path: forward slashes, strip leading ./\n */\nexport function normalizePath(filePath: string): string {\n return filePath.replace(/\\\\/g, \"/\").replace(/^\\.\\//, \"\");\n}\n\n/**\n * Convert a label to a filename-safe slug.\n * \"Components & Hooks\" -> \"components-hooks\"\n */\nexport function sanitizeLabel(label: string, maxLength: number = 40): string {\n return label\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\")\n .slice(0, maxLength);\n}\n","import type { AffectedRoute, ImportGraph } from \"../types.js\";\nimport {\n isPageFile,\n isLayoutFile,\n getLayoutDir,\n pagePathToRoute,\n} from \"../utils/nextjs.js\";\nimport { normalizePath } from \"../utils/path.js\";\nimport { logger } from \"../logger.js\";\n\nconst MAX_DEPTH = 3;\n\n/**\n * BFS up the reverse import graph to find affected page routes.\n * Also handles layout files — a layout change affects all pages\n * in the same directory and its descendants.\n *\n * Tracks the trigger chain (source file → ... → page) for each route.\n */\nexport function findAffectedRoutes(\n changedFiles: string[],\n graph: ImportGraph,\n projectRoot: string,\n maxRoutes: number = 0\n): AffectedRoute[] {\n const routeMap = new Map<string, AffectedRoute>();\n\n for (const file of changedFiles) {\n // BFS from this changed file up the reverse graph.\n // Track the path from source to each discovered node.\n const visited = new Set<string>();\n const queue: Array<{ path: string; depth: number; chain: string[] }> = [\n { path: file, depth: 0, chain: [file] },\n ];\n visited.add(file);\n\n while (queue.length > 0) {\n const { path, depth, chain } = queue.shift()!;\n\n // Check if this is a page file\n if (isPageFile(path)) {\n const route = pagePathToRoute(path);\n if (route !== null && !routeMap.has(route)) {\n routeMap.set(route, {\n pagePath: path,\n route,\n reason: depth === 0 ? \"direct\" : \"transitive\",\n depth,\n triggerChain: chain,\n });\n }\n }\n\n // Don't BFS beyond max depth\n if (depth >= MAX_DEPTH) continue;\n\n // Walk reverse edges\n const importers = graph.reverse.get(path);\n if (!importers) continue;\n\n for (const importer of importers) {\n if (visited.has(importer)) continue;\n visited.add(importer);\n queue.push({ path: importer, depth: depth + 1, chain: [...chain, importer] });\n }\n }\n }\n\n // Handle layout files: a layout wraps all pages in its directory subtree\n // via Next.js filesystem convention (no import required)\n for (const file of changedFiles) {\n if (!isLayoutFile(file)) continue;\n const layoutDir = getLayoutDir(file);\n\n for (const knownFile of graph.forward.keys()) {\n if (knownFile.startsWith(layoutDir) && isPageFile(knownFile)) {\n const route = pagePathToRoute(knownFile);\n if (route !== null && !routeMap.has(route)) {\n routeMap.set(route, {\n pagePath: knownFile,\n route,\n reason: \"transitive\",\n depth: 1,\n triggerChain: [file, knownFile],\n });\n }\n }\n }\n }\n\n const routes = Array.from(routeMap.values()).sort(\n (a, b) => a.depth - b.depth\n );\n\n if (maxRoutes > 0 && routes.length > maxRoutes) {\n const skipped = routes.length - maxRoutes;\n logger.warn(\n `Limiting to ${maxRoutes} route(s), skipping ${skipped} deeper route(s). Use --max-routes 0 for unlimited.`\n );\n const limited = routes.slice(0, maxRoutes);\n logger.dim(`Found ${limited.length} affected route(s) (of ${routes.length} total)`);\n return limited;\n }\n\n logger.dim(`Found ${routes.length} affected route(s)`);\n return routes;\n}\n\n/**\n * Find routes reachable from a single file via reverse import graph BFS.\n */\nexport function findRoutesForFile(\n file: string,\n graph: ImportGraph\n): string[] {\n const routes = new Set<string>();\n const start = normalizePath(file);\n const queue: Array<{ path: string; depth: number }> = [{ path: start, depth: 0 }];\n const visited = new Set<string>([start]);\n\n while (queue.length > 0) {\n const { path, depth } = queue.shift()!;\n if (isPageFile(path)) {\n const route = pagePathToRoute(path);\n if (route) routes.add(route);\n }\n if (depth >= MAX_DEPTH) continue;\n const importers = graph.reverse.get(path);\n if (!importers) continue;\n for (const importer of importers) {\n if (visited.has(importer)) continue;\n visited.add(importer);\n queue.push({ path: importer, depth: depth + 1 });\n }\n }\n return Array.from(routes);\n}\n","import { exec as execCb, execSync } from \"child_process\";\nimport { promisify } from \"util\";\nimport { rm, mkdtemp } from \"fs/promises\";\nimport { join } from \"path\";\nimport { tmpdir } from \"os\";\nimport { logger } from \"../logger.js\";\nimport { AfterbeforeError } from \"../errors.js\";\nimport { cleanupRegistry } from \"../cleanup.js\";\nimport { detectPackageManager } from \"../utils/pm.js\";\nimport type { WorktreeInfo } from \"../types.js\";\n\nconst exec = promisify(execCb);\n\nexport async function createWorktree(\n base: string,\n cwd: string\n): Promise<WorktreeInfo> {\n const worktreeDir = await mkdtemp(join(tmpdir(), \"afterbefore-wt-\"));\n\n logger.dim(`Creating worktree for ${base} at ${worktreeDir}`);\n try {\n await exec(`git worktree add \"${worktreeDir}\" \"${base}\"`, { cwd });\n } catch (err) {\n throw new AfterbeforeError(\n `Failed to create worktree for ref \"${base}\".`,\n `Make sure the branch/ref \"${base}\" exists. Run \"git branch -a\" to see available refs.`\n );\n }\n\n const pm = detectPackageManager(cwd);\n logger.dim(`Installing dependencies with ${pm}`);\n await exec(`${pm} install`, { cwd: worktreeDir });\n\n const cleanup = async () => {\n logger.dim(`Cleaning up worktree at ${worktreeDir}`);\n try {\n execSync(`git worktree remove --force \"${worktreeDir}\"`, {\n cwd,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n } catch {\n try {\n await rm(worktreeDir, { recursive: true, force: true });\n execSync(\"git worktree prune\", {\n cwd,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n } catch {\n logger.warn(`Failed to clean up worktree at ${worktreeDir}`);\n }\n }\n };\n\n cleanupRegistry.register(cleanup);\n\n return { path: worktreeDir, ref: base, cleanup };\n}\n","export class AfterbeforeError extends Error {\n constructor(\n message: string,\n public readonly suggestion: string\n ) {\n super(message);\n this.name = \"AfterbeforeError\";\n }\n}\n","import { existsSync } from \"fs\";\nimport { join } from \"path\";\n\nexport type PackageManager = \"npm\" | \"pnpm\" | \"yarn\" | \"bun\";\n\nexport function detectPackageManager(dir: string): PackageManager {\n if (existsSync(join(dir, \"bun.lockb\")) || existsSync(join(dir, \"bun.lock\")))\n return \"bun\";\n if (existsSync(join(dir, \"pnpm-lock.yaml\"))) return \"pnpm\";\n if (existsSync(join(dir, \"yarn.lock\"))) return \"yarn\";\n return \"npm\";\n}\n\n/** Get the exec command for running a package binary (like `next dev`) */\nexport function pmExec(pm: PackageManager): string {\n switch (pm) {\n case \"bun\":\n return \"bunx\";\n case \"pnpm\":\n return \"pnpm exec\";\n case \"yarn\":\n return \"yarn\";\n default:\n return \"npx\";\n }\n}\n","import { spawn } from \"child_process\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { logger } from \"../logger.js\";\nimport { AfterbeforeError } from \"../errors.js\";\nimport { detectPackageManager, pmExec } from \"../utils/pm.js\";\nimport type { ServerInfo } from \"../types.js\";\n\nfunction waitForServer(url: string, timeoutMs: number): Promise<void> {\n const start = Date.now();\n\n return new Promise((resolve, reject) => {\n const poll = async () => {\n if (Date.now() - start > timeoutMs) {\n reject(\n new AfterbeforeError(\n `Server at ${url} did not respond within ${timeoutMs / 1000}s.`,\n `Try running \"next dev\" manually to check for errors. Also check if port ${url.split(\":\").pop()} is already in use.`\n )\n );\n return;\n }\n\n try {\n await fetch(url);\n resolve();\n } catch {\n setTimeout(poll, 150);\n }\n };\n\n poll();\n });\n}\n\nexport async function startServer(\n projectDir: string,\n port: number\n): Promise<ServerInfo> {\n const url = `http://localhost:${port}`;\n const pm = detectPackageManager(projectDir);\n const exec = pmExec(pm);\n const [cmd, ...baseArgs] = exec.split(\" \");\n\n // Check for stale .next/dev/lock — another dev server may be running\n const lockFile = join(projectDir, \".next\", \"dev\", \"lock\");\n if (existsSync(lockFile)) {\n throw new AfterbeforeError(\n `Another Next.js dev server is running in ${projectDir} (.next/dev/lock exists).`,\n `Stop the other dev server first, or delete .next/dev/lock if it's stale.`\n );\n }\n\n logger.info(`Starting Next.js dev server on ${url} (using ${pm})`);\n\n const child = spawn(cmd, [...baseArgs, \"next\", \"dev\", \"-p\", String(port)], {\n cwd: projectDir,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n detached: true,\n });\n\n // Log stderr for debugging but don't fail on it\n child.stderr?.on(\"data\", (data: Buffer) => {\n const msg = data.toString().trim();\n if (msg) logger.dim(`[next:${port}] ${msg}`);\n });\n\n await waitForServer(url, 60_000);\n logger.success(`Server ready at ${url}`);\n\n return { port, process: child, url };\n}\n\nexport async function stopServer(server: ServerInfo): Promise<void> {\n logger.dim(`Stopping server on port ${server.port}`);\n const pid = server.process.pid;\n\n // Kill the entire process group (spawned with detached: true) so that\n // grandchild processes like `next dev` don't survive as zombies.\n try {\n process.kill(-pid!, \"SIGTERM\");\n } catch {\n // Process group may already be dead\n }\n\n // Wait briefly for graceful shutdown\n await new Promise<void>((resolve) => {\n const timeout = setTimeout(() => {\n try {\n process.kill(-pid!, \"SIGKILL\");\n } catch {\n // Already dead\n }\n resolve();\n }, 5_000);\n\n server.process.on(\"exit\", () => {\n clearTimeout(timeout);\n resolve();\n });\n });\n}\n","import { join } from \"path\";\nimport { execSync } from \"child_process\";\nimport { chromium, devices, type Browser, type BrowserContext, type Page } from \"playwright\";\nimport { logger } from \"../logger.js\";\nimport type { CaptureResult, CaptureOptions, CaptureTask, ScenarioAction } from \"../types.js\";\nimport { detectTabs, sanitizeTabLabel } from \"../utils/tabs.js\";\nimport { detectSections, tagSectionOnPage, cleanupSectionTags, sanitizeSectionLabel, hideSectionHeading, showSectionHeading } from \"../utils/sections.js\";\nimport { normalizePath, sanitizeLabel } from \"../utils/path.js\";\n\nexport async function launchBrowser(): Promise<Browser> {\n try {\n return await chromium.launch();\n } catch {\n logger.dim(\"Chromium not found, installing...\");\n execSync(\"npx playwright install chromium\", {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n return await chromium.launch();\n }\n}\n\ninterface TaggedComponentInstance {\n source: string;\n name: string;\n index: number;\n componentKey: string;\n parentKey: string;\n}\n\nconst MAX_COMPONENT_INSTANCES_PER_SOURCE = 5;\n\nfunction sanitizeComponentLabel(label: string): string {\n const noExt = label.replace(/\\.[a-z0-9]+$/i, \"\");\n return sanitizeLabel(noExt, 60);\n}\n\nfunction groupBySource(\n instances: TaggedComponentInstance[]\n): Map<string, TaggedComponentInstance[]> {\n const map = new Map<string, TaggedComponentInstance[]>();\n for (const instance of instances) {\n const list = map.get(instance.source) ?? [];\n list.push(instance);\n map.set(instance.source, list);\n }\n for (const [source, list] of map.entries()) {\n list.sort((a, b) => a.index - b.index);\n map.set(source, list);\n }\n return map;\n}\n\nasync function captureByAttr(\n page: Page,\n attrName: string,\n attrValue: string,\n outPath: string\n): Promise<boolean> {\n try {\n const locator = page.locator(`[${attrName}=\"${attrValue}\"]`).first();\n if ((await locator.count()) === 0) return false;\n await locator.screenshot({ path: outPath });\n return true;\n } catch {\n return false;\n }\n}\n\nasync function tagChangedComponentInstances(\n page: Page,\n changedComponents: string[],\n maxPerSource: number = MAX_COMPONENT_INSTANCES_PER_SOURCE\n): Promise<TaggedComponentInstance[]> {\n const normalized = changedComponents.map(normalizePath);\n\n return page.evaluate(\n ({ changed, maxPerSource }) => {\n type Target = { original: string; variants: string[] };\n type Candidate = {\n el: Element;\n parent: Element;\n name: string;\n top: number;\n left: number;\n };\n type Tagged = {\n source: string;\n name: string;\n index: number;\n componentKey: string;\n parentKey: string;\n };\n\n const normalize = (p: string): string =>\n p.replace(/\\\\/g, \"/\").replace(/^\\.\\//, \"\");\n\n const targets: Target[] = changed.map((original) => {\n const norm = normalize(original);\n const noSrc = norm.replace(/^src\\//, \"\");\n return {\n original: norm,\n variants: Array.from(new Set([norm, noSrc, `src/${noSrc}`])),\n };\n });\n\n // Cleanup from previous runs on this page\n for (const el of document.querySelectorAll(\"[data-ab-comp-key]\")) {\n el.removeAttribute(\"data-ab-comp-key\");\n }\n for (const el of document.querySelectorAll(\"[data-ab-parent-key]\")) {\n el.removeAttribute(\"data-ab-parent-key\");\n }\n\n const getFiber = (el: Element): any => {\n const anyEl = el as any;\n for (const key in anyEl) {\n if (\n key.startsWith(\"__reactFiber$\") ||\n key.startsWith(\"__reactInternalInstance$\")\n ) {\n return anyEl[key];\n }\n }\n return null;\n };\n\n const matchSource = (rawSource: string): string | null => {\n const normalizedSource = normalize(rawSource);\n for (const target of targets) {\n for (const variant of target.variants) {\n if (\n normalizedSource === variant ||\n normalizedSource.endsWith(`/${variant}`)\n ) {\n return target.original;\n }\n }\n }\n return null;\n };\n\n const getComponentMatch = (\n el: Element\n ): { source: string; name: string } | null => {\n const fiber = getFiber(el);\n if (!fiber) return null;\n\n let current = fiber;\n while (current) {\n const sourceFile =\n current?._debugSource?.fileName ??\n current?.elementType?._debugSource?.fileName ??\n current?.type?._debugSource?.fileName;\n if (typeof sourceFile === \"string\") {\n const source = matchSource(sourceFile);\n if (source) {\n const type = current?.elementType ?? current?.type;\n const name =\n (typeof type === \"function\" &&\n (type.displayName || type.name)) ||\n (typeof type === \"string\" ? type : undefined) ||\n source.split(\"/\").pop() ||\n source;\n return { source, name };\n }\n }\n current = current.return;\n }\n\n return null;\n };\n\n const pickParentContainer = (el: Element): Element => {\n const ownRect = el.getBoundingClientRect();\n let parent: Element | null = el.parentElement;\n\n while (parent && parent !== document.body) {\n const rect = parent.getBoundingClientRect();\n if (\n rect.width >= ownRect.width * 1.15 ||\n rect.height >= ownRect.height * 1.15\n ) {\n return parent;\n }\n parent = parent.parentElement;\n }\n\n return el.parentElement ?? el;\n };\n\n const bySource = new Map<string, Candidate[]>();\n const elements = Array.from(document.querySelectorAll(\"body *\"));\n\n for (const el of elements) {\n const rect = el.getBoundingClientRect();\n if (rect.width < 4 || rect.height < 4) continue;\n if (rect.bottom < 0 || rect.right < 0) continue;\n\n const match = getComponentMatch(el);\n if (!match) continue;\n\n const parent = el.parentElement;\n if (parent) {\n const parentMatch = getComponentMatch(parent);\n if (parentMatch && parentMatch.source === match.source) {\n continue;\n }\n }\n\n const list = bySource.get(match.source) ?? [];\n list.push({\n el,\n parent: pickParentContainer(el),\n name: match.name,\n top: rect.top + window.scrollY,\n left: rect.left + window.scrollX,\n });\n bySource.set(match.source, list);\n }\n\n // Fallback: if fiber detection found nothing for a target, try heading match\n for (const target of targets) {\n if (bySource.has(target.original) && (bySource.get(target.original)!).length > 0) continue;\n\n const fileName = target.original.split(\"/\").pop() ?? \"\";\n const componentName = fileName.replace(/\\.[a-z0-9]+$/i, \"\");\n if (!componentName || componentName.length < 2) continue;\n\n const pattern = new RegExp(\"\\\\b\" + componentName + \"\\\\b\", \"i\");\n const headings = Array.from(document.querySelectorAll(\"h1, h2, h3, h4\"));\n\n for (const heading of headings) {\n const text = (heading.textContent ?? \"\").trim();\n if (!pattern.test(text)) continue;\n\n // Walk up to find section container (same pattern as sections.ts)\n let container: Element = heading;\n const headingTag = heading.tagName.toLowerCase();\n let walkParent: Element | null = heading.parentElement;\n\n while (walkParent && walkParent !== document.body) {\n const parentTag = walkParent.tagName.toLowerCase();\n if (parentTag === \"section\" || parentTag === \"article\") { container = walkParent; break; }\n if (walkParent.getAttribute(\"role\") === \"region\") { container = walkParent; break; }\n if (parentTag === \"body\" || parentTag === \"main\") break;\n const siblingHeadings = walkParent.querySelectorAll(`:scope > * ${headingTag}, :scope > ${headingTag}`);\n if (siblingHeadings.length > 1) break;\n container = walkParent;\n walkParent = walkParent.parentElement;\n }\n\n const rect = container.getBoundingClientRect();\n if (rect.width < 4 || rect.height < 4) continue;\n\n const list = bySource.get(target.original) ?? [];\n list.push({\n el: container,\n parent: pickParentContainer(container),\n name: componentName,\n top: rect.top + window.scrollY,\n left: rect.left + window.scrollX,\n });\n bySource.set(target.original, list);\n break; // Take first heading match per component\n }\n }\n\n const tagged: Tagged[] = [];\n\n for (let sourceIndex = 0; sourceIndex < targets.length; sourceIndex++) {\n const source = targets[sourceIndex].original;\n const list = bySource.get(source) ?? [];\n list.sort((a, b) => (a.top === b.top ? a.left - b.left : a.top - b.top));\n\n // Dimension-based dedup: keep first instance per distinct size (±2px)\n const deduped: Candidate[] = [];\n const seenDims: { w: number; h: number }[] = [];\n for (const candidate of list) {\n const rect = candidate.el.getBoundingClientRect();\n const isDuplicate = seenDims.some(\n (d) => Math.abs(d.w - rect.width) <= 2 && Math.abs(d.h - rect.height) <= 2\n );\n if (!isDuplicate) {\n seenDims.push({ w: rect.width, h: rect.height });\n deduped.push(candidate);\n }\n }\n\n const limit = Math.min(deduped.length, maxPerSource);\n for (let i = 0; i < limit; i++) {\n const instance = deduped[i];\n const componentKey = `ab-comp-${sourceIndex}-${i}`;\n const parentKey = `ab-parent-${sourceIndex}-${i}`;\n\n instance.el.setAttribute(\"data-ab-comp-key\", componentKey);\n instance.parent.setAttribute(\"data-ab-parent-key\", parentKey);\n\n tagged.push({\n source,\n name: instance.name,\n index: i,\n componentKey,\n parentKey,\n });\n }\n }\n\n return tagged;\n },\n { changed: normalized, maxPerSource }\n );\n}\n\nasync function captureComponentInstances(\n afterPage: Page,\n beforePage: Page,\n changedComponents: string[],\n capturePrefix: string,\n captureLabel: string,\n outputDir: string,\n results: CaptureResult[]\n): Promise<void> {\n const deduped = Array.from(new Set(changedComponents.map(normalizePath)));\n const [afterInstances, beforeInstances] = await Promise.all([\n tagChangedComponentInstances(afterPage, deduped),\n tagChangedComponentInstances(beforePage, deduped),\n ]);\n\n logger.dim(` Component detection on ${captureLabel}: ${deduped.length} source(s), ${afterInstances.length} after / ${beforeInstances.length} before instance(s)`);\n\n const afterBySource = groupBySource(afterInstances);\n const beforeBySource = groupBySource(beforeInstances);\n\n for (const source of deduped) {\n const afterList = afterBySource.get(source) ?? [];\n const beforeList = beforeBySource.get(source) ?? [];\n const pairCount = Math.min(afterList.length, beforeList.length);\n if (pairCount === 0) {\n if (afterList.length > 0 || beforeList.length > 0) {\n logger.dim(` ${source}: ${afterList.length} after / ${beforeList.length} before (skipping unpaired)`);\n }\n continue;\n }\n\n for (let pairIndex = 0; pairIndex < pairCount; pairIndex++) {\n const afterInstance = afterList[pairIndex];\n const beforeInstance = beforeList[pairIndex];\n const sourceSlug = sanitizeComponentLabel(source) || \"component\";\n const itemSlug = `${sourceSlug}-${pairIndex + 1}`;\n const componentName =\n afterInstance.name || beforeInstance.name || source.split(\"/\").pop() || source;\n const baseLabel = `${captureLabel} [${componentName} #${pairIndex + 1}]`;\n\n const parentPrefix = `${capturePrefix}~cmp.${itemSlug}~parent`;\n const parentBeforePath = join(outputDir, `${parentPrefix}-before.png`);\n const parentAfterPath = join(outputDir, `${parentPrefix}-after.png`);\n const [parentBeforeOk, parentAfterOk] = await Promise.all([\n captureByAttr(\n beforePage,\n \"data-ab-parent-key\",\n beforeInstance.parentKey,\n parentBeforePath\n ),\n captureByAttr(\n afterPage,\n \"data-ab-parent-key\",\n afterInstance.parentKey,\n parentAfterPath\n ),\n ]);\n if (parentBeforeOk && parentAfterOk) {\n results.push({\n route: `${baseLabel} [parent]`,\n prefix: parentPrefix,\n beforePath: parentBeforePath,\n afterPath: parentAfterPath,\n level: \"parent\",\n parentPrefix: capturePrefix,\n componentSource: source,\n componentName,\n });\n }\n\n const componentPrefix = `${capturePrefix}~cmp.${itemSlug}~component`;\n const componentBeforePath = join(outputDir, `${componentPrefix}-before.png`);\n const componentAfterPath = join(outputDir, `${componentPrefix}-after.png`);\n const [componentBeforeOk, componentAfterOk] = await Promise.all([\n captureByAttr(\n beforePage,\n \"data-ab-comp-key\",\n beforeInstance.componentKey,\n componentBeforePath\n ),\n captureByAttr(\n afterPage,\n \"data-ab-comp-key\",\n afterInstance.componentKey,\n componentAfterPath\n ),\n ]);\n if (componentBeforeOk && componentAfterOk) {\n results.push({\n route: `${baseLabel} [component]`,\n prefix: componentPrefix,\n beforePath: componentBeforePath,\n afterPath: componentAfterPath,\n level: \"component\",\n parentPrefix: capturePrefix,\n componentSource: source,\n componentName,\n });\n }\n\n }\n }\n}\n\nasync function captureAutoSections(\n afterPage: Page,\n beforePage: Page,\n parentPrefix: string,\n parentLabel: string,\n outputDir: string,\n options: CaptureOptions,\n settle: (page: Page) => Promise<void>,\n results: CaptureResult[]\n): Promise<void> {\n const sections = await detectSections(afterPage, options.maxSectionsPerRoute);\n if (sections.length === 0) return;\n\n const usedSlugs = new Set<string>();\n\n for (const section of sections) {\n let slug = sanitizeSectionLabel(section.label);\n if (!slug) continue;\n\n // Handle duplicate slugs\n if (usedSlugs.has(slug)) {\n let suffix = 2;\n while (usedSlugs.has(`${slug}-${suffix}`)) suffix++;\n slug = `${slug}-${suffix}`;\n }\n usedSlugs.add(slug);\n\n const sectionPrefix = `${parentPrefix}~s.${slug}`;\n const sectionLabel = `${parentLabel} [${section.label}]`;\n const sectionAfterPath = join(outputDir, `${sectionPrefix}-after.png`);\n const sectionBeforePath = join(outputDir, `${sectionPrefix}-before.png`);\n\n try {\n // Hide heading so section screenshot shows only the component content\n await hideSectionHeading(afterPage, section.index);\n\n // Screenshot section on after page\n const afterEl = afterPage.locator(`[data-ab-section=\"${section.index}\"]`).first();\n await afterEl.screenshot({ path: sectionAfterPath });\n\n await showSectionHeading(afterPage, section.index);\n\n // Try to find and tag matching section on before page\n const found = await tagSectionOnPage(beforePage, section.label, section.index);\n if (found) {\n await hideSectionHeading(beforePage, section.index);\n\n const beforeEl = beforePage.locator(`[data-ab-section=\"${section.index}\"]`).first();\n await beforeEl.screenshot({ path: sectionBeforePath });\n\n await showSectionHeading(beforePage, section.index);\n } else {\n // Section is new — skip (full-page already shows it)\n continue;\n }\n\n results.push({\n route: sectionLabel,\n prefix: sectionPrefix,\n beforePath: sectionBeforePath,\n afterPath: sectionAfterPath,\n level: \"section\",\n });\n } catch {\n // Element screenshot failed — skip silently\n }\n }\n\n // Clean up tags on both pages\n await cleanupSectionTags(afterPage);\n await cleanupSectionTags(beforePage);\n}\n\nasync function captureAutoTabs(\n afterPage: Page,\n beforePage: Page,\n task: CaptureTask,\n beforeUrl: string,\n afterUrl: string,\n outputDir: string,\n options: CaptureOptions,\n settle: (page: Page) => Promise<void>,\n results: CaptureResult[]\n): Promise<void> {\n const tabs = await detectTabs(afterPage, options.maxTabsPerRoute);\n const usedPrefixes = new Set<string>();\n\n for (const tab of tabs) {\n let slug = sanitizeTabLabel(tab.label);\n if (!slug) continue;\n\n // Handle duplicate slugs\n if (usedPrefixes.has(slug)) {\n let suffix = 2;\n while (usedPrefixes.has(`${slug}-${suffix}`)) suffix++;\n slug = `${slug}-${suffix}`;\n }\n usedPrefixes.add(slug);\n\n const tabPrefix = `${task.prefix}~${slug}`;\n const tabLabel = `${task.label} [${tab.label}]`;\n const tabBeforePath = join(outputDir, `${tabPrefix}-before.png`);\n const tabAfterPath = join(outputDir, `${tabPrefix}-after.png`);\n\n try {\n // Click tab on after page\n const afterUrlBefore = afterPage.url();\n await afterPage.getByRole(\"tab\", { name: tab.label }).first().click();\n await settle(afterPage);\n\n // Check if click navigated away\n if (afterPage.url() !== afterUrlBefore) {\n await afterPage.goBack({ waitUntil: \"networkidle\" });\n await settle(afterPage);\n continue;\n }\n\n await afterPage.screenshot({ path: tabAfterPath, fullPage: true });\n\n // Try clicking same tab on before page (may not exist)\n try {\n const beforeUrlBefore = beforePage.url();\n await beforePage.getByRole(\"tab\", { name: tab.label }).first().click({ timeout: 2000 });\n await settle(beforePage);\n\n if (beforePage.url() !== beforeUrlBefore) {\n await beforePage.goBack({ waitUntil: \"networkidle\" });\n await settle(beforePage);\n // Still capture — before stays in default state\n await beforePage.screenshot({ path: tabBeforePath, fullPage: true });\n } else {\n await beforePage.screenshot({ path: tabBeforePath, fullPage: true });\n }\n } catch {\n // Tab doesn't exist on before page — capture default state\n await beforePage.screenshot({ path: tabBeforePath, fullPage: true });\n }\n\n results.push({\n route: tabLabel,\n prefix: tabPrefix,\n beforePath: tabBeforePath,\n afterPath: tabAfterPath,\n level: \"tab\",\n });\n\n // Component detection within this tab view\n if ((task.changedComponents?.length ?? 0) > 0) {\n await captureComponentInstances(\n afterPage, beforePage,\n task.changedComponents!,\n tabPrefix, tabLabel,\n outputDir,\n results\n );\n }\n\n // Auto-detect sections within this tab view\n if (options.autoSections && !task.skipAutoSections) {\n await captureAutoSections(\n afterPage, beforePage,\n tabPrefix, tabLabel,\n outputDir, options, settle, results\n );\n }\n } catch {\n // Tab click failed — skip this tab silently\n logger.dim(` Skipped tab \"${tab.label}\" on ${task.route}`);\n }\n }\n\n // Navigate back to route to reset state for next task\n if (tabs.length > 0) {\n await Promise.all([\n beforePage.goto(`${beforeUrl}${task.route}`, { waitUntil: \"networkidle\" }),\n afterPage.goto(`${afterUrl}${task.route}`, { waitUntil: \"networkidle\" }),\n ]);\n }\n}\n\nasync function captureOneRoute(\n task: CaptureTask,\n beforeCtx: BrowserContext,\n afterCtx: BrowserContext,\n beforeUrl: string,\n afterUrl: string,\n outputDir: string,\n options: CaptureOptions\n): Promise<CaptureResult[]> {\n const results: CaptureResult[] = [];\n const [beforePage, afterPage] = await Promise.all([\n beforeCtx.newPage(),\n afterCtx.newPage(),\n ]);\n\n try {\n const beforePath = join(outputDir, `${task.prefix}-before.png`);\n const afterPath = join(outputDir, `${task.prefix}-after.png`);\n\n const settle = async (page: Page) => {\n await page.evaluate(\"document.fonts.ready\");\n if (options.delay > 0) {\n await page.waitForTimeout(options.delay);\n }\n };\n\n const performActions = async (page: Page, actions: ScenarioAction[]) => {\n for (const action of actions) {\n if (action.click) {\n await page.locator(action.click).first().click();\n }\n if (action.scroll) {\n await page.locator(action.scroll).first().scrollIntoViewIfNeeded();\n }\n if (action.wait && action.wait > 0) {\n await page.waitForTimeout(action.wait);\n }\n await settle(page);\n }\n };\n\n const screenshot = async (page: Page, path: string) => {\n if (task.selector) {\n const el = page.locator(task.selector).first();\n await el.screenshot({ path });\n } else {\n await page.screenshot({ path, fullPage: true });\n }\n };\n\n // Capture before and after in parallel\n await Promise.all([\n beforePage\n .goto(`${beforeUrl}${task.route}`, { waitUntil: \"networkidle\" })\n .then(() => settle(beforePage))\n .then(() => task.actions ? performActions(beforePage, task.actions) : undefined)\n .then(() => screenshot(beforePage, beforePath)),\n afterPage\n .goto(`${afterUrl}${task.route}`, { waitUntil: \"networkidle\" })\n .then(() => settle(afterPage))\n .then(() => task.actions ? performActions(afterPage, task.actions) : undefined)\n .then(() => screenshot(afterPage, afterPath)),\n ]);\n\n results.push({ route: task.label, prefix: task.prefix, beforePath, afterPath, level: \"page\" });\n\n // Per changed component: emit context/parent/component captures\n if (\n (task.changedComponents?.length ?? 0) > 0 &&\n !task.actions &&\n !task.selector\n ) {\n await captureComponentInstances(\n afterPage, beforePage,\n task.changedComponents!,\n task.prefix, task.label,\n outputDir,\n results\n );\n }\n\n // Auto-detect and capture section states (on default page view)\n if (options.autoSections && !task.actions && !task.selector && !task.skipAutoSections) {\n await captureAutoSections(\n afterPage, beforePage,\n task.prefix, task.label,\n outputDir, options, settle, results\n );\n }\n\n // Auto-detect and capture tab states\n if (options.autoTabs && !task.actions && !task.selector && !task.skipAutoTabs) {\n await captureAutoTabs(\n afterPage, beforePage,\n task, beforeUrl, afterUrl,\n outputDir, options, settle, results\n );\n }\n } finally {\n await Promise.all([beforePage.close(), afterPage.close()]);\n }\n\n return results;\n}\n\nconst BATCH_SIZE = 3;\n\nexport async function captureRoutes(\n tasks: CaptureTask[],\n beforeUrl: string,\n afterUrl: string,\n outputDir: string,\n options: CaptureOptions\n): Promise<CaptureResult[]> {\n const browser = options.browser ?? (await launchBrowser());\n const ownsBrowser = !options.browser;\n const results: CaptureResult[] = [];\n\n try {\n // Build viewport/device context options\n const device = options.device ? devices[options.device] : undefined;\n const contextOpts = device\n ? { ...device }\n : {\n viewport: { width: options.width, height: options.height },\n deviceScaleFactor: 2,\n };\n\n // Two contexts so before/after can be captured in parallel\n const [beforeCtx, afterCtx] = await Promise.all([\n browser.newContext(contextOpts),\n browser.newContext(contextOpts),\n ]);\n\n for (let batchStart = 0; batchStart < tasks.length; batchStart += BATCH_SIZE) {\n const batch = tasks.slice(batchStart, batchStart + BATCH_SIZE);\n const batchResults = await Promise.allSettled(\n batch.map((task, idx) => {\n options.onProgress?.(batchStart + idx + 1, task.label);\n return captureOneRoute(task, beforeCtx, afterCtx, beforeUrl, afterUrl, outputDir, options);\n })\n );\n for (const result of batchResults) {\n if (result.status === \"fulfilled\") {\n results.push(...result.value);\n } else {\n logger.dim(`Capture failed: ${result.reason}`);\n }\n }\n }\n\n await Promise.all([beforeCtx.close(), afterCtx.close()]);\n } finally {\n if (ownsBrowser) {\n await browser.close();\n }\n }\n\n return results;\n}\n","import type { Page } from \"playwright\";\nimport { sanitizeLabel } from \"./path.js\";\n\nexport interface DetectedTab {\n label: string;\n selected: boolean;\n}\n\n/**\n * Detect top-level ARIA tabs on the page.\n * Skips tablists nested inside tabpanels to prevent exponential explosion.\n * Returns only inactive tabs (up to maxTabs).\n */\nexport async function detectTabs(\n page: Page,\n maxTabs: number\n): Promise<DetectedTab[]> {\n const allTabs: DetectedTab[] = await page.evaluate(() => {\n const tablists = document.querySelectorAll('[role=\"tablist\"]');\n const results: { label: string; selected: boolean }[] = [];\n\n for (const tablist of tablists) {\n // Skip tablists nested inside a tabpanel\n if (tablist.closest('[role=\"tabpanel\"]')) continue;\n\n const tabs = tablist.querySelectorAll('[role=\"tab\"]');\n for (const tab of tabs) {\n const label = (tab.textContent ?? \"\").trim();\n if (!label) continue;\n const selected =\n tab.getAttribute(\"aria-selected\") === \"true\" ||\n tab.getAttribute(\"data-state\") === \"active\";\n results.push({ label, selected });\n }\n }\n return results;\n });\n\n // Return only inactive tabs, capped at maxTabs\n return allTabs.filter((t) => !t.selected).slice(0, maxTabs);\n}\n\n/**\n * Convert tab label to a filename-safe slug.\n * \"Components & Hooks\" → \"components-hooks\"\n */\nexport function sanitizeTabLabel(label: string): string {\n return sanitizeLabel(label);\n}\n","import type { Page } from \"playwright\";\nimport { sanitizeLabel } from \"./path.js\";\n\nexport interface DetectedSection {\n label: string;\n index: number;\n}\n\n/**\n * Detect heading-labeled sections on the page.\n * Finds h2/h3 headings, walks up to their section container, and tags each\n * with a `data-ab-section` attribute for reliable Playwright targeting.\n * Returns empty array if fewer than 3 headings found (not a showcase page).\n */\nexport async function detectSections(\n page: Page,\n maxSections: number\n): Promise<DetectedSection[]> {\n const sections: DetectedSection[] = await page.evaluate((max: number) => {\n const headings = Array.from(document.querySelectorAll(\"h2, h3\"));\n\n // Filter out headings with empty text\n const valid = headings.filter((h) => (h.textContent ?? \"\").trim().length > 0);\n if (valid.length < 3) return [];\n\n const results: { label: string; index: number }[] = [];\n const tagged = new Set<Element>();\n let idx = 0;\n\n for (const heading of valid) {\n if (idx >= max) break;\n\n // Walk up to find the section container\n const container = findSectionContainer(heading);\n if (!container || tagged.has(container)) continue;\n\n // Skip containers that are too large (>90% of page height)\n if (container.scrollHeight > document.documentElement.scrollHeight * 0.9) continue;\n\n container.setAttribute(\"data-ab-section\", String(idx));\n heading.setAttribute(\"data-ab-heading\", String(idx));\n tagged.add(container);\n\n results.push({\n label: (heading.textContent ?? \"\").trim(),\n index: idx,\n });\n idx++;\n }\n\n return results;\n\n function findSectionContainer(heading: Element): Element | null {\n let el: Element | null = heading;\n const headingTag = heading.tagName.toLowerCase();\n\n while (el && el !== document.body) {\n const parentEl: Element | null = el.parentElement;\n if (!parentEl) return el;\n\n // Stop at semantic section elements\n const parentTag = parentEl.tagName.toLowerCase();\n if (parentTag === \"section\" || parentTag === \"article\") return parentEl;\n if (parentEl.getAttribute(\"role\") === \"region\") return parentEl;\n\n // Stop at body/main — return current element\n if (parentTag === \"body\" || parentTag === \"main\") return el;\n\n // Stop when parent has multiple headings at the same level\n // (indicates current element IS the section)\n const siblingHeadings = parentEl.querySelectorAll(`:scope > * ${headingTag}, :scope > ${headingTag}`);\n if (siblingHeadings.length > 1) return el;\n\n el = parentEl;\n }\n\n return el;\n }\n }, maxSections);\n\n return sections;\n}\n\n/**\n * Find a section on the page by heading text and tag it with `data-ab-section`.\n * Used to match sections on the before page by heading text from the after page.\n * Returns false if heading not found.\n */\nexport async function tagSectionOnPage(\n page: Page,\n headingText: string,\n index: number\n): Promise<boolean> {\n return page.evaluate(\n ({ text, idx }: { text: string; idx: number }) => {\n const headings = Array.from(document.querySelectorAll(\"h2, h3\"));\n const match = headings.find(\n (h) => (h.textContent ?? \"\").trim() === text\n );\n if (!match) return false;\n\n // Walk up to find the section container (same logic as detectSections)\n let el: Element | null = match;\n const headingTag = match.tagName.toLowerCase();\n\n while (el && el !== document.body) {\n const parentEl: Element | null = el.parentElement;\n if (!parentEl) break;\n\n const parentTag = parentEl.tagName.toLowerCase();\n if (parentTag === \"section\" || parentTag === \"article\") {\n el = parentEl;\n break;\n }\n if (parentEl.getAttribute(\"role\") === \"region\") {\n el = parentEl;\n break;\n }\n if (parentTag === \"body\" || parentTag === \"main\") break;\n\n const siblingHeadings = parentEl.querySelectorAll(\n `:scope > * ${headingTag}, :scope > ${headingTag}`\n );\n if (siblingHeadings.length > 1) break;\n\n el = parentEl;\n }\n\n if (el) {\n el.setAttribute(\"data-ab-section\", String(idx));\n match.setAttribute(\"data-ab-heading\", String(idx));\n return true;\n }\n return false;\n },\n { text: headingText, idx: index }\n );\n}\n\n/**\n * Hide the heading that triggered a section detection.\n * Uses display:none to collapse the space it occupies.\n */\nexport async function hideSectionHeading(page: Page, sectionIndex: number): Promise<void> {\n await page.evaluate((idx: number) => {\n const heading = document.querySelector(`[data-ab-heading=\"${idx}\"]`);\n if (heading instanceof HTMLElement) {\n heading.style.display = \"none\";\n }\n }, sectionIndex);\n}\n\n/**\n * Restore a previously hidden section heading.\n */\nexport async function showSectionHeading(page: Page, sectionIndex: number): Promise<void> {\n await page.evaluate((idx: number) => {\n const heading = document.querySelector(`[data-ab-heading=\"${idx}\"]`);\n if (heading instanceof HTMLElement) {\n heading.style.display = \"\";\n }\n }, sectionIndex);\n}\n\n/**\n * Remove all `data-ab-section` and `data-ab-heading` attributes from the page.\n */\nexport async function cleanupSectionTags(page: Page): Promise<void> {\n await page.evaluate(() => {\n for (const el of document.querySelectorAll(\"[data-ab-section]\")) {\n el.removeAttribute(\"data-ab-section\");\n }\n for (const el of document.querySelectorAll(\"[data-ab-heading]\")) {\n el.removeAttribute(\"data-ab-heading\");\n if (el instanceof HTMLElement) {\n el.style.display = \"\";\n }\n }\n });\n}\n\n/**\n * Convert section label to a filename-safe slug.\n * \"Card Component\" → \"card-component\"\n */\nexport function sanitizeSectionLabel(label: string): string {\n return sanitizeLabel(label);\n}\n","import { join, dirname } from \"path\";\nimport { unlinkSync } from \"fs\";\nimport { ODiffServer } from \"odiff-bin\";\nimport sharp from \"sharp\";\nimport type { CaptureResult, CompareResult } from \"../types.js\";\n\nasync function trimImage(\n path: string\n): Promise<{ data: Buffer; width: number; height: number }> {\n const { data, info } = await sharp(path)\n .trim({ threshold: 50 })\n .toBuffer({ resolveWithObject: true });\n return { data, width: info.width, height: info.height };\n}\n\nasync function generateComposite(\n beforePath: string,\n afterPath: string,\n outputPath: string,\n bgColor: string\n): Promise<void> {\n // Step 1: trim white page background from source images\n const [beforeTrimmed, afterTrimmed] = await Promise.all([\n trimImage(beforePath),\n trimImage(afterPath),\n ]);\n\n const imgW = Math.max(beforeTrimmed.width, afterTrimmed.width);\n const imgH = Math.max(beforeTrimmed.height, afterTrimmed.height);\n\n // Step 2: compute canvas layout from trimmed sizes\n const PADDING = 40;\n const GAP = 40;\n const LABEL_H = 70;\n const canvasW = Math.max(600, Math.min(2400, imgW * 2 + GAP + PADDING * 2));\n const canvasH = Math.max(300, Math.min(2400, imgH + LABEL_H + PADDING * 2));\n const maxImgH = canvasH - PADDING * 2 - LABEL_H;\n const colW = Math.floor((canvasW - PADDING * 2 - GAP) / 2);\n\n // Step 3: resize trimmed images (natural shape preserved, no corner rounding)\n const [beforeBuf, afterBuf] = await Promise.all(\n [beforeTrimmed, afterTrimmed].map(async (trimmed) => {\n return await sharp(trimmed.data)\n .resize(colW, maxImgH, { fit: \"inside\" })\n .toBuffer({ resolveWithObject: true });\n })\n );\n\n const beforeLeft = PADDING + Math.floor((colW - beforeBuf.info.width) / 2);\n const beforeTop = PADDING + Math.floor((maxImgH - beforeBuf.info.height) / 2);\n const afterLeft = PADDING + colW + GAP + Math.floor((colW - afterBuf.info.width) / 2);\n const afterTop = PADDING + Math.floor((maxImgH - afterBuf.info.height) / 2);\n\n const labelY = PADDING + maxImgH + 20;\n const beforeLabelX = PADDING + Math.floor(colW / 2);\n const afterLabelX = PADDING + colW + GAP + Math.floor(colW / 2);\n\n const labelSvg = Buffer.from(\n `<svg width=\"${canvasW}\" height=\"${canvasH}\">\n <text x=\"${beforeLabelX}\" y=\"${labelY}\" text-anchor=\"middle\"\n font-family=\"system-ui, sans-serif\" font-size=\"30\" font-weight=\"500\"\n fill=\"#888\">Before</text>\n <text x=\"${afterLabelX}\" y=\"${labelY}\" text-anchor=\"middle\"\n font-family=\"system-ui, sans-serif\" font-size=\"30\" font-weight=\"500\"\n fill=\"#22c55e\">After</text>\n </svg>`\n );\n\n await sharp({\n create: {\n width: canvasW,\n height: canvasH,\n channels: 4,\n background: bgColor,\n },\n })\n .composite([\n { input: beforeBuf.data, left: beforeLeft, top: beforeTop },\n { input: afterBuf.data, left: afterLeft, top: afterTop },\n { input: labelSvg, left: 0, top: 0 },\n ])\n .png()\n .toFile(outputPath);\n}\n\nexport interface CompareOptions {\n bgColor: string;\n}\n\nasync function compareOne(\n capture: CaptureResult,\n outputDir: string,\n threshold: number,\n server: ODiffServer,\n options: CompareOptions\n): Promise<CompareResult> {\n const diffPath = join(outputDir, `${capture.prefix}-diff.png`);\n const dir = dirname(capture.beforePath);\n const comparePath = join(dir, `${capture.prefix}-compare.png`);\n\n const result = await server.compare(\n capture.beforePath,\n capture.afterPath,\n diffPath,\n { threshold: 0.1, antialiasing: true }\n );\n\n // Always clean up diff heatmap — it's an implementation artifact\n try { unlinkSync(diffPath); } catch {}\n\n let diffPixels = 0;\n let totalPixels = 0;\n let diffPercentage = 0;\n let changed = false;\n\n if (result.match) {\n // No change\n } else if (result.reason === \"pixel-diff\") {\n diffPixels = result.diffCount;\n diffPercentage = result.diffPercentage;\n changed = diffPercentage > threshold;\n } else {\n // layout-diff or file-not-exists — treat as changed\n diffPercentage = 100;\n changed = true;\n }\n\n await generateComposite(\n capture.beforePath,\n capture.afterPath,\n comparePath,\n options.bgColor\n );\n\n return {\n route: capture.route,\n prefix: capture.prefix,\n beforePath: capture.beforePath,\n afterPath: capture.afterPath,\n comparePath,\n diffPixels,\n totalPixels,\n diffPercentage,\n changed,\n };\n}\n\nexport async function compareScreenshots(\n captures: CaptureResult[],\n outputDir: string,\n threshold: number = 0.1,\n options: CompareOptions\n): Promise<CompareResult[]> {\n const server = new ODiffServer();\n try {\n const results: CompareResult[] = [];\n for (const capture of captures) {\n results.push(await compareOne(capture, outputDir, threshold, server, options));\n }\n return results;\n } finally {\n server.stop();\n }\n}\n","import { execSync } from \"child_process\";\nimport { logger } from \"../logger.js\";\nimport { generateSummaryMd } from \"../templates/summary.md.js\";\nimport type { CompareResult } from \"../types.js\";\n\nconst COMMENT_MARKER = \"<!-- afterbefore -->\";\n\nfunction findPrNumber(): string | null {\n try {\n const output = execSync(\"gh pr view --json number -q .number\", {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }).trim();\n return output || null;\n } catch {\n return null;\n }\n}\n\nfunction findExistingCommentId(prNumber: string): string | null {\n try {\n const output = execSync(\n `gh api repos/{owner}/{repo}/issues/${prNumber}/comments --jq '.[] | select(.body | contains(\"${COMMENT_MARKER}\")) | .id'`,\n { encoding: \"utf-8\", stdio: [\"pipe\", \"pipe\", \"pipe\"] }\n ).trim();\n const ids = output.split(\"\\n\").filter(Boolean);\n return ids[0] || null;\n } catch {\n return null;\n }\n}\n\nfunction postOrUpdateComment(prNumber: string, body: string): void {\n const existingId = findExistingCommentId(prNumber);\n\n if (existingId) {\n logger.info(`Updating existing PR comment (id: ${existingId})`);\n execSync(\n `gh api repos/{owner}/{repo}/issues/comments/${existingId} -X PATCH -f body=@-`,\n {\n input: body,\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }\n );\n } else {\n logger.info(`Creating new PR comment`);\n execSync(\n `gh api repos/{owner}/{repo}/issues/${prNumber}/comments -f body=@-`,\n {\n input: body,\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }\n );\n }\n}\n\nexport async function generateReport(\n results: CompareResult[],\n outputDir: string,\n options: { post: boolean }\n): Promise<void> {\n if (options.post) {\n const prNumber = findPrNumber();\n if (!prNumber) {\n logger.warn(\n \"Could not find PR number. Make sure you are on a branch with an open PR and `gh` is authenticated.\"\n );\n return;\n }\n\n const prSummary = generateSummaryMd(results, undefined, { includeFilePaths: false });\n postOrUpdateComment(prNumber, prSummary);\n logger.success(`Posted results to PR #${prNumber}`);\n }\n}\n","import type { CompareResult } from \"../types.js\";\n\ninterface SummaryOptions {\n includeFilePaths?: boolean;\n}\n\nexport function generateSummaryMd(results: CompareResult[], gitDiff?: string, options?: SummaryOptions): string {\n const includeFilePaths = options?.includeFilePaths ?? true;\n const changed = results.filter((r) => r.changed);\n const lines: string[] = [];\n\n lines.push(\"<!-- afterbefore -->\");\n lines.push(\"\");\n lines.push(\"## afterbefore Report\");\n lines.push(\"\");\n\n if (changed.length === 0) {\n lines.push(\"No visual changes detected.\");\n lines.push(\"\");\n lines.push(`${results.length} route(s) captured, all unchanged.`);\n return lines.join(\"\\n\");\n }\n\n lines.push(\n `${results.length} route(s) captured, **${changed.length}** with visual changes.`\n );\n lines.push(\"\");\n lines.push(\"| Route | Diff % | Status |\");\n lines.push(\"|-------|--------|--------|\");\n\n for (const r of results) {\n const status = r.changed ? \"Changed\" : \"Unchanged\";\n const pct = r.changed ? `${r.diffPercentage.toFixed(2)}%` : \"0%\";\n lines.push(`| \\`${r.route}\\` | ${pct} | ${status} |`);\n }\n\n if (gitDiff) {\n lines.push(\"\");\n lines.push(\"### Changed files\");\n lines.push(\"```diff\");\n lines.push(gitDiff);\n lines.push(\"```\");\n }\n\n if (includeFilePaths) {\n lines.push(\"\");\n lines.push(\"### Screenshots\");\n lines.push(\"| Route | Before | After | Compare |\");\n lines.push(\"|-------|--------|-------|---------|\");\n\n for (const r of changed) {\n lines.push(`| \\`${r.route}\\` | \\`${r.beforePath}\\` | \\`${r.afterPath}\\` | \\`${r.comparePath}\\` |`);\n }\n\n lines.push(\"\");\n lines.push(\"Review the before/after screenshots above to verify the visual changes match the code diff.\");\n }\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":["resolve","unlinkSync","resolve","existsSync","readFileSync","resolve","readFileSync","join","existsSync","readFileSync","resolve","existsSync","resolve","readFileSync","join","readFileSync","resolve","execSync","join","existsSync","join","join","execSync","existsSync","join","resolve","exec","join","existsSync","join","execSync","execSync","maxPerSource","join","join","dirname","execSync","execSync","resolve","unlinkSync"]}
|