hook-transpiler

Crates.iohook-transpiler
lib.rshook-transpiler
version0.2.5
created_at2025-12-17 17:02:07.205949+00
updated_at2025-12-18 20:41:00.943963+00
descriptionMinimal JSX/TSX transpiler for Relay hooks with dynamic import() rewrite to context.helpers.loadModule and friendly errors
homepagehttps://github.com/clevertree/hook-transpiler
repositoryhttps://github.com/clevertree/hook-transpiler
max_upload_size
id1990730
size124,134
Ari Asulin (clevertree)

documentation

https://docs.rs/hook-transpiler

README

hook-transpiler — Rust Crate for Cross‑Platform JSX/TSX Transpiling and Loading

Last updated: 2025-12-12 10:02 (local)

Purpose

Minimal, reliable transpiler used by Relay clients (web and React Native) to:

  • Transpile JSX/TSX → executable JavaScript
  • Rewrite dynamic import()context.helpers.loadModule(spec) for lazy loading
  • Offer friendly diagnostics suitable for non‑devs
  • Provide fetch helpers (with TLS) for server/native contexts (future work)

This crate is the foundation for client‑side (WASM) and server‑side transpilation. Client‑web uses the WASM build first; server /api/transpile acts as a fallback. RN initially uses the server endpoint, with room to evolve to on‑device transpilation.

Current Status Summary

  • Core Rust crate exists and compiles in workspace
  • Core transforms implemented (TS strip, React classic runtime, import() rewrite)
  • Friendly error types implemented (parse/transform/codegen)
  • Initial unit tests included (JSX basics, dynamic import rewrite, minimal get‑client snippet)
  • Server fallback route implemented and calling this crate: POST /api/transpile
  • Client‑web wired for strict crate‑WASM usage (no SWC/Babel/server fallback while validating) — confirmed working
  • RN integration next: choose native binding approach (JSI) or use server fallback initially
  • A separate isolated WASM crate for the browser is aligned to swc_core v50.x APIs (in progress; usable subset already powering client‑web)

Task List and Status

  1. Core crate (this directory)

    • Create crate and add to workspace
    • Implement parser and transforms (TS strip, React classic w/ pragma, dynamic import() → loader)
    • Error types for parse/transform/codegen with filename and positions
    • Optional CommonJS output flag (for RN if needed)
    • Initial unit tests (basic JSX, dynamic import rewrite, minimal get‑client)
    • Expand unit tests
      • Full template/hooks/client/get-client.jsx transpilation (integration test)
      • TSX inter‑module async imports (A lazily imports B)
      • Negative cases with user‑friendly diagnostics (parse error locations)
  2. Web/WASM build (client‑first)

    • Add build script to emit wasm + JS glue to client‑web public assets (scripts/build-hook-wasm.sh)
    • Client‑web loader that initializes the WASM and exposes globalThis.__hook_transpile_jsx
    • [*] Align and finalize WASM build against swc_core v50.x (isolated browser crate) — in progress
    • Minimal wasm‑exposed API shape: transpile_jsx(source, filename) -> { code, map?, diagnostics? }
    • Document WASM loading behavior and troubleshooting — see Release Validation doc
  3. Server fallback (apps/server)

    • Implement POST /api/transpile endpoint invoking this crate
    • Map errors to friendly diagnostics for clients
    • Add source map support (optional, nice‑to‑have)
    • Integration test covering endpoint with typical inputs
  4. Client‑web integration (apps/client‑web)

    • Strict mode using only crate‑WASM during bring‑up (no SWC/Babel/server fallback)
    • Settings option to allow server fallback or force server‑only
    • [*] Re‑enable server fallback after WASM is green (client‑first strategy) — available via Settings
    • Surface diagnostics in the UI where hooks are rendered
    • E2E test: load get-client.jsx, verify lazy imports route via helpers.loadModule
  5. Client‑React‑Native integration (apps/client‑react‑native)

    • Decide native integration path (modern & efficient):
      • Phase 1: Use server endpoint for reliability on all devices
      • Phase 2: Add native JSI/TurboModule binding to Rust (C++ shim), compile crate as shared lib (.so/.a) via NDK/clang
        • Android: cargo-ndk build, Gradle packaging, Hermes compatibility
        • iOS: Xcode build settings, CocoaPods or Swift Package linking
    • Expose minimal API to JS: transpileJsx(source, filename) -> string | { code }
    • Optionally request CommonJS output until ESM path is uniform
    • Replace Debug tab tests with a single “Client Transpiler Test”
    • Settings toggle to choose Client vs Server transpiler (default: Client)
  6. Fetch handling utilities

    • Server/native: reqwest TLS‑enabled fetch helper (optional feature)
    • Web: WASM build provides a stub delegating to JS fetch
    • Tests for basic fetch scenarios (HTTPS + cert validation)
  7. Documentation

    • This README with tasks and status
    • Crate API examples and diagnostics format
    • Update READMEs: hook‑transpiler, client‑react‑native, client‑web, and project root with transpiler details
    • Release Validation guide: docs/RELEASE_VALIDATION.md
    • Migration notes for client‑web and RN
  8. Server GET fallback behavior (integration & tests)

    • Implement fallback option in server for GET that need transpile (route available)
    • Ensure middleware/handlers invoke transpiler where appropriate
    • Unit/integration tests for fallback path (success and diagnostics)
  9. Release & Distribution

    • Android APK (release) build with RN client using the latest transpiler path
    • Install on device and smoke‑test: load get-client.jsx, verify lazy import() via helpers.loadModule
    • Document release steps and troubleshooting

Build (Server/Native)

This crate is part of the Cargo workspace. To build and run tests:

cargo build -p hook-transpiler
cargo test -p hook-transpiler

Build (Web/WASM)

Artifacts are generated into the web app’s source folder so Vite can bundle them. Run the cross-platform helper from the repo root:

# From repo root (cross-platform)
npm run build:wasm

# Dev cycle (build then start web dev server)
npm run web:dev:wasm

Expected outputs (canonical location under the web app source):

  • apps/client-web/src/wasm/hook_transpiler.js
  • apps/client-web/src/wasm/hook_transpiler_bg.wasm

Client‑web loads these at startup via apps/client-web/src/wasmEntry.ts (shim) and exposes:

globalThis.__hook_transpile_jsx(source: string, filename: string) => string | { code: string }

See also: Release validation steps in docs/RELEASE_VALIDATION.md.

Minimal Public API (Rust)

The crate exposes a Rust API consumed by the server and unit tests:

pub struct TranspileOptions {
    pub filename: Option<String>,
    pub react_dev: bool,
    pub to_commonjs: bool,
    pub pragma: Option<String>,
    pub pragma_frag: Option<String>,
}

pub struct TranspileOutput {
    pub code: String,
    pub map: Option<String>,
}

pub fn transpile(source: &str, opts: TranspileOptions) -> Result<TranspileOutput, TranspileError>;

Error types are user‑facing and include filename and (when available) locations:

  • ParseError { filename, line, col, message }
  • TransformError(filename, source_err)
  • CodegenError(filename, source_err)

Dynamic import() rewrite

All import(spec) calls are rewritten to context.helpers.loadModule(spec) so hooks lazily load peer modules through the runtime’s loader. This is essential for TSX/JSX modules importing each other asynchronously.

Testing

Run unit tests:

cargo test -p hook-transpiler

Planned tests to add:

  • End‑to‑end transpilation for template/hooks/client/get-client.jsx
  • Cross‑file lazy imports (A → import("./B"))
  • Friendly diagnostics on malformed JSX/TSX

Notes

  • Source maps are currently omitted; can be enabled later.
  • RN can initially rely on the server endpoint and optionally request CommonJS output (to_commonjs: true).
  • Web client should attempt WASM first, then server fallback (once strict bring‑up completes).

Publishing (crates.io + npm)

  • The Rust crate is the single source of truth; the npm package compiles its WASM/JS from this crate.
  • Keep versions in Cargo.toml and package.json aligned (e.g., 0.2.x) before releasing.
  • Publish flow:
    1. cargo publish
    2. npm run build (regenerates WASM/JS from the crate) → npm publish
  • No duplicate Rust code is shipped to npm; only the generated WASM/JS artifacts built from this crate.
Commit count: 0

cargo fmt