// 배포 텍스트에는 한자를 두지 않는다. 이미지처럼 텍스트가 아닌 자산은 이 검사의 대상이 아니다. import fs from "node:fs"; import path from "node:path"; import url from "node:url"; const here = path.dirname(url.fileURLToPath(import.meta.url)); const root = path.resolve(here, ".."); const TEXT_EXTENSIONS = new Set([".astro", ".css", ".html", ".js", ".json", ".mjs", ".svg", ".ts", ".tsx", ".txt", ".webmanifest", ".xml"]); const HANJA = /[\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff]/u; const HANJA_GLOBAL = /[\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff]/gu; function scanRoots(argv) { const roots = []; for (let index = 2; index < argv.length; index += 1) { const argument = argv[index]; const value = argument === "--root" ? argv[++index] : argument.startsWith("--root=") ? argument.slice("--root=".length) : null; if (!value) throw new Error(`Unknown or incomplete argument: ${argument}`); const candidate = path.resolve(root, value); if (candidate !== root && !candidate.startsWith(root + path.sep)) throw new Error(`Root must stay inside apps/site: ${value}`); roots.push(candidate); } return roots.length ? roots : [path.join(root, "src"), path.join(root, "public")]; } function textFiles(directory) { const files = []; const walk = (current) => { for (const entry of fs.readdirSync(current, { withFileTypes: true })) { const candidate = path.join(current, entry.name); if (entry.isDirectory()) walk(candidate); else if (entry.isFile() && TEXT_EXTENSIONS.has(path.extname(entry.name).toLowerCase())) files.push(candidate); } }; walk(directory); return files; } const failures = []; let scanned = 0; for (const scanRoot of scanRoots(process.argv)) { if (!fs.existsSync(scanRoot)) throw new Error(`Scan root does not exist: ${path.relative(root, scanRoot) || "."}`); for (const file of textFiles(scanRoot)) { scanned += 1; const lines = fs.readFileSync(file, "utf8").split(/\r?\n/); lines.forEach((line, index) => { if (!HANJA.test(line)) return; const characters = [...new Set(line.match(HANJA_GLOBAL) || [])].join(""); failures.push(`${path.relative(root, file).replaceAll("\\", "/")}:${index + 1} contains ${characters}`); }); } } if (failures.length) { console.error(`FAIL: ${failures.length} Hanja occurrence(s) across ${scanned} shipped text files.`); for (const failure of failures) console.error(` ${failure}`); process.exit(1); } console.log(`PASS: ${scanned} shipped text files contain no Hanja.`);