@@ -19,38 +19,85 @@ jobs:
1919
2020 - name : Check for TypeScript
2121 run : |
22- # Allowlist (TS legitimate as a bridge/adapter to a non-ReScript ecosystem):
23- # bindings/ - language bindings (Deno/TS/AssemblyScript FFI)
24- # *.d.ts - TypeScript type declarations for ReScript FFI
25- # tests/, test/ - Deno test runners
26- # scripts/ - Deno build scripts
27- # mcp-adapter/ - MCP server adapters (MCP is Deno/TS-typed by spec)
28- # *vscode* - VSCode extensions (TS is the ecosystem default)
29- # cli/ - CLI entry points (Deno scripts)
30- # mod.ts - canonical Deno module entrypoint
31- # *lsp-server.ts, *lsp.ts - Language Server Protocol implementations
32- # deno-*/ - subprojects explicitly named for Deno
33- TS_FILES=$(find . \( -name "*.ts" -o -name "*.tsx" \) \
34- | grep -v node_modules \
35- | grep -v '/bindings/' \
36- | grep -v '\.d\.ts$' \
37- | grep -v '/tests/' \
38- | grep -v '/test/' \
39- | grep -v '/scripts/' \
40- | grep -v '/mcp-adapter/' \
41- | grep -Ev '/[^/]*vscode[^/]*/' \
42- | grep -v '/cli/' \
43- | grep -v '/mod\.ts$' \
44- | grep -Ev 'lsp[-_]?server\.ts$' \
45- | grep -Ev '[/-]lsp\.ts$' \
46- | grep -Ev '/deno-[^/]+/' \
47- || true)
48- if [ -n "$TS_FILES" ]; then
49- echo "❌ TypeScript files detected - use ReScript instead"
50- echo "$TS_FILES"
51- exit 1
52- fi
53- echo "✅ No TypeScript files outside allowlisted bridge/adapter paths"
22+ python3 << 'PYEOF'
23+ import re, sys, fnmatch, pathlib
24+
25+ # Universal builtin allowlist — bridges that need no per-repo declaration.
26+ # Files matching any of these patterns are always allowed.
27+ BUILTIN_GLOBS = [
28+ '*.d.ts',
29+ '**/bindings/**',
30+ '**/tests/**', '**/test/**',
31+ '**/scripts/**',
32+ '**/mcp-adapter/**',
33+ '**/*vscode*/**',
34+ '**/cli/**',
35+ '**/mod.ts',
36+ '**/lsp-server.ts', '**/lsp_server.ts', '**/lsp.ts', '**/*-lsp.ts',
37+ '**/deno-*/**',
38+ '**/node_modules/**',
39+ '**/vendor/**',
40+ '**/examples/**',
41+ '**/ffi/**',
42+ ]
43+
44+ # Per-repo exemptions parsed from .claude/CLAUDE.md "TypeScript Exemptions" table.
45+ # Single source of truth — adding a row here unblocks CI for that path.
46+ # Format expected:
47+ # ### TypeScript Exemptions ...
48+ # | Path | Files | Rationale | Unblock condition |
49+ # |---|---|---|---|
50+ # | `path/to/file.ts` | 1 | ... | ... |
51+ # | `dir/*.ts` | 6 | ... | ... |
52+ exemptions = []
53+ claude_md = pathlib.Path('.claude/CLAUDE.md')
54+ if claude_md.exists():
55+ in_table = False
56+ for line in claude_md.read_text(encoding='utf-8').splitlines():
57+ if re.search(r'TypeScript [Ee]xemptions', line):
58+ in_table = True
59+ continue
60+ if in_table and line.startswith(('### ', '## ', '# ')):
61+ break
62+ if in_table and line.startswith('|'):
63+ m = re.match(r'\|\s*`([^`]+)`', line)
64+ if m:
65+ exemptions.append(m.group(1))
66+
67+ # Find all .ts and .tsx files
68+ found = []
69+ for ext in ('ts', 'tsx'):
70+ found.extend(str(p) for p in pathlib.Path('.').rglob(f'*.{ext}'))
71+
72+ def allowed(path):
73+ p = path.lstrip('./')
74+ for g in BUILTIN_GLOBS + exemptions:
75+ if fnmatch.fnmatchcase(p, g):
76+ return True
77+ # also treat glob ending with / as a directory prefix
78+ base = g.rstrip('/').rstrip('*').rstrip('/')
79+ if base and (p == base or p.startswith(base + '/')):
80+ return True
81+ return False
82+
83+ bad = sorted(f for f in found if not allowed(f))
84+ if bad:
85+ print("❌ TypeScript files detected outside the allowlist.\n")
86+ for f in bad:
87+ print(f" {f}")
88+ print()
89+ print("To resolve, either:")
90+ print(" (a) migrate the file to AffineScript")
91+ print(" (see Human_Programming_Guide.adoc migration chapter), OR")
92+ print(" (b) move it to an allowlisted bridge path")
93+ print(" (bindings/, tests/, scripts/, mcp-adapter/, *vscode*/, cli/, deno-*/, etc.), OR")
94+ print(" (c) add an entry to the 'TypeScript Exemptions' table in .claude/CLAUDE.md")
95+ print(" with rationale + unblock condition.")
96+ if exemptions:
97+ print(f"\n(Currently {len(exemptions)} exemption(s) parsed from .claude/CLAUDE.md.)")
98+ sys.exit(1)
99+ print(f"✅ No TypeScript files outside allowlist ({len(exemptions)} per-repo exemption(s) parsed).")
100+ PYEOF
54101
55102 - name : Check for Go
56103 run : |
0 commit comments