#!/usr/bin/env node // tools/scripts/lint-game-logic-purity.mjs // Source: 04-CONTEXT.md D-25 — purity guard for packages/game-logic. // Greps for forbidden APIs in src/**/*.ts. Comments are stripped before scan. // // Usage: node tools/scripts/lint-game-logic-purity.mjs // Exit: 0 clean, 1 forbidden API found, 2 usage error. import { readFileSync, readdirSync, statSync } from 'node:fs'; import { join } from 'node:path'; const ROOT = 'packages/game-logic/src'; const FORBIDDEN = [ { name: 'Date.*', re: /\bDate\s*\./ }, { name: 'new Date(', re: /\bnew\s+Date\s*\(/ }, { name: 'Math.random', re: /\bMath\s*\.\s*random\b/ }, { name: 'process.*', re: /\bprocess\s*\.[a-z]/i }, { name: 'fs.*', re: /(?:^|\W)fs\s*\./ }, { name: 'fetch(', re: /\bfetch\s*\(/ }, { name: "require('http", re: /require\s*\(\s*['"]https?\b/ }, { name: "import 'http", re: /from\s*['"]https?\b/ }, { name: "import 'node:fs'", re: /from\s*['"]node:fs\b/ }, { name: "import 'node:os'", re: /from\s*['"]node:os\b/ }, ]; /** Strip block and line comments. Cheap, not a full parser; sufficient. * Normalises CRLF → LF before splitting so that the `$` anchor in the * line-comment regex correctly matches the end of each line on Windows * checkouts (without normalisation a trailing \r defeats the pattern). */ function stripComments(src) { return src .replace(/\r\n/g, '\n') .replace(/\/\*[\s\S]*?\*\//g, '') .split('\n') .map((line) => line.replace(/\/\/.*$/, '')) .join('\n'); } function walk(dir, out = []) { for (const f of readdirSync(dir)) { const p = join(dir, f); const s = statSync(p); if (s.isDirectory()) walk(p, out); else if (p.endsWith('.ts')) out.push(p); } return out; } const files = walk(ROOT); let violations = 0; for (const f of files) { const src = stripComments(readFileSync(f, 'utf-8')); for (const rule of FORBIDDEN) { if (rule.re.test(src)) { process.stderr.write(`lint-game-logic-purity: ${f} contains forbidden API ${rule.name}\n`); violations++; } } } if (violations > 0) { process.stderr.write( `lint-game-logic-purity: ${violations} violation(s) in ${files.length} file(s).\n`, ); process.exit(1); } console.log(`lint-game-logic-purity: OK (${files.length} file(s) clean)`); process.exit(0);