import fs from "node:fs"; import path from "node:path"; export const defaultPlaywrightLaunchScanRoots = Object.freeze(["scripts", "tools", "web"]); export const defaultPlaywrightLaunchAllowlist = Object.freeze([ "scripts/src/browser-launcher.mjs" ]); const sourceExtensions = new Set([".cjs", ".cts", ".js", ".jsx", ".mjs", ".mts", ".ts", ".tsx"]); const skippedDirectories = new Set([".git", ".state", ".worktree", "dist", "node_modules"]); const directChromiumLaunchPattern = new RegExp("\\bchromium\\s*\\.\\s*launch\\s*\\(", "u"); export function findDirectChromiumLaunches(options = {}) { const repoRoot = path.resolve(options.repoRoot ?? process.cwd()); const roots = options.roots ?? defaultPlaywrightLaunchScanRoots; const allowlist = new Set(options.allowlist ?? defaultPlaywrightLaunchAllowlist); const scannedFiles = []; const allowed = []; const violations = []; for (const root of roots) { const absoluteRoot = path.resolve(repoRoot, root); if (!fs.existsSync(absoluteRoot)) continue; for (const file of collectSourceFiles(absoluteRoot)) { const relativePath = normalizeRelative(repoRoot, file); scannedFiles.push(relativePath); const matches = findMatches(file, relativePath); if (matches.length === 0) continue; if (allowlist.has(relativePath)) allowed.push(...matches); else violations.push(...matches); } } return { status: violations.length === 0 ? "pass" : "fail", repoRoot, roots, allowlist: [...allowlist].sort(), scannedFileCount: scannedFiles.length, allowed, violations }; } function collectSourceFiles(directory) { const out = []; for (const entry of fs.readdirSync(directory, { withFileTypes: true })) { if (entry.isDirectory()) { if (!skippedDirectories.has(entry.name)) out.push(...collectSourceFiles(path.join(directory, entry.name))); continue; } if (!entry.isFile()) continue; const full = path.join(directory, entry.name); if (sourceExtensions.has(path.extname(entry.name))) out.push(full); } return out; } function findMatches(file, relativePath) { const lines = fs.readFileSync(file, "utf8").split(/\r?\n/u); const matches = []; lines.forEach((line, index) => { if (directChromiumLaunchPattern.test(line)) { matches.push({ file: relativePath, line: index + 1, text: line.trim().slice(0, 200) }); } }); return matches; } function normalizeRelative(root, file) { return path.relative(root, file).split(path.sep).join("/"); }