We’ve open-sourced a tool that generates ReScript bindings directly from TypeScript definitions.
If you’ve ever wanted to use a TypeScript library in ReScript but found writing bindings manually tedious, this tool aims to automate much of that work.
Why we built it:
Reduce friction when adopting ReScript in existing TypeScript ecosystems
Speed up binding generation for large libraries
Make it easier for teams migrating from TypeScript to ReScript
We’re looking for feedback from the ReScript community on:
wow this looks awesome! I’ll try it! I’ll also take a look at the implementation. I’d be very interested in knowing more about the challenges you’ve faced and the bindings design decisions you’ve made.
Thanks! I’d love to hear your feedback after you’ve had a chance to try it.
The motivation came from one of our internal projects. We have a design system called blend-design-system, and we maintain ReScript bindings for it in blend-rescript.
We were writing the bindings manually, and whenever a new version of blend-design-system was released, we wouldn’t know if something had changed or been fixed unless we ran into an issue and later discovered that our bindings were outdated.
So I thought of building a tool that could generate the bindings for blend-rescript automatically. Since ReScript is used across Juspay, the idea was to have a package that the whole organization could use. Whenever a new version of blend-design-system is released, we can automate binding generation (for example through GitHub Actions), making it much easier for developers to keep the bindings up to date.
That was the original motivation behind the project.
I.e. npx rescript-bindgen --pkg @mui/material --out generated --report instead of npx @juspay/rescript-bindgen --pkg @mui/material --out generated --report
But is this just for react components? Or ts packages in general? I tried it out with hono, but it just says that all exports are not a react component:
I maintain bindings for base-ui so I tested your tool and here are my findings:
Base UI exports many components through namespace objects and re-exported index.d.ts files. In those cases bindgen currently reports many parts as not-a-component or no-props, and several raw props are emitted as placeholder string values because Base UI uses generic any/unknown surfaces for render props, refs, and collection values (this is not really an issue of bindgen in itself).
Despite these limitations, bindgen helped my agents finish bindings for components I hadn’t covered yet, so great work!
D:\work\rescript>npx @juspay/rescript-bindgen --pkg @webgpu/types
[bindgen] error: ENOENT: no such file or directory, mkdir 'D:\C:\Users\user\AppData\Local\npm-cache\_npx\822d969c2331e1aa\node_modules\@juspay\rescript-bindgen\.bindgen-cache'
I’m on Windows, and it’s likely it gets confused with the drive letters.
Trying this out when the current directory is on the C drive gives a similar error:
C:\_temp>npx @juspay/rescript-bindgen --pkg @webgpu/types --out webgpu-types
[bindgen] error: ENOENT: no such file or directory, mkdir 'C:\C:\Users\user\AppData\Local\npm-cache\_npx\822d969c2331e1aa\node_modules\@juspay\rescript-bindgen\.bindgen-cache'
So it for some reason tries to prepend the current drive letter to the path.
Thanks for reporting this, I was able to reproduce it.
The first error is Windows-specific. The package cache path was being derived from URL.pathname, which turns a Windows file URL into /C:/.... Resolving that as a filesystem path produced the invalid C:\C:\... path. We’ve replaced that logic with Node’s fileURLToPath() and added a Windows regression test. The fix has been pushed to PR #193.
I also tested the generator against @webgpu/types. With the Windows fix, the package installs correctly and resolves to dist/index.d.ts, but generation then stops because @webgpu/types is a global declaration package rather than an importable JavaScript module.
The current generator starts from module exports and emits bindings using constructs such as @module. In contrast, @webgpu/types declares browser globals such as GPUDevice, GPUBuffer, and GPUTexture, with no runtime @webgpu/types module to import.
This is supportable, but it requires a dedicated global-declarations mode: detecting global-only .d.ts files, traversing their top-level interfaces/types/variables, and emitting appropriate @val, @get, @set, @send, and constructor bindings without @module.
So there are two separate items:
The Windows cache-path bug is fixed in PR #193.
Direct generation from global-only packages such as @webgpu/types is not supported yet, but it is technically feasible and would make a good follow-up feature.