95 lines
2.8 KiB
JavaScript
95 lines
2.8 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
|
|
const root = process.cwd();
|
|
const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
|
|
|
|
for (const scriptName of [
|
|
"phase-i18n:boundary",
|
|
"i18n:phase1-smoke",
|
|
"i18n:phase2-smoke",
|
|
"i18n:phase3-smoke",
|
|
"i18n:phase4-boundary",
|
|
"i18n:phase5-smoke",
|
|
"i18n:phase7-smoke",
|
|
"i18n:phase8-smoke",
|
|
"i18n:phase9-hardening",
|
|
]) {
|
|
assert.ok(pkg.scripts?.[scriptName], `Missing package script: ${scriptName}`);
|
|
}
|
|
|
|
const runtimeFiles = listFiles(root, [
|
|
"app",
|
|
"components",
|
|
"config",
|
|
"lib",
|
|
"server",
|
|
]).filter((file) => /\.(ts|tsx|mjs|js)$/.test(file));
|
|
const boundaryAllowlist = new Set([
|
|
path.join(root, "lib", "i18n", "date-fns.ts"),
|
|
path.join(root, "lib", "i18n", "format.ts"),
|
|
]);
|
|
|
|
const forbiddenRuntimePatterns = [
|
|
/@supabase\//i,
|
|
/createClient\([^)]*supabase/i,
|
|
/supabase\.co/i,
|
|
/NEXT_PUBLIC_SUPABASE/i,
|
|
/SUPABASE_SERVICE_ROLE/i,
|
|
/from ["']date-fns\/locale\/tr["']/,
|
|
/from ["']date-fns\/locale["']/,
|
|
/["']tr-TR["']/,
|
|
];
|
|
|
|
const violations = [];
|
|
for (const file of runtimeFiles) {
|
|
if (boundaryAllowlist.has(file)) continue;
|
|
const text = fs.readFileSync(file, "utf8");
|
|
for (const pattern of forbiddenRuntimePatterns) {
|
|
if (pattern.test(text)) violations.push(`${path.relative(root, file)} :: ${pattern}`);
|
|
}
|
|
}
|
|
assert.deepEqual(violations, [], `I18n release boundary violations:\n${violations.join("\n")}`);
|
|
|
|
const phase9ApiBoundary = fs.readFileSync(path.join(root, "scripts", "phase9-api-boundary.mjs"), "utf8");
|
|
for (const marker of [
|
|
"instance.localization",
|
|
"UNSUPPORTED_LOCALE",
|
|
"Accept-Language",
|
|
"resolvedLocale",
|
|
"portalLocale",
|
|
]) {
|
|
assert.ok(phase9ApiBoundary.includes(marker), `Phase 9 API boundary must include ${marker}`);
|
|
}
|
|
|
|
const hardcodedReport = path.join(root, "docs", "self-hosted-redesign", "i18n-phase-4-hardcoded-text-report.md");
|
|
assert.ok(fs.existsSync(hardcodedReport), "Hardcoded text report must exist for release review");
|
|
assert.ok(
|
|
fs.readFileSync(hardcodedReport, "utf8").includes("release blocker"),
|
|
"Hardcoded text report must document release blocker policy",
|
|
);
|
|
|
|
console.log("I18n phase 9 boundary passed: scripts, Supabase/runtime locale and release report gates verified.");
|
|
|
|
function listFiles(baseDir, roots) {
|
|
const files = [];
|
|
for (const rootName of roots) {
|
|
const start = path.join(baseDir, rootName);
|
|
if (!fs.existsSync(start)) continue;
|
|
walk(start, files);
|
|
}
|
|
return files;
|
|
}
|
|
|
|
function walk(current, files) {
|
|
const stat = fs.lstatSync(current);
|
|
if (stat.isSymbolicLink()) return;
|
|
if (stat.isDirectory()) {
|
|
if (["node_modules", ".next", ".git"].includes(path.basename(current))) return;
|
|
for (const entry of fs.readdirSync(current)) walk(path.join(current, entry), files);
|
|
return;
|
|
}
|
|
if (stat.isFile()) files.push(current);
|
|
}
|