feat: 한국어 우선 쇼케이스 정리
All checks were successful
ci / build (push) Successful in 51s
release / release (push) Successful in 2m34s

This commit is contained in:
Yun Chan 2026-08-26 18:14:41 +09:00
parent e4087c29f5
commit 2b7fa32b5b
109 changed files with 437 additions and 279 deletions

View file

@ -0,0 +1,59 @@
// 배포 텍스트에는 한자를 두지 않는다. 이미지처럼 텍스트가 아닌 자산은 이 검사의 대상이 아니다.
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.`);