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!
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.
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)
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!
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.
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)
jawsm skips the IR all together so not quite the same but they're all incredibly interesting
![]()
Well that is intense
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:
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
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
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
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
Oh could you link me to that markdown file in a gist? Security being what it is and all
I'm actually hacking on getting custom wit support in porffor right now.
I wish we could share more of the tooling to make it easier to support new JS engines/compilers
Like js-component-bindgen could probably be part of it
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!
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).
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
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.
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.
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:
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.
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?
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
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
Also, ComponentizeJS is certainly maintained -- there's a new release going out today/tomorrow IIRC.
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
Also, ComponentizeJS is certainly maintained
I meant maintained in the long run. IIUC it will soon be replaced by the wit-dylib based version
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!).
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
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 :)
plerryon? perpli? plirry?
Naming things is unfortunately one of the two hard problems in computer science !
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
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
wiTS
time will come
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.
One thing that I would also recommend keeping tabs on is component size -- had some surprises here:
https://github.com/bytecodealliance/jco/pull/2299
Fully agree, it does: https://github.com/ahirner/perry-wit/blob/main/tests/benchmark.json
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