Skip to content

Add react + rescript example - #493

Closed
tom-sherman wants to merge 2 commits into
oven-sh:mainfrom
tom-sherman:react-rescript-example
Closed

tom-sherman wants to merge 2 commits into
oven-sh:mainfrom
tom-sherman:react-rescript-example

Conversation

@tom-sherman

Copy link
Copy Markdown
Contributor

"web-vitals": "^1.0.1"
},
"scripts": {
"build": "rescript",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is using rescript, which launches a Node.js process and not a Bun.js runtime process

@Jarred-Sumner

Copy link
Copy Markdown
Collaborator

Thank you for the example. Unfortunately, this example is running in Node.js and not in Bun.

bun run [script] runs your scripts, in this case rescript

if your script runs node, you're still using node.

Just, with bun run, instead of waiting ~160ms for npm run, you wait ~6ms for bun run

if you pass bun run a JavaScript or TypeScript file, then it runs in bun.js.

I'm closing this, but if rescript does run in bun successfully (keep in mind that bun will honor #! shebangs for node), then please definitely do reopen it

@tom-sherman

Copy link
Copy Markdown
Contributor Author

Noted! 99% of ReScript is a native binary (that's why it's so fast) it's just that the binary is bootstrapped using a node script: https://github.com/rescript-lang/rescript-compiler/blob/e30c806e184762703be6b59ba4a9d1a611756382/rescript

I've raised an issue here: rescript-lang/rescript#5530

Jarred-Sumner added a commit that referenced this pull request Aug 23, 2026
…-parsing every module on a second VM

A cold run with module.enableCompileCache() used to spend its exit
re-parsing every missed module on a dedicated "BunCompileCache" thread with
its own JSC VM and eagerly compiling every function in it.

The SourceProvider now gets the live top-level UnlinkedCodeBlock from JSC
right after CodeCache generates it (oven-sh/WebKit#493) and keeps a Weak to
it on the VM's client data. The block is encoded once with encodeCodeBlock():
either right before Bun's GC drops unlinked function code
(JSC__VM__runGC → deleteAllUnlinkedCodeBlocks), or at persist time (exit,
module.flushCompileCache(), a --watch reload), whichever comes first. That
writes the top-level block plus the code blocks of every function that ran
up to that point; functions that never ran stay lazy stubs. Nothing is
rooted: a block the GC already collected is simply not cached this run.
The file format is unchanged: one CachedBytecode, decoded by the same path
as before.

Workers encode what they compiled in their own on_exit and hand the bytes
to the entry; the main thread's exit writes them. Entries carry an id so
bytecode for a module that was rewritten and re-required in the same
process is dropped in favor of the new version's.

--watch reloads now go through the JS thread when the cache is enabled,
like they already did with --watch-kill-signal listeners, so the modules
get encoded before execve; the grace timer still forces the reload if the
JS thread is stuck, persisting only what was already encoded.

Pins WebKit to the PR preview build until #493 lands.
Jarred-Sumner added a commit that referenced this pull request Aug 23, 2026
…-parsing every module on a second VM

A cold run with module.enableCompileCache() used to spend its exit
re-parsing every missed module on a dedicated "BunCompileCache" thread with
its own JSC VM and eagerly compiling every function in it.

The SourceProvider now gets the live top-level UnlinkedCodeBlock from JSC
right after CodeCache generates it (oven-sh/WebKit#493) and keeps a Weak to
it on the VM's client data. The block is encoded once with encodeCodeBlock():
either right before Bun's GC drops unlinked function code
(JSC__VM__runGC → deleteAllUnlinkedCodeBlocks), or at persist time (exit,
module.flushCompileCache(), a --watch reload), whichever comes first. That
writes the top-level block plus the code blocks of every function that ran
up to that point; functions that never ran stay lazy stubs. Nothing is
rooted: a block the GC already collected is simply not cached this run.
The file format is unchanged: one CachedBytecode, decoded by the same path
as before.

Workers encode what they compiled in their own on_exit and hand the bytes
to the entry; the main thread's exit writes them. Entries carry an id so
bytecode for a module that was rewritten and re-required in the same
process is dropped in favor of the new version's.

--watch reloads now go through the JS thread when the cache is enabled,
like they already did with --watch-kill-signal listeners, so the modules
get encoded before execve; the grace timer still forces the reload if the
JS thread is stuck, persisting only what was already encoded.

Pins WebKit to the PR preview build until #493 lands.
sosukesuzuki pushed a commit that referenced this pull request Aug 25, 2026
### Problem
- Bun's WebKit pin `aea1f010b6` is 412 upstream commits behind
`8c4fd56347`, 90 of them in JavaScriptCore, WTF or bmalloc.
oven-sh/WebKit#503 merges that range into the fork.
- Upstream removed the `@newPromiseCapability` private builtin
(`38027ff0ec`). Six call sites in Bun's bundled modules use it, so
`node:events`, `node:util`, `node:dgram` and the HTTP server fail to
load (`Private symbol not found: newPromiseCapability`).
- #40263 and oven-sh/WebKit#501 were a parallel attempt at `55d9d9007f`,
one WebCore-only commit ahead. Both are closed in favor of this pair.

### Fix
- oven-sh/WebKit#503 is merged. `WEBKIT_VERSION` is
`cb61607f1a4bae79d7701965062634dee9efb349`, its merge commit on the
fork's main (release
`autobuild-cb61607f1a4bae79d7701965062634dee9efb349`, 42 tarballs). That
commit is the preview build this PR was tested against (`d2654c3b`) plus
oven-sh/WebKit `a0a80b2276` (an optional depth bound on
`recursivelyGenerateUnlinkedCodeBlockForProgram/ForModuleProgram`).
- The six call sites create their promise with `$newPromise()` and
settle it with `$resolvePromise` / `$rejectPromise`, or the
`...WithFirstResolvingFunctionCallCheck` variants where a second settle
is possible. `builtins.d.ts` follows.
- `EncodeURIComponent.cpp` includes `<wtf/HexNumber.h>` itself (upstream
`314133b7a6` no longer does).
- Verified: `test/js/bun/jsc/webkit-upgrade-8c4fd56347.test.ts` pins
four JavaScript-visible engine changes that fail at the current pin. The
events, util and dgram tests cover the ported settlement paths.

### Background
- Bun links a prebuilt JavaScriptCore from oven-sh/WebKit releases.
`scripts/build/deps/webkit.ts` names the release tag.
- Built-in modules (`src/js/`) go through JavaScriptCore's builtin
compiler. A `$name` call becomes the private name `@name`, which has to
exist in the engine.
- `$newPromise` creates a pending promise. `$resolvePromise` /
`$rejectPromise` settle it and require it to be pending. The
`...WithFirstResolvingFunctionCallCheck` variants ignore calls after the
first, like a Promise executor's functions.

<details><summary>Notes</summary>

- Duplicate resolution: oven-sh/WebKit#501 and #503 have the same
structure (main at `62f427b86f`, then #488's head `d0fae3b3c9`, then
upstream/main) and the same `CachedTypes.cpp` resolution (the two files
differ in comments and an unused alias). WTF and bmalloc are identical.
The only upstream difference is WebCore's `55d9d9007f`
(`MediaElementAudioSourceNode` use-after-free), which the JSCOnly port
does not compile. #503 was kept because this PR's CI run was green
(Build #104490). The test file `webkit-upgrade-8c4fd56347.test.ts` is
carried over from #40263.
- oven-sh/WebKit#488 (upstream `baf4a9a7ec0b`) stopped merging after the
fork's bytecode cache rework (#490, #493, #494, #497). The per-commit
review of the upstream range (API and ABI changes, behavior changes,
performance) and the conflict resolutions are in oven-sh/WebKit#503. The
new conflict in this round is `CachedTypes.cpp`: the fork's new code
block record layout against upstream moving the global-only fields
(`features`, `lineCount`, source URL directives) to
`UnlinkedGlobalCodeBlock` and deleting `m_jumpTargets`.
- The ported call sites (the changes of #40054, carried over):
`node:events` (`once`), `node:util` (`aborted`), `node:dgram`
(`Symbol.asyncDispose`) and the HTTP server (CONNECT, Upgrade, the
per-request completion promise). `builtins.d.ts` declares `$newPromise`,
`$resolvePromiseWithFirstResolvingFunctionCallCheck` and
`$rejectPromiseWithFirstResolvingFunctionCallCheck` and drops
`$newPromiseCapability`. `@newPromise` is a bytecode intrinsic and
`@resolvePromise` / `@rejectPromise` are link-time constants, so they
exist in every engine build. `util.aborted` registers and unregisters
its `FinalizationRegistry` entry with the same token (the promise).
- Behavior changes in the upstream range that are visible from
JavaScript: `Promise.try` follows the updated spec (`PromiseResolve`
instead of `NewPromiseCapability`); the module map no longer caches
fetch failures, so a second `import()` of a specifier whose load failed
re-runs Bun's module loader instead of rejecting with the cached error;
`Uint8Array.prototype.setFromBase64` on a zero-length target returns `{
read: 0, written: 0 }` without validating the input;
`WebAssembly.Module.imports()/exports()` descriptors drop the
non-standard `type` field; re-exported imported Wasm globals and tags
keep object identity; a DFG `++`/`--` on an `int32` that overflows with
an unused result now deoptimizes instead of wrapping (`7711916200`). The
first, third, fourth and last of these are pinned by
`test/js/bun/jsc/webkit-upgrade-8c4fd56347.test.ts`.
- Performance changes of note: `SymbolTableEntry` no longer allocates a
`WatchpointSet` per watched variable until the DFG watches it
(`cea233cede`); `Object.assign` with several sources clones the first
one through `objectCloneFast` (`96ca975b2a`); `JSON.parse` allocates
arrays once at their final size; `TypedArray.prototype.sort()` without a
comparator uses a radix sort for 2/4/8-byte element types; `Map`/`Set`
`forEach` is inlined in the DFG and FTL; `RegExp` cells shrink from 96
to 80 bytes; `UnlinkedFunctionCodeBlock` shrinks from 216 to 192 bytes.
- `src/jsc/bindings/NodeVMSyntheticModule.cpp` calls
`SymbolTable::set(NoLockingNecessary, ...)`. After `cea233cede` only the
locked overload exists. `NoLockingNecessary` converts to a
`ConcurrentJSLocker`, so the call compiles unchanged.
- The upstream change to Linux thread scheduling (per-QOS
`sched_setattr` on every WTF thread, `SCHED_BATCH` compiler threads on
hosts with 4 or fewer cores) is gated off for Bun in the fork: Bun's
threads keep inheriting the process scheduling attributes.
- Suites run on a local debug + ASAN build against the merged WebKit
(`bun run build:local`): `test/js/bun/jsc`, `bun/jsc-stress` (116/116),
`node/events`, `node/util`, `node/dgram`, `node/vm`, `node/module`,
`bun/resolve`, `node/worker_threads`, `bun/wasm`, `web/url`,
`web/atomics`, `node/http/node-http-connect`, `node/async_hooks`,
`node/string_decoder`, `bundler/bundler_compile`,
`bundler/bun-build-api`: 3,548 pass. The failures are 5 s timeouts under
debug + ASAN, this machine's IPv6 multicast `ENODEV`, and one test that
fails the same way at the current pin. `bun build --bytecode` output
from that build loads and runs. A debug + ASAN build against the
`autobuild-preview-pr-503-311eab61` prebuilt runs
`test/js/bun/jsc/webkit-upgrade-8c4fd56347.test.ts`,
`test/js/bun/jsc/webkit-upgrade-3722912f.test.ts`,
`node/events/event-emitter.test.ts` and
`node/util/test-aborted.test.ts`: 106 pass.
- Every push to oven-sh/WebKit#503 produces a new preview tag
(`autobuild-preview-pr-503-<first 8 of the head sha>`), and this PR's
`WEBKIT_VERSION` follows it. CI lanes that fetch the prebuilt fail on
the download until that tag's Actions run has published the release.
- Rebase over #40201: Bun main moved its pin to the fork's
`c148a12dd82b` and calls the bytecode APIs that release added
(`EncoderStringTable`, persistent payloads). The `311eab61` preview
predates them, so the branch could not rebase until oven-sh/WebKit#503
merged the fork's main (head `d2654c3b`, 0 commits behind). The rebase
itself conflicted only on the `WEBKIT_VERSION` line. A debug + ASAN
build against `autobuild-preview-pr-503-d2654c3b` passes
`webkit-upgrade-8c4fd56347.test.ts`,
`node/events/event-emitter.test.ts`, `node/util/test-aborted.test.ts`,
`node/dgram` (except the IPv6 multicast `ENODEV` of this machine),
`node/http/node-http-connect.test.ts`, `web/atomics`, `web/url`,
`node/string_decoder` and `test/js/bun/jsc`. The compiled-executable
bytecode paths of #40201 work against it: the aliasing run keeps 12 MB
of instruction streams out of anonymous memory and 45 internal modules
load from embedded bytecode. The failures on this machine are the DOMJIT
hot loops and two `bun-build-compile` tests that exceed their timeouts
under debug + ASAN (the compile alone takes 5 to 47 s here), and the
nested `node-http-connect.node.mts` run that takes 5.0 s against a 5 s
limit.
- The `$newPromiseCapability` call in `src/node-fallbacks/events.js`
(the browser polyfill, not a JSC builtin) is a pre-existing bug and was
reported separately by #40054.
</details>

<!-- robobun:evidence:begin -->

---

**no test proof** · iteration 2 · platform-specific test(s) that do not
run on this machine, deferring to CI, which covers all platforms:
test/js/node/dgram/node-dgram.test.js

<!-- robobun:evidence:end -->
social4hyq pushed a commit to social4hyq/ohos-bun that referenced this pull request Sep 5, 2026
oven-sh#494 merged)

12-commit upstream delta, clean merge, no regressions found against
the 1.4.1 merge's documented failure classes. bun-webkit bumped to
2e2aa2290f first (PR oven-sh#493), then bun to 1.4.2 (PR oven-sh#494), both via
GitHub CI (local build container's sshd was unreachable). Real-device
smoke + watch.test.ts + the execve/pthread_create repro all clean on
1.4.2; the intermittent SIGSEGV from the previous EAGAIN-fix
verification did not reproduce this run, but it's inherently flaky
(3/8 historically) so stays open, not closed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019hTeaNqnsU8Qqm1eacUy2g
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants