Skip to content

Prototype standalone Oxc HTML template compiler for stock ReScript output - #36

Draft
zth wants to merge 9 commits into
mainfrom
feat/html-compiler
Draft

zth wants to merge 9 commits into
mainfrom
feat/html-compiler

Conversation

@zth

@zth zth commented Sep 22, 2026 •

Copy link
Copy Markdown
Owner

Ordinary ResX JSX now becomes HTML template writers through a standalone Rust/Oxc transform of stock ReScript JavaScript. ReScript remains responsible for type checking and JavaScript generation. The server bundler applies native transform edits in memory and emits the application bundle. App source and stock compiler output are untouched; there is no second ReScript compilation or generated ReScript project.

Application integration

App.res → stock ReScript → App.js → ResX Oxc bundler plugin → app.mjs

In the demo, bun run build:res:templates runs stock ReScript once and bundles to .resx/optimized/app.mjs. bun run start:templates runs that bundle from the existing demo directory. /catalog uses normal ReScript JSX with no annotations or alternate component API. The demo optimizes 20 template regions across eight modules.

The Bun plugin now owns compiler startup/shutdown, so build scripts use plugins: [resxBunPlugin()] without manually managing a worker. Unchanged modules remain available to other plugins. Vite/Rollup production integration also has a real build-and-run test, including reuse after a source edit.

The native tool is in compiler/oxc/src/main.rs, the shared adapter is compiler/oxc-transform.mjs, and the tested Bun integration is compiler/oxc-bun.mjs. See compiler/README.md for setup and generated-code examples.

What makes it faster

Nested native JSX with literal attributes becomes coalesced static HTML plus dynamic child slots. This removes native element/props allocations and repeated tag/attribute serialization. Writers remain private; dynamic expressions evaluate once, in order, and dynamic children use the existing renderer for escaping, context, and async behavior.

The transform checks scope-resolved imports against approved ResX runtime paths. Mutation, namespace escapes, direct eval, unsupported props, and void elements conservatively fall back. Static text is coalesced during planning without a separate merge pass. Generated names use decoded Oxc symbol/reference information, covering escaped identifiers and nested bindings; writer arguments cannot shadow the runtime import. A persistent Oxc process handles modules; content-addressed caching avoids re-parsing unchanged code. MagicString applies edits and supplies source maps. The in-memory cache retains only the latest source version per module.

The implementation stays local: approximately 470 lines of Rust and 160 lines of adapter/build integration. The design borrows scope-safe generated names from Solid’s SSR template transform and incremental string coalescing from Vue’s SSR code generation.

Measurements

Bun 1.3.14, Linux x64, AMD RYZEN AI MAX+ 395. Three fresh-process baseline/optimized pairs, run serially after builds/tests finished. Each process warms up for at least one second per workload and takes nine approximately 60 ms samples. Values below are medians of process medians. Both modes evaluate the same stock fixture module; the optimized mode transforms it before timing. Output is verified before and after each workload. These are fixture measurements, not a claim that every application becomes this much faster.

100-card workload Baseline Oxc Throughput gain
Render an existing tree 24.23 µs 8.52 µs 2.84×
Construct and render 34.12 µs 14.29 µs 2.39×
Construct and render via async API 34.50 µs 14.60 µs 2.36×
ResX request → construct/render → response text 49.35 µs 21.64 µs 2.28×

Reproduce with bun bench/compiler.mjs and bun bench/compiler.mjs --optimized. The request workload includes Request/Response and handler work, but no network transport. The simplification produces byte-identical JavaScript for this measured fixture, preserving these results.

Build comparison uses identical Bun production bundling with/without the transform, three serial pairs per cache condition. Stock ReScript is already up to date; “uncached” refers to the ResX transform cache, not a clean ReScript build. Native tool compilation is excluded.

Demo bundling Baseline Oxc Added time
Empty transform cache 5.33 ms 21.27 ms 15.94 ms
Reused transform cache 5.60 ms 10.94 ms 5.34 ms

Whole build medians, including stock compilation and plugin initialization: 127.71 → 193.34 ms in the empty-cache sample and 138.33 → 141.84 ms with cache. Stock compilation varies substantially between these short runs; the bundling measurements isolate plugin overhead. Bundling now includes automatic compiler initialization. No raw measurement snapshots are committed.

Validation

  • 130 framework tests pass against baseline bundles and again against optimized bundles.
  • 28 compiler/plugin tests (including 60 generated attribute/child combinations compared with ordinary rendering) cover generated code, public exports, HTML parity, scope shadowing, mutations/escapes, direct eval, fallback props, evaluation order, Unicode/surrogates, map source content, ESM matching, parse-error recovery, content cache reuse/invalidation, generated-name collisions, closed-worker behavior, Bun loader interoperability, and actual Bun/Vite build reuse after source edits.
  • Actual baseline/optimized demo servers return byte-identical /catalog, /start, 404 HTML, and catalog CSS, with matching statuses.
  • Dedicated GitHub CI builds the native tool, runs both test modes, and verifies the demo.

Remaining scope

This is an opt-in prototype, not a release-ready compiler package. Native binary distribution/install and watch-mode integration still need work. Bun production bundling and Vite/Rollup library builds are tested. Development-server watch/HMR remains unverified. The demo uses stock CommonJS output; the Vite integration test bundles an ESM caller with the real CommonJS framework runtime. Current analysis is local JavaScript analysis: no CMT instruction manifest or cross-module component inlining yet. Typed ReScript analysis can be added later for optimizations that need stronger proofs. Runtime monkey-patching elsewhere in an application is outside the optimization contract. Streaming was not a performance target.

@zth zth changed the title Prototype transparent JSX compilation to HTML templates Prototype standalone ResX compiler using stock ReScript artifacts Sep 22, 2026
@zth zth changed the title Prototype standalone ResX compiler using stock ReScript artifacts Prototype standalone Oxc HTML template compiler for stock ReScript output Sep 22, 2026
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.

1 participant