diff --git a/apps/desktop/package.json b/apps/desktop/package.json index ddef3ca9eb14..848a5a4de760 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -7,7 +7,7 @@ "scripts": { "ensure:electron": "node scripts/ensure-electron-runtime.mjs", "start": "node scripts/start-electron.mjs", - "typecheck": "tsc --noEmit", + "typecheck": "node ../../scripts/typecheck.ts", "test": "vp test run --passWithNoTests", "smoke-test": "node scripts/smoke-test.mjs" }, diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 53b5476d3a2d..fa979dc7fe4c 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -43,7 +43,7 @@ "sync:pierre-icons": "node modules/t3-markdown-text/scripts/sync-pierre-file-icons.mjs", "generate": "node --disable-warning=MODULE_TYPELESS_PACKAGE_JSON scripts/generate-uniwind-themes.mts", "test": "vp test run --config ../../vite.config.ts --dir .", - "typecheck": "tsc --noEmit" + "typecheck": "node ../../scripts/typecheck.ts" }, "dependencies": { "@clerk/expo": "catalog:", diff --git a/apps/server/package.json b/apps/server/package.json index 2aa6a961d6eb..ba6447018459 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -19,7 +19,7 @@ "build:bundle": "vp pack", "build:exe": "node scripts/cli.ts build-exe", "start": "node dist/bin.mjs", - "typecheck": "tsc --noEmit", + "typecheck": "node ../../scripts/typecheck.ts", "test": "vp test run", "record:codex-replay": "bun scripts/record-codex-app-server-replay-fixture.ts", "record:claude-replay": "bun scripts/record-claude-agent-sdk-replay-fixture.ts", diff --git a/apps/server/scripts/record-pi-rpc-replay-fixture.ts b/apps/server/scripts/record-pi-rpc-replay-fixture.ts index 32d0dc7c7560..00621a2879dd 100644 --- a/apps/server/scripts/record-pi-rpc-replay-fixture.ts +++ b/apps/server/scripts/record-pi-rpc-replay-fixture.ts @@ -244,13 +244,13 @@ const record = Effect.gen(function* () { ); const entries: Array = []; - const placeholder = { + const placeholder: ProviderReplayTranscript = { provider: PI_PROVIDER, protocol: PI_RPC_REPLAY_PROTOCOL, version: piVersion, scenario: fixture.name, entries: [], - } satisfies ProviderReplayTranscript; + }; const layerRecordingSpawner = Layer.effect( ChildProcessSpawner.ChildProcessSpawner, Effect.map(Effect.service(ChildProcessSpawner.ChildProcessSpawner), (live) => diff --git a/apps/web/package.json b/apps/web/package.json index d17795f15c5d..af778d412fc2 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -8,7 +8,7 @@ "dev": "vp dev", "build": "vp build", "preview": "vp preview", - "typecheck": "tsc --noEmit", + "typecheck": "node ../../scripts/typecheck.ts", "test": "vp test run --passWithNoTests --project unit" }, "dependencies": { diff --git a/docs/operations/development.md b/docs/operations/development.md index b7e1b257d0b2..cc27b16386e4 100644 --- a/docs/operations/development.md +++ b/docs/operations/development.md @@ -106,6 +106,14 @@ vp lint vp run --filter typecheck ``` +TypeScript workspaces use pinned tsc-rs on macOS ARM64 and Linux x64, with Effect +diagnostics built in. Other platforms keep Effect-patched TypeScript +until tsc-rs ships their binaries. Custom compiler flags also use that compiler, +including watch mode. Keep `typescript`, `@effect/tsgo`, and the +`effect-tsgo patch` prepare step: they provide the fallback compiler and Effect +editor features that tsc-rs does not yet include. +Marketing keeps `astro check` for Astro files. + Use `vp run lint:mobile` for native mobile changes. CI owns the full suite; see [ci.yml](../../.github/workflows/ci.yml) for its current jobs. The [manual Windows lane](../../.github/workflows/windows-tests.yml) is available for focused diff --git a/infra/relay/package.json b/infra/relay/package.json index 472f2606f20a..86c5a1c2f9ee 100644 --- a/infra/relay/package.json +++ b/infra/relay/package.json @@ -9,7 +9,7 @@ "tunnels:census": "node scripts/tunnel-census.ts", "destroy": "alchemy destroy", "test": "vp test run --config ../../vite.config.ts --dir .", - "typecheck": "tsc --noEmit" + "typecheck": "node ../../scripts/typecheck.ts" }, "dependencies": { "@clerk/backend": "catalog:", diff --git a/knip.jsonc b/knip.jsonc index 24d0f85db881..7ad78a09111c 100644 --- a/knip.jsonc +++ b/knip.jsonc @@ -18,8 +18,8 @@ }, "scripts": { // Knip loads its preprocessor through a CLI option; native verification and - // worktree setup (from t3.json) run directly. - "entry": ["knip-schemas.ts", "mobile-native-client.ts", "setup-worktree.ts"], + // worktree setup (from t3.json), and the shared typecheck launcher run directly. + "entry": ["knip-schemas.ts", "mobile-native-client.ts", "setup-worktree.ts", "typecheck.ts"], }, "apps/server": { // Vite+ pack entries and the launcher used by installed background services. diff --git a/oxlint-plugin-t3code/package.json b/oxlint-plugin-t3code/package.json index 35f13bbf3828..7344aa4bf1e7 100644 --- a/oxlint-plugin-t3code/package.json +++ b/oxlint-plugin-t3code/package.json @@ -3,7 +3,7 @@ "private": true, "type": "module", "scripts": { - "typecheck": "tsc --noEmit", + "typecheck": "node ../scripts/typecheck.ts", "test": "vp test run --config ../vite.config.ts --dir ." }, "dependencies": { diff --git a/package.json b/package.json index 559801c550c7..a5c5412f5600 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "@shadcn/lint": "0.1.5", "@types/node": "catalog:", "knip": "6.34.0", + "tsc-rs": "0.1.0", "typescript": "catalog:", "vite-plus": "catalog:" }, diff --git a/packages/client-runtime/package.json b/packages/client-runtime/package.json index 3b72e948454a..c6b7181efa28 100644 --- a/packages/client-runtime/package.json +++ b/packages/client-runtime/package.json @@ -393,7 +393,7 @@ } }, "scripts": { - "typecheck": "tsc --noEmit", + "typecheck": "node ../../scripts/typecheck.ts", "test": "vp test run" }, "dependencies": { diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 2daf804aadbf..07524edecfaa 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -21,7 +21,7 @@ } }, "scripts": { - "typecheck": "tsc --noEmit", + "typecheck": "node ../../scripts/typecheck.ts", "test": "vp test run --config ../../vite.config.ts --dir ." }, "dependencies": { diff --git a/packages/effect-acp/package.json b/packages/effect-acp/package.json index 0ceaf8e8af95..096a5de24186 100644 --- a/packages/effect-acp/package.json +++ b/packages/effect-acp/package.json @@ -37,7 +37,7 @@ } }, "scripts": { - "typecheck": "tsc --noEmit", + "typecheck": "node ../../scripts/typecheck.ts", "test": "vp test run --config ../../vite.config.ts --dir .", "generate": "node scripts/generate.ts" }, diff --git a/packages/effect-codex-app-server/package.json b/packages/effect-codex-app-server/package.json index 608db8d2ca30..775def638619 100644 --- a/packages/effect-codex-app-server/package.json +++ b/packages/effect-codex-app-server/package.json @@ -29,7 +29,7 @@ } }, "scripts": { - "typecheck": "tsc --noEmit", + "typecheck": "node ../../scripts/typecheck.ts", "test": "vp test run --config ../../vite.config.ts --dir .", "generate": "node scripts/generate.ts", "probe": "node test/examples/codex-app-server-probe.ts" diff --git a/packages/shared/package.json b/packages/shared/package.json index 3732ebd5cfa6..db626f3917f6 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -401,7 +401,7 @@ } }, "scripts": { - "typecheck": "tsc --noEmit", + "typecheck": "node ../../scripts/typecheck.ts", "test": "vp test run --config ../../vite.config.ts --dir ." }, "dependencies": { diff --git a/packages/ssh/package.json b/packages/ssh/package.json index 04b6173bd252..beaab1b95fa4 100644 --- a/packages/ssh/package.json +++ b/packages/ssh/package.json @@ -25,7 +25,7 @@ } }, "scripts": { - "typecheck": "tsc --noEmit", + "typecheck": "node ../../scripts/typecheck.ts", "test": "vp test run --config ../../vite.config.ts --dir ." }, "dependencies": { diff --git a/packages/tailscale/package.json b/packages/tailscale/package.json index e51f07739370..e8d58d7b8013 100644 --- a/packages/tailscale/package.json +++ b/packages/tailscale/package.json @@ -9,7 +9,7 @@ } }, "scripts": { - "typecheck": "tsc --noEmit", + "typecheck": "node ../../scripts/typecheck.ts", "test": "vp test run --config ../../vite.config.ts --dir ." }, "dependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 17a19d1ffbeb..5dc603394475 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -141,6 +141,9 @@ importers: knip: specifier: 6.34.0 version: 6.34.0 + tsc-rs: + specifier: 0.1.0 + version: 0.1.0 typescript: specifier: 'catalog:' version: 7.0.2 @@ -5406,6 +5409,18 @@ packages: '@tiptap/starter-kit@3.31.3': resolution: {integrity: sha512-WKof9RewdmGHvWJ1wn0/HVNG2mV+HOgVRyJkKekuM9fgr6BZAAH/xZsWE1eon+94JnQ+KtK2ThydXQM/qc6b2A==} + '@tsc-rs/darwin-arm64@0.1.0': + resolution: {integrity: sha512-VgS/GcwcSOOdSlQnpw4etSnL5xFZNwfuPIa7KtBpfC6Qg1Fg0w150s58a00Zs6D6DnDd7yBxx3XgHcyNLjzaPA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [darwin] + + '@tsc-rs/linux-x64@0.1.0': + resolution: {integrity: sha512-gzswr3ihKhkviz2ZcmkyVomXY4jqMPT4cWu/rjJqbJtMXJJHuZ9H5Eqv2fcCX9AB0i18EmPNva7Gp7NYaAdpJA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [linux] + '@tweenjs/tween.js@23.1.3': resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==} @@ -10847,6 +10862,11 @@ packages: resolution: {integrity: sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==} engines: {node: '>=6.10'} + tsc-rs@0.1.0: + resolution: {integrity: sha512-ibJNjypqagRtvc3wrQALo4VxOd0QmujczOIqMm97ioTkRuMS3UQlmX3f17IkhsLwgQ2az4UuWzfuaYliUdf3xQ==} + engines: {node: '>=16.20.0'} + hasBin: true + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -15888,6 +15908,12 @@ snapshots: '@tiptap/extensions': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3) '@tiptap/pm': 3.31.3 + '@tsc-rs/darwin-arm64@0.1.0': + optional: true + + '@tsc-rs/linux-x64@0.1.0': + optional: true + '@tweenjs/tween.js@23.1.3': {} '@tybys/wasm-util@0.10.3': @@ -22178,6 +22204,11 @@ snapshots: ts-dedent@2.3.0: {} + tsc-rs@0.1.0: + optionalDependencies: + '@tsc-rs/darwin-arm64': 0.1.0 + '@tsc-rs/linux-x64': 0.1.0 + tslib@2.8.1: {} type-fest@0.13.1: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 92373445f5ed..d9622fd7697d 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -19,6 +19,7 @@ allowBuilds: msw: false node-pty: true sharp: true + tsc-rs: true utf-8-validate: false workerd: false @@ -58,6 +59,9 @@ catalog: yaml: ^2.9.0 minimumReleaseAgeExclude: + - tsc-rs@0.1.0 + - "@tsc-rs/linux-x64@0.1.0" + - "@tsc-rs/darwin-arm64@0.1.0" - expo-glass-effect@58.0.3 - expo-blur@58.0.3 - expo-audio@58.0.4 diff --git a/scripts/package.json b/scripts/package.json index 30890bde1002..6bd2770a59ec 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -3,7 +3,7 @@ "private": true, "type": "module", "scripts": { - "typecheck": "tsc --noEmit", + "typecheck": "node typecheck.ts", "test": "vp test run --config ../vite.config.ts --dir ." }, "dependencies": { diff --git a/scripts/typecheck.ts b/scripts/typecheck.ts new file mode 100644 index 000000000000..4b401616a09e --- /dev/null +++ b/scripts/typecheck.ts @@ -0,0 +1,44 @@ +// @effect-diagnostics nodeBuiltinImport:off - This compiler launcher runs before the application runtime. +import { + HostProcessArchitecture, + HostProcessArguments, + HostProcessPlatform, +} from "@t3tools/shared/hostProcess"; +import * as Effect from "effect/Effect"; +import * as NodeChildProcess from "node:child_process"; +import * as NodeURL from "node:url"; + +function runCompiler(compilerUrl: URL, args: ReadonlyArray) { + const result = NodeChildProcess.spawnSync( + process.execPath, + [NodeURL.fileURLToPath(compilerUrl), ...args], + { stdio: "inherit" }, + ); + if (result.error) throw result.error; + if (result.signal) throw new Error(`Typecheck terminated by ${result.signal}`); + return result.status ?? 1; +} + +const platform = Effect.runSync(HostProcessPlatform); +const architecture = Effect.runSync(HostProcessArchitecture); +const extraArgs = Effect.runSync(HostProcessArguments).slice(2); +const supportsRust = + (platform === "darwin" && architecture === "arm64") || + (platform === "linux" && architecture === "x64"); + +if (supportsRust && extraArgs.length === 0) { + process.exitCode = runCompiler( + new URL("./bin/tsc-rs", import.meta.resolve("tsc-rs/package.json")), + ["--noEmit"], + ); +} else { + process.stderr.write( + supportsRust + ? "Custom compiler arguments use Effect-patched TypeScript.\n" + : `tsc-rs has no ${platform}-${architecture} binary; using Effect-patched TypeScript.\n`, + ); + process.exitCode = runCompiler( + new URL("./bin/tsc", import.meta.resolve("typescript/package.json")), + ["--noEmit", ...extraArgs], + ); +}