Repository navigation
Node.js support - #939
Node.js support#939justjake wants to merge 58 commits into
Conversation
e7ed943 to
c7ebd25
Compare
|
Implemented With this, compat/nodejs/ffi.ts is now zero-copy and less complex. |
|
We can port from Is there anything I can do to make this PR easier to review or merge? |
We have a call with the Node.js/Platformatic people that are working on the node ffi things next week. I'll probably have a look at this around that time. For now its mostly a priorities kind of thing. |
What is the current progress on this? |
|
|
@MBearo I published this branch to NPM :) https://www.npmjs.com/package/@jitl/opentui-react Demos: |
Thank you for your efforts! |
|
Closing as upstream has merged Node 26 support. @kommander or @simonklee, are you interested in a more reasonably scoped PR to add FFI support for older node? We've moved several Notion internal tools from Ink to my node22 compat fork, works great! |
|
I think making opentui work in older Node.js versions would be great. It's the only way to ship TUIs on npm that can work for most users without bundling Node.js 26 |
Build the missing OpenTUI 0.4.5 RISC-V renderer with Zig 0.15.2 and install it under /usr/lib/opencode. Use Koffi for native calls and callbacks because Bun's interpreter-only RISC-V runtime rejects its engine FFI with "bun:ffi requires the JIT". Preserve pointer and integer representations, callback ownership and raw memory access. Fix the bundled struct code treating RISC-V pointers as 32-bit. Build Koffi 3.2.1 from source with rv64gc/lp64d for C, C++ and assembly. Its prebuilt addon executes RVV instructions during module initialization, causing "Illegal instruction" while loading the embedded .node module on baseline CPUs. Drop the prebuilt dependency and embed the locally built addon through a static import. Keep its libgcc/libstdc++ dependencies and libopentui.so on disk because Koffi cannot resolve Bun's virtual filesystem. Copy the addon into packages/opencode/node_modules/@opentui/core for Bun's isolated workspace layout. The root node_modules destination is absent, making build() fail with "No such file or directory" after Koffi has compiled successfully. No upstream fix for the prebuilt CPU mismatch was found. Koffi documents source builds, and OpenTUI's earlier Node.js proposal provides precedent for the backend. https://koffi.dev/contribute#build-from-source oven-sh/bun#35246 anomalyco/opentui#939 anomalyco/opentui#1445
Partially addresses #2
This PR brings Node.js compatibility. Bun-only imports and APIs have been replaced by compatibility interface modules, which pick a runtime-appropriate implementation. Most codepaths when running in
bunare the same as before the PR.Node.js differenes
bun:ffi->@opentui/core/compat/ffi: I re-implemented relevantbun:ffiAPIs on top of koffi. There's more copying than in bun, and I'm unsure about correctness, but I think it's a worthwhile start. The upcoming version of koffi offer capabilities much closer to bun, but I'm not sure when the author will release it.bun:test->@opentui/core/compat/test: I know the AGENTS.md says no vitest, but vitest is the most popular Node.js test runner, and its api is very similar to bun:test. We use vite alias to replace bun:test with the compat module based on vitest. Source code still says"bun:test"because "bun:test" only works when your test file directly imports it.globalThis.Worker->@opentui/core/compat/Worker. Node.js doesn't have a global worker, and its worker_threads worker isn't web-compatible without this shim. Warning: there's a bit of weird node module loader shenanigans here to work around node not understandingimport "./foo.js"meansimport "./foo.ts", but it's pretty benign for opentui's use.bun-ffi-structs: oh boy. I ended up having codex vendor this because module-loader tricks won't work once we're bundled, and it wasn't immediately obvious how to do as a build step. Bun runtime still uses upstream, nodejs gets this hack.Bun.spawnSync->@opentui/core/compat/testHelpers. This is only used in test, so I put in own file to avoid bundling it.Bun.*->opentui/core/compat/runtime: Re-implemented Bun.sleep, Bun.writeFile, and installed the typical npm packages for Bun.stringWidth, Bun.stripANSIimport ... with { ... }->new URL(..., import.meta.url). These were pretty mechanically easy to switch to the more universal option.Testing
Test scripts:
test:bun: the oldbun run test|bun run test:js. Unit tests w/ bun:test.test:nodejs:npx vitest run. Maybe I could usebunxbut I want to run under node!test:dist: New type of integration test that tests that thenpm pack'd artifact actually works in a few very simple example projects. Node is not as forgiving as Bun, so this caught a few goofs that don't show up in vitest or bun test. For example, for some reason Bun@1.2 would emit invalid bundles with duplicate exports, detected by this test. Another case was needing to import react-reconciler/something.js w/ the extension. ¯_(ツ)_/¯ node.Thanks
This is a cool project, and thank you in advance for your attention to this matter.
Let me know if you'd prefer I split up the changes. I personally prefer to review 1 big pr rather than 10 small prs without context, but I know its not the common opinion. Lots of those lines of code are duplicate snapshots!