Hi — looking for a sanity check before filing a GitHub issue.
With jco transpile --instantiation async, importing a WIT function named
reject (also resolve, and likely promise / gen / runNext /
maybeSyncReturn) produces JS that fails to load:
SyntaxError: Identifier 'reject' has already been declared
reject / resolve are legal WIT identifiers (not WIT keywords, not JS
keywords). The collision is with locals that the instantiation helper emits
in the same function scope.
package demo:jco;
interface respondent {
reject: func(id: u32);
}
world demo {
import respondent;
export run: func(id: u32);
}
Generated instantiate() contains, in one scope:
const { reject } = imports['demo:jco/respondent'];
// ...
let promise, resolve, reject; // instantiation helper
Either the import or the helper locals are deconflicted (e.g. reject$1), same as other name clashes — module loads cleanly.
Imports go through LocalNames in render_imports, but the helper is a raw string in transpile_bindgen.rs (let promise, resolve, reject; plus gen / runNext / maybeSyncReturn) and those names are never registered via exclude_globals / create_once. So the deconflicter never sees them.
Specific to --instantiation mode (default ESM puts the helper in a $init IIFE, so this is shadowing rather than redeclaration). Still present on bindgen from main last we checked, and on @bytecodealliance/jco@1.10.2.
Related precedent: #1898 / #1899 (exported eval not sanitized) — same “bindings must produce valid JS for any legal WIT name” principle.
No wasm toolchain needed:
npm i @bytecodealliance/jco@1.10.2 # also fails on newer in our checks up to 1.34.0
# demo.wit as above
jco transpile demo.wit --stub --instantiation async --no-typescript -o out
node -e "import('./out/demo.js').then(() => console.log('OK'), e => console.log('FAIL:', e.message))"
# FAIL: Identifier 'reject' has already been declared
Control: rename reject → anything else → OK.
Hey @Mike M thanks for the report -- yeah that is definitely worth filing an issue for! I think basically the list that needs to be updated is here, but definitely this is worth filing an issue for and having the repro will be great to add to Jco.
https://github.com/bytecodealliance/jco/issues/2134
Hey @Mike M Thanks for filing the issue! Do you want to take a stab at it? If not I'm happy to get to it (hopefully today/by monday!)
yeah, sure! I wouldn't have presumed, but I'm happy to. Gimme a few.
One thought I'd like your input on: since this isn't a forbidden JS keyword, it's bindgen’s own helper locals that weren’t reserved. So what do you think about either:
1) Extend global_names (or I could create a sibling call when instantiation/$init is emitted) with the helper locals:
promise, resolve, reject, gen, runNext, maybeSyncReturn, normalizeInstantiationError
Then an imported reject becomes reject$1 only when it actually collides — which is how LocalNames seem to work?
or...
2) perhaps even better (for a future where this chunk of helper code is modified again), leverage LocalNames directly and wrap those keyword in local_names.create_once(...), which seems to be the precedent for preventing collisions in the bindings, e.g.,
let promise = local_names.create_once("promise");
let resolve = local_names.create_once("resolve");
let reject = local_names.create_once("reject");
let run_next = local_names.create_once("runNext");
// ...
uwrite!(output, "
let {promise}, {resolve}, {reject};
function {run_next} (value) {{ ... }}
...
", ...);
This has the benefit of not adding the name to global_names once, only to have another name pop up later that also needs to be added to global_names and leads to this same bug.
Thoughts?
Here's the PR if you want to look at the impl I suggest above. Happy to tweak/pivot as needed. Pls advise when you have a moment.
https://github.com/bytecodealliance/jco/pull/2135
Thanks for the PR! IMO I'm OK with the approach -- these local names are just slightly different from that initial global names list, and I think what you worked out there is pretty reasonable.
It's a bit of a different paradigm to deal with, but I think the separation is pretty easy to reason about for anyone stumbling upon it -- as soon as someone sees instantiation_helper_names it would be pretty obvious what is happening!
Thanks again for putting a PR up for this -- it looks like there was a slight regression, addin ga PR to fix that now
Thank you!
something was still wrong. I just pushed an adjusted commit. I'll watch it.
looks like my commit message was making commitinit unhappy.
The other CI fail looks unrelated.
Hey also, yes, apologies one of the CI failures is actually has nothing to do with your PR! I've approved the CI run and we should be able to get this merged in soon
That was fast! :) Thanks for your help, @Victor Adossi !
no worries! Still need to get this out so hopefully today or tomorrow we'll have new versions across the ecosystem (jco-transpile & jco) so that this can be usable from a proper release!
Hey @Mike M so Jco 1.35.0 has the released code!
https://www.npmjs.com/package/@bytecodealliance/jco/v/1.35.0
Would love if you could confirm things are fixed!
Everything looks good from here @Victor Adossi ! Thanks so much for your support and speedy integration. Much appreciated! Y'all are killin' it! :)
Last updated: Oct 11 2026 at 02:20 UTC