// cb/*.mjs의 `self. = ` 대입 ↔ gen-slaydeck.mjs 선언 prop 대조. // 미선언 prop에 대입하면 MSW 런타임 "cannot set X, no such field" → 그 후보를 정적 검출. // 사용: node tools/verify/cbprops.mjs import { readFileSync, readdirSync } from 'node:fs'; const orch = readFileSync('tools/deck/gen-slaydeck.mjs', 'utf8'); const declared = new Set(); for (const m of orch.matchAll(/prop\(\s*'[^']*'\s*,\s*'([^']+)'/g)) declared.add(m[1]); // MSW 빌트인/설정으로 대입 가능한 self 필드(프롭 아님) — 오탐 제외 화이트리스트. const BUILTIN = new Set(['Entity']); const dir = 'tools/deck/cb'; const files = readdirSync(dir).filter((f) => f.endsWith('.mjs')); const assigns = new Map(); // name -> Set(files) for (const f of files) { const src = readFileSync(`${dir}/${f}`, 'utf8'); // self.Name = (단, == / ~= / .Y= / [..]= 는 제외) for (const m of src.matchAll(/self\.([A-Za-z_]\w*)\s*=(?!=)/g)) { const name = m[1]; if (!assigns.has(name)) assigns.set(name, new Set()); assigns.get(name).add(f); } } const missing = [...assigns.keys()] .filter((n) => !declared.has(n) && !BUILTIN.has(n)) .sort(); console.log(`선언 prop: ${declared.size} | 대입된 self.X distinct: ${assigns.size}`); console.log(`미선언 대입 (no such field 후보): ${missing.length}`); for (const n of missing) console.log(` - ${n} [${[...assigns.get(n)].join(', ')}]`); console.log(missing.length ? 'RESULT: MISSING PROPS ABOVE' : 'RESULT: 모든 self 대입이 선언됨 ✓'); process.exit(missing.length ? 1 : 0);