Public: anyone can read this; the crew can change it.

Typecheck with the native TypeScript compiler (TS 7 / tsgo): tsc is now the forge's top CPU consumer #1870

Merged opened this in answer to

Check · Review · Land · Live done — Landed and live in production.

Merged

torque/forge:torque/1864-typecheck-with-the-native-typescript-com → main 1 commit fork

  • Merged. torque/1864-typecheck-with-the-native-typescript-com was merged into main as 2c3792c.

Review

Sign in to review this change.
9 files changed +86 −14
CLAUDE.md +3 −1
@@ -13,7 +13,9 @@ isn't understood yet.
1313
1414 1. **Zero runtime dependencies.** `package.json` `dependencies` is empty and
1515 stays empty. Never `bun add` a runtime dep. Need a 20-line utility? Write it
16 as a 20-line file. `devDependencies` is types + `tsc` only.
16 as a 20-line file. `devDependencies` is types + `tsc` only (`typescript` is
17 the native 7.x checker; `typescript-api` is 5.x for `scripts/hollow.ts`'s
18 AST walk).
1719 2. **Bun, not npm/yarn/pnpm.** Install, run, test, build all go through `bun`.
1820 The runtime provides everything: `Bun.serve`, `Bun.spawn`, `bun:sqlite`,
1921 `Bun.password` (argon2), WebCrypto, gzip.
README.md +7 −3
@@ -33,7 +33,7 @@ feeling on a foundation sized for one person.
3333 ## Run it
3434
3535 ```sh
36 bun install # dev-only: types + tsc, no runtime deps
36 bun install # dev-only: types + tsc (native, 7.x), no runtime deps
3737 bun run src/main.ts # web on :3000, ssh on :2222 (FORGE_SSH_PORT=0 turns it off; a taken port turns it off with one warning)
3838 bun src/main.ts --preflight # can this tree boot? config parses, migrations run on a copy of the database; one line, exit 0 or 1, no socket opened. Run it before the swap, not under load: the copy is not atomic against a live writer, and a `preflight-*` directory a killed run left behind is swept by the next run once an hour old, and a missing data directory is created, as boot would
3939 ```
@@ -676,8 +676,12 @@ across how many files, in how long, then the slowest and fastest shard.
676676 The check's first step is the gate-list audit (`bun scripts/gate.ts`), so a
677677 test file listed in neither `GATE` nor `EXCLUDED` fails in under a second,
678678 before install-heavy work. `tsc` then runs `--incremental` with a 2 GB heap,
679 its build info in `.forge-cache/`: the forge restores that directory from the
680 base's own green check and keeps it only from green checks of the default
679 its build info in `.forge-cache/`. It is TypeScript 7, the native compiler (a
680 Go binary, so the bound is `GOMEMLIMIT=2GiB`): on this tree a cold check is
681 ~3 s and ~17 CPU-s where 5.9 took ~30 s and ~50 CPU-s (#1864). 5.9 stays as
682 the dev dependency `typescript-api` only because `scripts/hollow.ts` walks
683 its AST, which 7.0 has no stable JS API for. The forge restores
684 `.forge-cache/` from the base's own green check and keeps it only from green checks of the default
681685 branch (`src/checks/cache.ts`), so a pull's code never fills a cache another
682686 check reads. The full gate also writes every file's time there, and the next
683687 split weighs every file by measurement rather than the 41 the table named.
bun.lock +45 −2
@@ -7,7 +7,8 @@
77 "devDependencies": {
88 "@anthropic-ai/sandbox-runtime": "^0.0.79",
99 "@types/bun": "^1.2.0",
10 "typescript": "^5.9.0",
10 "typescript": "^7.0.2",
11 "typescript-api": "npm:typescript@^5.9.0",
1112 },
1213 },
1314 },
@@ -20,13 +21,55 @@
2021
2122 "@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg=="],
2223
24 "@typescript/typescript-aix-ppc64": ["@typescript/[email protected]", "", { "os": "aix", "cpu": "ppc64" }, "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ=="],
25
26 "@typescript/typescript-darwin-arm64": ["@typescript/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA=="],
27
28 "@typescript/typescript-darwin-x64": ["@typescript/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA=="],
29
30 "@typescript/typescript-freebsd-arm64": ["@typescript/[email protected]", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ=="],
31
32 "@typescript/typescript-freebsd-x64": ["@typescript/[email protected]", "", { "os": "freebsd", "cpu": "x64" }, "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw=="],
33
34 "@typescript/typescript-linux-arm": ["@typescript/[email protected]", "", { "os": "linux", "cpu": "arm" }, "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ=="],
35
36 "@typescript/typescript-linux-arm64": ["@typescript/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ=="],
37
38 "@typescript/typescript-linux-loong64": ["@typescript/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ=="],
39
40 "@typescript/typescript-linux-mips64el": ["@typescript/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA=="],
41
42 "@typescript/typescript-linux-ppc64": ["@typescript/[email protected]", "", { "os": "linux", "cpu": "ppc64" }, "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA=="],
43
44 "@typescript/typescript-linux-riscv64": ["@typescript/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ=="],
45
46 "@typescript/typescript-linux-s390x": ["@typescript/[email protected]", "", { "os": "linux", "cpu": "s390x" }, "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw=="],
47
48 "@typescript/typescript-linux-x64": ["@typescript/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A=="],
49
50 "@typescript/typescript-netbsd-arm64": ["@typescript/[email protected]", "", { "os": "none", "cpu": "arm64" }, "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA=="],
51
52 "@typescript/typescript-netbsd-x64": ["@typescript/[email protected]", "", { "os": "none", "cpu": "x64" }, "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA=="],
53
54 "@typescript/typescript-openbsd-arm64": ["@typescript/[email protected]", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ=="],
55
56 "@typescript/typescript-openbsd-x64": ["@typescript/[email protected]", "", { "os": "openbsd", "cpu": "x64" }, "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg=="],
57
58 "@typescript/typescript-sunos-x64": ["@typescript/[email protected]", "", { "os": "sunos", "cpu": "x64" }, "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g=="],
59
60 "@typescript/typescript-win32-arm64": ["@typescript/[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ=="],
61
62 "@typescript/typescript-win32-x64": ["@typescript/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g=="],
63
2364 "bun-types": ["[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
2465
2566 "commander": ["[email protected]", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
2667
2768 "node-forge": ["[email protected]", "", {}, "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ=="],
2869
29 "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
70 "typescript": ["typescript@7.0.2", "", { "optionalDependencies": { "@typescript/typescript-aix-ppc64": "7.0.2", "@typescript/typescript-darwin-arm64": "7.0.2", "@typescript/typescript-darwin-x64": "7.0.2", "@typescript/typescript-freebsd-arm64": "7.0.2", "@typescript/typescript-freebsd-x64": "7.0.2", "@typescript/typescript-linux-arm": "7.0.2", "@typescript/typescript-linux-arm64": "7.0.2", "@typescript/typescript-linux-loong64": "7.0.2", "@typescript/typescript-linux-mips64el": "7.0.2", "@typescript/typescript-linux-ppc64": "7.0.2", "@typescript/typescript-linux-riscv64": "7.0.2", "@typescript/typescript-linux-s390x": "7.0.2", "@typescript/typescript-linux-x64": "7.0.2", "@typescript/typescript-netbsd-arm64": "7.0.2", "@typescript/typescript-netbsd-x64": "7.0.2", "@typescript/typescript-openbsd-arm64": "7.0.2", "@typescript/typescript-openbsd-x64": "7.0.2", "@typescript/typescript-sunos-x64": "7.0.2", "@typescript/typescript-win32-arm64": "7.0.2", "@typescript/typescript-win32-x64": "7.0.2" }, "bin": { "tsc": "bin/tsc" } }, "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA=="],
71
72 "typescript-api": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
3073
3174 "undici-types": ["[email protected]", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="],
3275
forge.json +2 −2
@@ -14,8 +14,8 @@
1414 }
1515 },
1616 "check": {
17 "fast": "bun scripts/gate.ts && node --max-old-space-size=2048 ./node_modules/typescript/lib/tsc.js --noEmit --incremental --tsBuildInfoFile .forge-cache/tsc.tsbuildinfo && bun scripts/test-shards.ts --timeout 60000 --tier fast --diff $FORGE_CHECK_BASE..HEAD --learned \"$FORGE_CHECK_LEARNED\"",
18 "run": "bun scripts/gate.ts && node --max-old-space-size=2048 ./node_modules/typescript/lib/tsc.js --noEmit --incremental --tsBuildInfoFile .forge-cache/tsc.tsbuildinfo && bun scripts/test-shards.ts --timeout 60000 --gate --impact",
17 "fast": "bun scripts/gate.ts && GOMEMLIMIT=2GiB ./node_modules/typescript/bin/tsc --noEmit --incremental --tsBuildInfoFile .forge-cache/tsc.tsbuildinfo && bun scripts/test-shards.ts --timeout 60000 --tier fast --diff $FORGE_CHECK_BASE..HEAD --learned \"$FORGE_CHECK_LEARNED\"",
18 "run": "bun scripts/gate.ts && GOMEMLIMIT=2GiB ./node_modules/typescript/bin/tsc --noEmit --incremental --tsBuildInfoFile .forge-cache/tsc.tsbuildinfo && bun scripts/test-shards.ts --timeout 60000 --gate --impact",
1919 "timeout": 7200,
2020 "limits": {
2121 "memory": 8192,
package.json +2 −1
@@ -14,6 +14,7 @@
1414 "devDependencies": {
1515 "@anthropic-ai/sandbox-runtime": "^0.0.79",
1616 "@types/bun": "^1.2.0",
17 "typescript": "^5.9.0"
17 "typescript": "^7.0.2",
18 "typescript-api": "npm:typescript@^5.9.0"
1819 }
1920 }
scripts/hollow.ts +3 −1
@@ -46,7 +46,9 @@
4646
4747 import { readdirSync, readFileSync } from "node:fs";
4848 import { posix } from "node:path";
49 import ts from "typescript";
49 // TypeScript 5's compiler API, under an alias: `typescript` is the native
50 // 7.x checker (a Go binary), and 7.0 ships no stable JS API to walk an AST.
51 import ts from "typescript-api";
5052 import { ALLOWED, type Allowed } from "./hollow-allow.ts";
5153
5254 const ACTIONS = "src/actions/";
src/web/og/card.ts +1 −1
@@ -50,7 +50,7 @@ const KIND_COLOR: Record<CardKind, string> = {
5050 function clean(s: unknown): string {
5151 return String(s ?? "")
5252 .replace(/\s+/g, " ")
53 .replace(/[^\u0009\u000A\u000D -퟿-�\u{10000}-\u{10FFFF}]/gu, "")
53 .replace(/[^\u0009\u000A\u000D -\uD7FF\uE000-\uFFFD\u{10000}-\u{10FFFF}]/gu, "")
5454 .trim();
5555 }
5656
test/og-card.test.ts +10
@@ -56,6 +56,16 @@ describe("og card", () => {
5656 wellFormed(renderCard({ kind: "app", title: "" }));
5757 });
5858
59 test("XML 1.0's Char ranges keep both ends and drop what lies between", () => {
60 // https://www.w3.org/TR/xml/#charsets — #x20-#xD7FF | #xE000-#xFFFD | #x10000-#x10FFFF
61 const kept = "\u0020\uD7FF\uE000\uFFFD\u{10000}\u{10FFFF}";
62 for (const dropped of ["\u0001", "\u001F", "\uD800", "\uDFFF", "\uFFFE", "\uFFFF"]) {
63 const svg = renderCard({ kind: "repo", title: `a${kept}${dropped}b` });
64 wellFormed(svg);
65 expect(svg).toContain(`a${kept}b`);
66 }
67 });
68
5969 test("fit and escapeXml", () => {
6070 expect(fit("short", 30, 1000)).toBe("short");
6171 expect(fit("a".repeat(200), 30, 300).endsWith("…")).toBe(true);
test/shards.test.ts +13 −3
@@ -508,16 +508,26 @@ describe("the gate audit alone, first in the check", () => {
508508 expect(green.err).toBe("");
509509 }, 30_000);
510510
511 test("forge.json audits before tsc, and tsc runs incremental from the check's cache", async () => {
511 test("forge.json audits before tsc, and the native tsc runs incremental from the check's cache", async () => {
512512 const check = parseCheck(readFileSync(new URL("../forge.json", import.meta.url), "utf8"), undefined);
513513 if (!check.ok || check.value === undefined) throw new Error("no check");
514514 for (const command of [check.value.run, check.value.fast ?? ""]) {
515515 expect(command.startsWith("bun scripts/gate.ts && ")).toBe(true);
516 expect(command.indexOf("scripts/gate.ts")).toBeLessThan(command.indexOf("tsc.js"));
517 expect(command).toContain("--max-old-space-size=2048");
516 // TypeScript 7 (#1864): a Go binary, so its heap bound is GOMEMLIMIT, not node's.
517 expect(command).toContain(" && GOMEMLIMIT=2GiB ./node_modules/typescript/bin/tsc --noEmit ");
518 expect(command.indexOf("scripts/gate.ts")).toBeLessThan(command.indexOf("bin/tsc"));
518519 expect(command).toContain(`--incremental --tsBuildInfoFile ${CHECK_CACHE}/tsc.tsbuildinfo`);
519520 }
520521 });
522
523 test("`typescript` is the native 7.x checker; 5.x stays only as the compiler API hollow.ts walks", async () => {
524 const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
525 expect(pkg.dependencies ?? {}).toEqual({});
526 expect(pkg.devDependencies["typescript"]).toMatch(/^\^7\./);
527 expect(pkg.devDependencies["typescript-api"]).toMatch(/^npm:typescript@\^5\./);
528 const checker = await import("typescript/package.json");
529 expect(checker.version).toMatch(/^7\./);
530 });
521531 });
522532
523533 describe("one file per worker, timed", () => {

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs