import { readFileSync, readdirSync, statSync } from "node:fs" import path from "node:path" import process from "node:process" const ROOT = process.cwd() const SEARCH_DIRS = ["app", "components"] const INCLUDE_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".mdx"]) const IGNORE_PATTERNS = [ ".backup", `${path.sep}style-guide${path.sep}`, `${path.sep}ui${path.sep}`, `${path.sep}api${path.sep}`, `${path.sep}manuals${path.sep}README.md`, `${path.sep}components${path.sep}privacy-policy-page.tsx`, `${path.sep}components${path.sep}terms-and-conditions-page.tsx`, `${path.sep}app${path.sep}seaga-hy900-support${path.sep}`, ] const BANNED_PATTERNS = [ { label: "rest of the site", regex: /\brest of the site\b/i }, { label: "design system", regex: /\bdesign system\b/i }, { label: "presentation", regex: /\bpresentation\b/i }, { label: "same clean", regex: /\bsame clean\b/i }, { label: "same Rocky shell", regex: /\bsame rocky shell\b/i }, { label: "dedicated page", regex: /\bdedicated page\b/i }, { label: "intake", regex: /\bintake\b/i }, { label: "handoff", regex: /\bhandoff\b/i }, { label: "popup", regex: /\bpopup\b/i }, { label: "embed", regex: /\bembed(?:ded)?\b/i }, { label: "this page now", regex: /\bthis page now\b/i }, { label: "consistent service experience", regex: /\bconsistent service experience\b/i }, { label: "best next step", regex: /\bbest next step\b/i }, { label: "fuller request", regex: /\bfuller request\b/i }, { label: "fuller intake", regex: /\bfuller intake\b/i }, ] function shouldIgnore(filePath) { return IGNORE_PATTERNS.some((pattern) => filePath.includes(pattern)) } function walk(dir) { const entries = readdirSync(dir, { withFileTypes: true }) const results = [] for (const entry of entries) { const fullPath = path.join(dir, entry.name) if (shouldIgnore(fullPath)) continue if (entry.isDirectory()) { results.push(...walk(fullPath)) continue } if (!entry.isFile()) continue if (INCLUDE_EXTENSIONS.has(path.extname(entry.name))) { results.push(fullPath) } } return results } function findLineNumber(content, index) { return content.slice(0, index).split("\n").length } function main() { const files = SEARCH_DIRS.flatMap((dir) => { const fullDir = path.join(ROOT, dir) if (!statSync(fullDir, { throwIfNoEntry: false })) return [] return walk(fullDir) }) const findings = [] for (const filePath of files) { const content = readFileSync(filePath, "utf8") for (const rule of BANNED_PATTERNS) { const match = content.match(rule.regex) if (!match || match.index == null) continue findings.push({ filePath, line: findLineNumber(content, match.index), label: rule.label, preview: match[0], }) } } if (findings.length === 0) { console.log("Public copy check passed.") process.exit(0) } console.error("Public copy guardrail failed. Rewrite customer-facing copy that talks about the site or UI mechanics:") for (const finding of findings) { console.error(`- ${path.relative(ROOT, finding.filePath)}:${finding.line} [${finding.label}] ${finding.preview}`) } process.exit(1) } main()