You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Publish an official wasip1 (WASI) build artifact of tsgo #63858
Please consider publishing an official GOOS=wasip1 GOARCH=wasm build artifact of tsgo alongside the native platform binaries (npm @typescript/* platform packages / GitHub release assets).
Context
We embed JavaScript tooling in WebAssembly (WASI) environments and evaluated running the TypeScript 7 compiler there. Building from source works today with no patches:
GOOS=wasip1 GOARCH=wasm go build ./cmd/tsgo # at tag typescript/v7.0.2, go1.26.4
One command, ~40s build, ~48.6 MB core module.
Runs under wasmtime (preview1): diagnostics are byte-identical to the native binary on our test projects, exit codes preserved.
Single-threaded as expected for this target (~12× native wall-clock in our samples — still practical for small/medium projects).
Known limitation we observed: wasm32's 4 GiB memory ceiling prices out very large programs (a type-fest-scale check needed ~6.6 GB natively).
Why an official artifact
The build is trivially reproducible, but an official artifact would give WASI/browser-adjacent consumers a supported, versioned binary instead of each project maintaining its own build pipeline, and would make regressions on this target visible to CI here rather than downstream.
Happy to share our verification receipts (diagnostic byte-comparisons vs the native binary) if useful.
What's the expectation? Just that you want a .wasm file to be on npm somewhere that's certainly easy enough, though since that's not a real platform, it'll have to be special cased. Not a big deal, esbuild does it.
It would be cool if we could fall back to this package + Node's Wasi shim if the package is installed and no native binary exists, but last I checked, we crash Node's shims.
A versioned .wasm published per release. The esbuild-wasm shape you mention is exactly right: an npm package in the platform-package family (or a GitHub release asset; npm is more convenient for toolchain integration, but either works). Special-cased is OK. wasip1 consumers commonly expect that.
The artifact built in your CI, even if nothing runs against it at first, so a change that breaks GOOS=wasip1 compilation shows up here instead of downstream.
On the Node WASI-shim fallback: that would be a nice bonus, but I'd decouple it from this ask. Our hosts are real WASI runtimes (Wasmtime and browser-adjacent hosts), where the current build already runs cleanly: byte-identical diagnostics vs the native binary across our test projects, exit codes preserved. The Node shim crash could be a separate issue.
Followed up on the shim crash — two separate things were going on:
The crash was a Node bug, already fixed. Node 20–22 segfault inside V8 when a wasm program grows memory past a few hundred MB (tsgo does that on any real project). Node 23 and 24 are clean — we ran real checks up to 3.5 GiB. So the fallback works today if it's gated on node >= 23: with returnOnExit: false and without passing the host env through (Go's wasip1 runtime derives cwd from $PWD), tsgo -p under node:wasi produces output diff-identical to the native binary. Happy to share the loader proof-of-concept and receipts.
LSP mode hangs for a different, tsgo-side reason — filed as #63862 with a small fix.
One gotcha here; should that wasm bundle include all of our embedded artifacts? Or just use the same exe location stuff we currently do? I'm kinda unsure.
I'd just embed. That's what the default GOOS=wasip1 go build does anyway, and it's the build we tested — output matched native with zero lib-path setup, under both wasmtime and node:wasi. There isn't really a "next to the exe" in wasm anyway (no real executable path), so relative lookup would turn into custom setup for every host. The libs only add a few MB to a ~49 MB module, and you get one self-contained file that runs anywhere.
Except that it's useless for editor use due to the paths getting exposed over the wire and then pointing nowhere, and error messages lying about their contents.
I have success in providing argv0 in hosts, but not all hosts let one do that.
Fair, our LSP testing only got as far as hover, so we never hit the lib-path problem. Could it be both? Embed by default so the CLI is one file that works anywhere, plus a --lib-dir flag/env for editor hosts pointing at the on-disk libs the package ships anyway. A flag or env var is also just more explicit than routing the lib location through argv0, and doesn't depend on the host exposing it.
Activity
jakebailey commentedon Jul 22, 2026
What's the expectation? Just that you want a
.wasmfile to be on npm somewhere that's certainly easy enough, though since that's not a real platform, it'll have to be special cased. Not a big deal, esbuild does it.It would be cool if we could fall back to this package + Node's Wasi shim if the package is installed and no native binary exists, but last I checked, we crash Node's shims.
calvinrp commentedon Jul 22, 2026
Concretely, two things would cover it:
.wasmpublished per release. Theesbuild-wasmshape you mention is exactly right: annpmpackage in the platform-package family (or a GitHub release asset;npmis more convenient for toolchain integration, but either works). Special-cased is OK.wasip1consumers commonly expect that.GOOS=wasip1compilation shows up here instead of downstream.On the Node WASI-shim fallback: that would be a nice bonus, but I'd decouple it from this ask. Our hosts are real WASI runtimes (
Wasmtimeand browser-adjacent hosts), where the current build already runs cleanly: byte-identical diagnostics vs the native binary across our test projects, exit codes preserved. The Node shim crash could be a separate issue.calvinrp commentedon Jul 22, 2026
Followed up on the shim crash — two separate things were going on:
The crash was a Node bug, already fixed. Node 20–22 segfault inside V8 when a wasm program grows memory past a few hundred MB (tsgo does that on any real project). Node 23 and 24 are clean — we ran real checks up to 3.5 GiB. So the fallback works today if it's gated on node >= 23: with
returnOnExit: falseand without passing the host env through (Go's wasip1 runtime derives cwd from$PWD),tsgo -pundernode:wasiproduces output diff-identical to the native binary. Happy to share the loader proof-of-concept and receipts.LSP mode hangs for a different, tsgo-side reason — filed as #63862 with a small fix.
jakebailey commentedon Jul 24, 2026
One gotcha here; should that wasm bundle include all of our embedded artifacts? Or just use the same exe location stuff we currently do? I'm kinda unsure.
calvinrp commentedon Jul 24, 2026
I'd just embed. That's what the default
GOOS=wasip1 go builddoes anyway, and it's the build we tested — output matched native with zero lib-path setup, under both wasmtime and node:wasi. There isn't really a "next to the exe" in wasm anyway (no real executable path), so relative lookup would turn into custom setup for every host. The libs only add a few MB to a ~49 MB module, and you get one self-contained file that runs anywhere.jakebailey commentedon Jul 24, 2026
Except that it's useless for editor use due to the paths getting exposed over the wire and then pointing nowhere, and error messages lying about their contents.
I have success in providing
argv0in hosts, but not all hosts let one do that.calvinrp commentedon Jul 24, 2026
Fair, our LSP testing only got as far as hover, so we never hit the lib-path problem. Could it be both? Embed by default so the CLI is one file that works anywhere, plus a
--lib-dirflag/env for editor hosts pointing at the on-disk libs the package ships anyway. A flag or env var is also just more explicit than routing the lib location through argv0, and doesn't depend on the host exposing it.jakebailey commentedon Jul 24, 2026
Not sure, I think it will also depend on what we do in the playground.
trobertson-graymatterllc commentedon Aug 24, 2026