Skip to content

Commit c1d9851

Browse files
committed
chore(typescript): assert native TS7 per workspace and drop a dead install exemption
1 parent 8c2f206 commit c1d9851

2 files changed

Lines changed: 58 additions & 18 deletions

File tree

bunfig.toml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,6 @@ exact = true
33
# Supply-chain gate: only install package versions published at least 7 days ago
44
# (blocks freshly published, potentially compromised releases).
55
minimumReleaseAge = 604800
6-
# @typescript/native-preview stays excluded permanently: it only publishes nightly
7-
# dev builds, so every version is structurally younger than any age gate.
8-
minimumReleaseAgeExcludes = [
9-
"@typescript/native-preview",
10-
]
116

127
[run]
138
env = { NEXT_PUBLIC_APP_URL = "http://localhost:3000" }

scripts/check-native-typecheck.ts

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,74 @@
11
#!/usr/bin/env bun
22
/**
3-
* Asserts that a bare `tsc` runs the native (Go) TypeScript 7 compiler.
3+
* Asserts that every workspace's bare `tsc` runs the native (Go) TypeScript 7 compiler.
44
*
5-
* `@typescript/typescript6` (needed for apps/sim's runtime TypeScript API) pulls in an alias of
6-
* `typescript@6` that declares its own `tsc` bin. Package managers pick bin winners by lexical
7-
* sort, not dependency depth, so it wins `node_modules/.bin/tsc` unless something sorts ahead —
8-
* which is the only job the root `@typescript/native` alias has. Nothing imports that alias, so
9-
* deleting it looks free and silently costs every `tsc` in the repo ~10x.
5+
* `@typescript/typescript6` (needed for apps/sim's runtime TypeScript API, and for the audit
6+
* scripts that read the stable compiler API) pulls in an alias of `typescript@6` that declares
7+
* its own `tsc` bin. Package managers pick bin winners by lexical sort, not dependency depth, so
8+
* it wins `node_modules/.bin/tsc` unless something sorts ahead — which is the only job the root
9+
* `@typescript/native` alias has. Nothing imports that alias, so deleting it looks free and
10+
* silently costs every `tsc` in the repo ~10x.
11+
*
12+
* Every workspace's `type-check` is a bare `tsc --noEmit`, which resolves through the nearest
13+
* `node_modules/.bin` walking up from that package. They all reach the root bin today, but a
14+
* workspace that installs anything shipping its own `tsc` would get a local one that shadows it —
15+
* invisible to a root-only assertion, and slow in exactly the same silent way. So each workspace
16+
* is resolved the way its own script would.
1017
*
1118
* @see https://github.com/microsoft/typescript-go/issues/4567
1219
*/
1320
import { spawnSync } from 'node:child_process'
14-
import { localBin } from './local-bin'
21+
import { existsSync } from 'node:fs'
22+
import path from 'node:path'
23+
import { fileURLToPath } from 'node:url'
24+
import { Glob } from 'bun'
25+
26+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
27+
28+
/** The `tsc` a bare invocation in `dir` would run: nearest `node_modules/.bin`, walking up. */
29+
function resolveTsc(dir: string): string | null {
30+
let current = dir
31+
while (true) {
32+
const candidate = path.join(current, 'node_modules', '.bin', 'tsc')
33+
if (existsSync(candidate)) return candidate
34+
const parent = path.dirname(current)
35+
if (parent === current || !current.startsWith(ROOT)) return null
36+
current = parent
37+
}
38+
}
1539

16-
const result = spawnSync(localBin('tsc'), ['--version'], { encoding: 'utf8' })
17-
const reported = result.stdout?.trim() ?? ''
40+
const workspaceDirs = [
41+
'.',
42+
...[...new Glob('{apps,packages}/*/package.json').scanSync(ROOT)].map(path.dirname),
43+
].sort()
44+
45+
const failures: string[] = []
46+
47+
for (const workspace of workspaceDirs) {
48+
const tsc = resolveTsc(path.join(ROOT, workspace))
49+
if (!tsc) {
50+
failures.push(`${workspace}: no \`tsc\` resolvable from this package`)
51+
continue
52+
}
53+
const result = spawnSync(tsc, ['--version'], { encoding: 'utf8' })
54+
const reported = result.stdout?.trim() ?? ''
55+
if (!/^Version 7\./.test(reported)) {
56+
const detail = reported || result.stderr?.trim() || `exit ${result.status}`
57+
failures.push(`${workspace}: ${path.relative(ROOT, tsc)} reports "${detail}"`)
58+
}
59+
}
1860

19-
if (!/^Version 7\./.test(reported)) {
20-
const detail = reported || result.stderr?.trim() || `exit ${result.status}`
61+
if (failures.length > 0) {
2162
console.error(
22-
`Native type-check audit failed: node_modules/.bin/tsc reports "${detail}", expected TypeScript 7.x.\n\n` +
63+
`Native type-check audit failed — expected TypeScript 7.x everywhere:\n\n${failures
64+
.map((failure) => ` ${failure}`)
65+
.join('\n')}\n\n` +
2366
' Check that `@typescript/native` is still in the root devDependencies, and that no newly\n' +
2467
' added package sorts ahead of it while declaring a `tsc` bin.'
2568
)
2669
process.exit(1)
2770
}
2871

29-
console.log(`Native type-check audit passed (bare \`tsc\` is ${reported}).`)
72+
console.log(
73+
`Native type-check audit passed (bare \`tsc\` is TypeScript 7.x in ${workspaceDirs.length} workspaces).`
74+
)

0 commit comments

Comments
 (0)