Stream: jco

Topic: Ahead of time TS to WASM/I


view this post on Zulip Alexander Hirner (Oct 22 2026 at 02:51):

I saw your recent perry experiments @Victor Adossi . I'm shimming P3 support underneath, and some leaner pliron backend. It does WIT based type generation.
It's already running in the browser itself with jco: https://ahirner.github.io/perry-wit/
https://github.com/ahirner/perry-wit
LMK what you think!

view this post on Zulip Victor Adossi (Oct 22 2026 at 02:59):

Hey @Alexander Hirner thanks for taking the time to share this, this is awesome!!

Perry-WIT compiles static TypeScript ahead-of-time directly into lightweight, high-performance WebAssembly components targeting WASI 0.3 (Preview 3).

The fact that you're already targeting P3 is fantastic (thanks for doing the hard work to keep up with the releases and target the latest).

In an ideal world this would get merged right into Perry (if you were into that), but in the time before that I'd love to get this supported and documented right in Jco as an option. I was going to go try to get to the wit-bindgen changes in order to support Perry but it looks like you've already done the work, at the very least since we currently support Perry via CLI perry-wit would be just as easy to support.

view this post on Zulip Victor Adossi (Oct 22 2026 at 03:04):

One thing that would really take this to the next level is a perry-wit NPM package (via napi-rs/neon/etc) that is easy to install.

For Perry (and other external builders like Porffor which IMO Jco should support too which @Mendy Berger called out) I've leaned towards not including the package in question as a dep in Jco (maybe as an optional or peer dep? but it does annoying things to semver requirements for bumps), so if perry-wit had an NPM package it would slot right in (I would absolutely be on board with adding the perry-wit package as a backend import)

view this post on Zulip Alexander Hirner (Oct 22 2026 at 03:05):

Basically yes :tm: regarding upstream. As first priority, we harden it for internal use, where P3 is actually very beneficial regarding future/async support.
I'm not too keen to give up on the Rust native pliron backend instead of LLVM :) Hence, a clearly cut yet shared crate boundary between perry HIR + WIT would be more feasible in the (mid?) term.
Short term, I definitely track perry (HIR) upstream!

view this post on Zulip Victor Adossi (Oct 22 2026 at 03:06):

Oh I certainly like the pliron backend bit -- all the better to compile the compiler to WASM, which is one of those things that should be a thing already but most aren't focused on it yet.

view this post on Zulip Victor Adossi (Oct 22 2026 at 03:08):

This is the first I've heard of pliron, so looking into it now I'm not a compiler dev, but very cool project!

Another promising project that is now stale was jawsm (https://github.com/drogus/jawsm)

view this post on Zulip Victor Adossi (Oct 22 2026 at 03:08):

jawsm skips the IR all together so not quite the same but they're all incredibly interesting

view this post on Zulip Victor Adossi (Oct 22 2026 at 03:09):

cuda-oxide is using pliron

Well that is intense

view this post on Zulip Alexander Hirner (Oct 22 2026 at 03:09):

Pliron is used in cubecl e.g. for ML lowering, hence that's what I'm more familiar with (based on computer vision solutions we are doing).
I didn't know jawsm! :looking:

view this post on Zulip Victor Adossi (Oct 22 2026 at 03:11):

Suuuuuper exciting tech, I just queued up like 3 videos in Pliron. The combinatorics here are so fascinating here because it feels like your pipeline could also switch out pliron for cranelift

view this post on Zulip Victor Adossi (Oct 22 2026 at 03:12):

There are so many points on the design space that the horizon is really wide and exciting. Thanks again for posting here, this is like another half year of exciting approaches/ways forward to explore

view this post on Zulip Alexander Hirner (Oct 22 2026 at 03:18):

I need to think of how a perry-wit NPM package would let compilation slot into jco pipelines more.
You are proposing this for easier distribution and use? And pardon, I'm not steeped in how jco is architected.

The cleaner crate boundary is mostly in my head but somewhat jotted out in this md. Basically, there is a core we could share across structurally typed functional languages. Most importantly, I think are very solid and efficient WASM-type specializations.
J-core_common_witty_expressions.md

view this post on Zulip Victor Adossi (Oct 22 2026 at 03:57):

Ah sorry yes, I literally only mean that I'd like to be able to use perry-wit from inside the NPM ecosystem more easily! napi-rs/neon will basically bundle the package such that the platform specific binaries are easier to install

view this post on Zulip Victor Adossi (Oct 22 2026 at 03:58):

Oh could you link me to that markdown file in a gist? Security being what it is and all

view this post on Zulip Mendy Berger (Oct 22 2026 at 04:02):

I'm actually hacking on getting custom wit support in porffor right now.

view this post on Zulip Mendy Berger (Oct 22 2026 at 04:02):

I wish we could share more of the tooling to make it easier to support new JS engines/compilers

view this post on Zulip Mendy Berger (Oct 22 2026 at 04:03):

Like js-component-bindgen could probably be part of it

view this post on Zulip Mendy Berger (Oct 22 2026 at 04:59):

Okay, I might actually have an idea here: bindgen.rs in ComponentizeJS is remarkably reusable. With just a few changes (< 100 lines) we can make it generic and have it work with any engine.
I just did and experiment with porffor and boa. Boa took < 500 lines to get a full working engine with custom wit!

view this post on Zulip Victor Adossi (Oct 22 2026 at 05:09):

We definitely could -- and js-component-bindgen is available via Rust and JS (via jco-transpile) precisely so that it can be reused more! We could definitely do better on packaging though. But it's a host side adapter tool, not a guest side one so I'm not sure if it quite fits...

The basic level that we can share is the wasm-tools level -- because once you start to do wit-based codegen you're instantly in per-language territory and it becomes hard to adjust to generically each language specific exprssion of certain concepts.

Trying to share wit-bindgen itself could work though, IF we get more abstract about what wit-bindgen outputs -- like I can imagine some export greet: func(s: string) -> string producing an stack based IR-like output of:

create-function-stub args=1
lower-arg type=string
run-stub-inner
lift-result type=string
return

and then another layer under that that actually converts the IR (i.e. implement one relatively large trait and your language is covered, as long as is looks like most languages/you can manipulate text).

The problem starts looking more and more compiler-shaped by the second. This could also unlock cross language generation too which is awesome.

Anyway, the closest we have to such a system we have today is wit-dylib:

https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wit-dylib

(Amazing work from Alex and Joel on wit-dylib)

wit-dylib is awesome for interpreted langauges, but it makes less sense for compiled languages (though you absolutely can use it for compiled languages, it would just be inefficient).

view this post on Zulip Victor Adossi (Oct 22 2026 at 05:12):

Mendy Berger said:

Okay, I might actually have an idea here: bindgen.rs in ComponentizeJS is remarkably reusable. With just a few changes (< 100 lines) we can make it generic and have it work with any engine.
I just did and experiment with porffor and boa. Boa took < 500 lines to get a full working engine with custom wit!

Looks like we posted at the same time (or well more like I was writing my comment while you posted yours), but I'd recommend looking at wit-dylib and/or the new componentize-js and building from there! This is how https://github.com/andreiltd/componentize-qjs is built

view this post on Zulip Mendy Berger (Oct 22 2026 at 05:22):

I didn't go with wit-dylib for porffor because it's kind of defeating the purpose of having a compiler in the first place. I'll take a look at the new componentize-js though.

view this post on Zulip Mendy Berger (Oct 22 2026 at 05:22):

What I think is special here is that unlike other languages, JS has a ton of engines/compilers, and each one brings it's own unique strengths, so having a tool that generates most of the JS glue code would enable a lot of experimentation around speed, bin size, correctness, etc.

view this post on Zulip Victor Adossi (Oct 22 2026 at 05:22):

The reason I think it's hard to generalize here (at least I thought -- really would be happy to be corrected here) is that the way these engines all work is slightly different:

Perry goes to LLVM IR
Porffor goes to C
QuickJS is a embeddable engine in C (usable from rust in the case of componentize-qjs)
Boa is an embeddable engine in Rust

The embeddable engines in X (that can be used from Rust) basically all fit the wit-dylib shape

Porfor/Perry seem a little less like the same shape, only because even after you generate bindings you need to plug into their linking mechanism to get the shims you want natively. Wrapping the binary in a generated Wasm/Rust shell is also a great fast option but is just slightly less efficient (perfectly OK IMO) -- "old" componentize-js splicing embedder does just that (built for the engine use case, of course) w/ C++, "new" componentize-js dynamically generates the WASM (!) for the connection points.

So there are like... a few points in the space, I think we might even be able to name all of them:

  1. Fully native, your language toolchain chain reads WIT and puts the right exports and imports on the binary directly
  2. Partially native + custom linked, your language toolchain produces some of the right exports/imports, and you generate some extra code that links directly to the first batch of code
  3. Partially native + wrapped, your language toolchain produces some of the right exports/imports and you wrap the entire output and always jump from wrapper language A to your actual language B.

view this post on Zulip Victor Adossi (Oct 22 2026 at 05:27):

Mendy Berger said:

I didn't go with wit-dylib for porffor because it's kind of defeating the purpose of having a compiler in the first place. I'll take a look at the new componentize-js though.

Yup, and this is precisely why they can't all have the same shape -- looks like we were writing at the same time again.

Engines you can generalize over easily (wit-dylib), but perry/porffor you can't quite, unless you wrap w/ a custom binary and add custom language specific code.

Basically once you try to do things fully natively to any given toolchain you need a wit-bindgen like layer and those are hard to abstract over, because of literal language syntax.

view this post on Zulip Mendy Berger (Oct 22 2026 at 05:30):

Yeah, seems like the new componentize-js uses wit-dylib :sad:
As I said before, bindgen.rs in ComponentizeJS could actually work for both runtimes and compilers since it does most of the work inside JS, but I guess that's no longer maintained?

view this post on Zulip Victor Adossi (Oct 22 2026 at 05:30):

Maybe you could generalize it across engines if you gave JS FFI semantics that were reusable across JS compilers! Some sort of consistent ABI for declaring compilation imports/exports

view this post on Zulip Victor Adossi (Oct 22 2026 at 05:36):

It's IMO fine to use wit-dylib in new componentize-js, that's exactly what wit-dylib is for, easier execution for engines that you need to call into.

bingen.rs can work for both rutimes and compilers (and so can wit-dylib) because it also depends on a spliced component with a consistent API -- am I missing something?

For maxium performance for either perry or porffor you need to generate code for their FFI paradigms and compile that inside their language toolchains, which is inherently toolchain specific

view this post on Zulip Victor Adossi (Oct 22 2026 at 05:37):

Also, ComponentizeJS is certainly maintained -- there's a new release going out today/tomorrow IIRC.

view this post on Zulip Mendy Berger (Oct 22 2026 at 06:03):

Maybe you could generalize it across engines if you gave JS FFI semantics that were reusable across JS compilers! Some sort of consistent ABI for declaring compilation imports/exports

Not sure that's necessary. You can just make that part configurable

view this post on Zulip Mendy Berger (Oct 22 2026 at 06:04):

Also, ComponentizeJS is certainly maintained

I meant maintained in the long run. IIUC it will soon be replaced by the wit-dylib based version

view this post on Zulip Victor Adossi (Oct 22 2026 at 11:07):

Hey @Alexander Hirner what do you think about giving perry-wit a new name?

I took some time to try to wrap my mind around how it works and I think it might be different enough from perry (though you use the internal parser & HIR) to warrant a new name entirely. You certainly extend perry in pliron but this starts to feel actually different from perry itself (maybe I'm the only one that thinks so!).

view this post on Zulip Victor Adossi (Oct 22 2026 at 11:09):

Really would like to see this approach adopted by perry, and as you say once internal hardening is done hopefully they adopt it as a new alternative backend upstream

view this post on Zulip Alexander Hirner (Oct 22 2026 at 13:23):

giving perry-wit a new name

No feelings at all. Anything coming to mind for what is basically perry HIR + WIT+ pliron? I should ask the PerryTS author if nothing strikes :)

view this post on Zulip Victor Adossi (Oct 22 2026 at 14:43):

plerryon? perpli? plirry?

Naming things is unfortunately one of the two hard problems in computer science !

view this post on Zulip Mendy Berger (Oct 23 2026 at 02:44):

I build/vibed a small project to show that reusing JS bindings is possible. It mostly just repurposes Jco and ComponentizeJS parts.
I got Boa, Porffor, and Static Hermes to work with it with relatively little code per engine/compiler

https://github.com/MendyBerger/js-guest-bindgen

view this post on Zulip Mendy Berger (Oct 23 2026 at 02:49):

I have never been deep in the weeds of implementing the component model, so @Victor Adossi probably has better judgment to decide if this is a project worth pursuing

view this post on Zulip Alexander Hirner (Oct 23 2026 at 10:30):

wiTS
time will come

view this post on Zulip Alexander Hirner (Oct 27 2026 at 20:35):

v0.1.2 features 24/24 upstream perry benchmarks running node vs in wasmtime.
https://github.com/ahirner/perry-wit/releases/tag/v0.1.2
Website is prettier too.

view this post on Zulip Victor Adossi (Oct 28 2026 at 01:59):

One thing that I would also recommend keeping tabs on is component size -- had some surprises here:

https://github.com/bytecodealliance/jco/pull/2299

view this post on Zulip Alexander Hirner (Oct 28 2026 at 05:32):

Fully agree, it does: https://github.com/ahirner/perry-wit/blob/main/tests/benchmark.json

view this post on Zulip Victor Adossi (Oct 28 2026 at 06:27):

If I'm reading that output right your HTTP (unbounded http bytes) workload wasm is 64k? That's fantastic!

I think we might switch to just using perry-wit directly in Jco if what I'm understanding is right and I can replicate w/ the node shims from perry turned on (that's what bloated the regular perry component size).

Would you be open to a contribution of perry-wit-as-a-library (I see right now there's not really an easy to include library crate and an NPM package? Would certainly love to make this well supported in Jco and that would be the easiest way to do so.

I'm conflicted on whether to remove all the supported backends as direct dependencies of jco or to add all of them (right now we include both componentize-js and componentize-qjs actually, but as we get more engines it may not make sense. People can of course use jco-transpile though).

Oh also one thing you might want to track is compilation speed -- how fast you can go from TS/JS to component wasm +/- optimization. I've found that Jawsm was actually the fastest approach, roughly 220ms, but of course it's incomplete and out of date at this point, and perf wasn't great.


Last updated: Oct 31 2026 at 23:03 UTC