afterbefore 0.1.19 → 0.2.1

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/index.js.map DELETED
@@ -1 +0,0 @@
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/git.ts","../src/errors.ts","../src/stages/diff.ts","../src/stages/classify.ts","../src/stages/graph.ts","../src/stages/resolve.ts","../src/utils/nextjs.ts","../src/stages/impact.ts","../src/stages/worktree.ts","../src/utils/pm.ts","../src/stages/server.ts","../src/stages/capture.ts"],"sourcesContent":["import { resolve, basename } from \"path\";\nimport chalk from \"chalk\";\nimport type { PipelineOptions, AffectedRoute, AfterbeforeConfig, CaptureTask } 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 { 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 } 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\";\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 expandRoutes(\n routes: AffectedRoute[],\n config: AfterbeforeConfig | null\n): CaptureTask[] {\n const tasks: CaptureTask[] = [];\n for (const r of routes) {\n const scenarios = config?.scenarios?.[r.route] ?? [];\n // Default capture\n tasks.push({\n route: r.route,\n label: r.route,\n prefix: routeToPrefix(r.route),\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 });\n }\n }\n return tasks;\n}\n\nfunction formatMs(ms: number): string {\n if (ms < 1000) return `${Math.round(ms)}ms`;\n return `${(ms / 1000).toFixed(1)}s`;\n}\n\nfunction applyConfigDefaults(options: PipelineOptions, config: AfterbeforeConfig | null): void {\n if (!config?.defaults) return;\n const defaults = config.defaults;\n\n // Only apply config defaults for fields that are still at their CLI default values.\n // We use the known CLI defaults to detect \"not explicitly set by user.\"\n const cliDefaults: Record<string, unknown> = {\n base: \"main\", output: \".afterbefore\",\n maxRoutes: 6, width: 1280, height: 720,\n delay: 0, maxDepth: 10,\n dryRun: false, verbose: false,\n };\n\n const opts = options as unknown as Record<string, unknown>;\n for (const [key, value] of Object.entries(defaults)) {\n if (key === \"cwd\" || value === undefined) continue;\n if (key in cliDefaults && opts[key] === cliDefaults[key]) {\n opts[key] = value;\n }\n }\n}\n\nexport async function runPipeline(options: PipelineOptions): Promise<void> {\n const { base, output, 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 const mode = options.dryRun ? \"Dry run\" : \"Capturing\";\n console.log(`\\nafterbefore v${version} · ${mode} against ${base}\\n`);\n\n // Load optional config (scenarios + defaults)\n const config = await loadConfig(cwd);\n applyConfigDefaults(options, config);\n\n logger.startPipeline(options.dryRun ? 3 : 6);\n\n // 1. Analyze diff\n const t1 = Date.now();\n logger.pipeline(1, \"Analyzing diff...\");\n const diffFiles = getChangedFiles(base, cwd);\n logger.stageComplete(\"Diff\", `${diffFiles.length} files changed`, Date.now() - t1);\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 t2 = Date.now();\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 // Skip worktree in dry-run mode.\n logger.pipeline(2, \"Building import graph...\");\n const worktreePromise = options.dryRun ? null : createWorktree(base, cwd);\n\n const graph = await buildImportGraph(cwd);\n const graphEdges = Array.from(graph.forward.values()).reduce((sum, deps) => sum + deps.size, 0);\n logger.stageComplete(\"Graph\", `${graph.forward.size} modules, ${graphEdges} edges`, Date.now() - t2);\n\n // 3. Find affected routes\n const t3 = Date.now();\n logger.pipeline(3, \"Finding affected routes...\");\n const changedPaths = impactfulFiles.map((f) => f.path);\n let affectedRoutes = findAffectedRoutes(changedPaths, graph, cwd, options.maxRoutes, options.maxDepth);\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 const directCount = affectedRoutes.filter(r => r.reason === \"direct\").length;\n const transitiveCount = affectedRoutes.length - directCount;\n const impactDetail = directCount > 0 && transitiveCount > 0\n ? `${affectedRoutes.length} routes (${directCount} direct, ${transitiveCount} transitive)`\n : `${affectedRoutes.length} routes`;\n logger.stageComplete(\"Impact\", impactDetail, Date.now() - t3);\n\n // --verbose: print trigger chains for each route\n if (options.verbose) {\n console.log(\"\");\n for (const r of affectedRoutes) {\n const chain = r.triggerChain.map(f => basename(f)).join(\" → \");\n const depthLabel = r.depth === 0 ? \"direct\" : `depth ${r.depth}`;\n console.log(chalk.dim(` ${r.route.padEnd(24)} ${depthLabel.padEnd(10)} ${chain}`));\n }\n console.log(\"\");\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 // --dry-run: print affected routes and exit\n if (options.dryRun) {\n worktreePromise?.then((w) => w.cleanup()).catch(() => {});\n logger.completePipeline();\n\n console.log(`\\n${affectedRoutes.length} route(s) would be captured:\\n`);\n for (const r of affectedRoutes) {\n const chain = r.triggerChain.map(f => basename(f)).join(\" → \");\n const depthLabel = r.depth === 0 ? \"direct\" : `depth ${r.depth}`;\n console.log(` ${r.route.padEnd(24)} ${chalk.dim(depthLabel.padEnd(10))} ${chalk.dim(chain)}`);\n }\n\n const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);\n console.log(chalk.dim(`\\n Completed in ${elapsed}s (dry run — no screenshots captured)\\n`));\n return;\n }\n\n // 4. Wait for worktree\n const t4 = Date.now();\n logger.pipeline(4, \"Setting up worktree...\");\n const worktree = await worktreePromise!;\n logger.stageComplete(\"Worktree\", \"created + dependencies installed\", Date.now() - t4);\n\n // 5. Start servers + browser in parallel\n const t5 = Date.now();\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 logger.stageComplete(\"Servers\", `ready on :${beforePort} and :${afterPort}`, Date.now() - t5);\n\n // 6. Capture screenshots\n const t6 = Date.now();\n logger.pipeline(6, \"Capturing screenshots...\");\n const tasks = expandRoutes(affectedRoutes, config);\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 delay: options.delay,\n onProgress: (i, label) =>\n logger.pipeline(6, `Capturing ${label} (${i}/${tasks.length})...`),\n }\n );\n logger.stageComplete(\"Capture\", `${captures.length} screenshots from ${tasks.length} routes`, Date.now() - t6);\n\n logger.completePipeline(true);\n console.log(`\\n${captures.length} screenshot pair(s) captured:\\n`);\n for (const c of captures) {\n console.log(` ${c.route}`);\n console.log(chalk.dim(` before: ${c.beforePath.replace(cwd + \"/\", \"\")}`));\n console.log(chalk.dim(` after: ${c.afterPath.replace(cwd + \"/\", \"\")}`));\n }\n const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);\n logger.success(`Done in ${elapsed}s — ${captures.length} route(s) captured`);\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 private readonly isTTY = !!process.stderr.isTTY;\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 if (this.isTTY) {\n this.spinner = ora({\n text: chalk.dim(text),\n spinner: BAR_SPINNER,\n }).start();\n } else {\n console.error(chalk.dim(text));\n }\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.isTTY) {\n return;\n }\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 stageComplete(name: string, detail: string, durationMs: number): void {\n const duration = durationMs < 1000\n ? `${Math.round(durationMs)}ms`\n : `${(durationMs / 1000).toFixed(1)}s`;\n const line = ` ${chalk.green(\"\\u2713\")} ${name.padEnd(12)} ${chalk.dim(detail.padEnd(50))} ${chalk.dim(duration)}`;\n this.log(\"stage\", `${name}: ${detail} (${duration})`);\n\n if (this.isTTY && this.spinner) {\n this.spinner.clear();\n console.log(line);\n this.spinner.render();\n } else {\n console.log(line);\n }\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 const bar = \"\\u2588\".repeat(BAR_WIDTH);\n this.spinner.succeed(` ${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.error(`${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 { execSync } from \"child_process\";\nimport { AfterbeforeError } from \"../errors.js\";\n\nexport function git(args: string, cwd?: string): string {\n try {\n return execSync(`git ${args}`, {\n cwd,\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }).trim();\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (message.includes(\"ENOENT\") || message.includes(\"not found\")) {\n throw new AfterbeforeError(\n \"Git is not installed or not in PATH.\",\n \"Install git: https://git-scm.com/downloads\"\n );\n }\n throw err;\n }\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 try {\n return git(`merge-base ${base} HEAD`, cwd);\n } catch {\n throw new AfterbeforeError(\n `Could not find merge base for \"${base}\". The branch or ref may not exist.`,\n `Run \"git branch -a\" to see available branches. Did you mean \"master\" instead of \"main\"?`\n );\n }\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","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 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","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 DEFAULT_MAX_DEPTH = 10;\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 maxDepth: number = DEFAULT_MAX_DEPTH\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 >= maxDepth) 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 maxDepth: number = DEFAULT_MAX_DEPTH\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 >= maxDepth) 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","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, type Browser, type BrowserContext, type Page } from \"playwright\";\nimport { logger } from \"../logger.js\";\nimport { AfterbeforeError } from \"../errors.js\";\nimport type { CaptureResult, CaptureOptions, CaptureTask, ScenarioAction } from \"../types.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 try {\n execSync(\"npx playwright install chromium\", {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n return await chromium.launch();\n } catch {\n throw new AfterbeforeError(\n \"Could not install or launch Playwright Chromium.\",\n 'Run \"npx playwright install chromium\" manually, then try again.'\n );\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 await page.screenshot({ path, fullPage: true });\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 });\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 const contextOpts = {\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"],"mappings":"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","names":["resolve","chalk","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","join","resolve","chalk","elapsed"]}