Skip to content

feat(jsx-compiler): add JSX/TSX compiler package for React SSR#154

Open
YusukeHirao wants to merge 3 commits into
devfrom
feat/jsx-compiler
Open

feat(jsx-compiler): add JSX/TSX compiler package for React SSR#154
YusukeHirao wants to merge 3 commits into
devfrom
feat/jsx-compiler

Conversation

@YusukeHirao

Copy link
Copy Markdown
Member

Summary

  • Add @kamado-io/jsx-compiler, a new @kamado-io/page-compiler compileHooks implementation that renders JSX/TSX components to static HTML via React's renderToStaticMarkup (SSR only — no hydration, no client JS shipped)
  • JSX/TSX is bundled with esbuild and executed via node:module's registerHooks() (a synchronous custom ESM loader), so the compiled component never touches disk
  • Upgrade @kamado-io/script-compiler's esbuild dependency to 0.28.1 (from 0.27.4), which fixes a crash in esbuild's IPC channel when metafile: true is combined with a build failure — hit by jsx-compiler's error-path tests

Design notes

  • Requires Node.js >= 24.13.1 (module.registerHooks())
  • main and layout get independent compiler instances/caches, since page-compiler's CompilerFunction never hands over a file path — see createCompileHooks JSDoc for the resolveDir derivation rationale
  • Layout components always receive main content under a fixed content prop regardless of layouts.contentVariableName
  • See packages/@kamado-io/jsx-compiler/README.md for usage and known constraints (serve-mode memory characteristics, script-compiler extension overlap)

Test plan

  • yarn lint passes
  • yarn build passes
  • yarn test passes (504 tests, including 37 new jsx-compiler tests: unit, cache, and full build-pipeline integration tests)
  • xhigh code-review, qa-engineer, and product-manager review passes completed, findings fixed

Add @kamado-io/jsx-compiler, a page-compiler compileHooks
implementation that renders JSX/TSX components to static HTML via
React's renderToStaticMarkup (no hydration, no client JS). JSX/TSX is
bundled with esbuild and executed through node:module's
registerHooks() (a synchronous custom ESM loader), avoiding any disk
I/O for the compiled component.

Also upgrade @kamado-io/script-compiler's esbuild dependency to
0.28.1: 0.27.4 crashes esbuild's IPC channel when metafile: true is
combined with a build failure, which jsx-compiler's error-path tests
exercise.
@YusukeHirao
YusukeHirao requested a review from yusasa16 as a code owner July 7, 2026 03:48
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

… by filePath

Two incremental-build correctness bugs found in review:

- createCompileHooks() never exposed a cacheDigest, and page-compiler
  omits function values (compileHooks included) from its own digest —
  relying on the config file hash to cover them. That hash can't see
  an esbuild/react/react-dom upgrade or an options change coming from
  another module, so an incremental build could silently keep serving
  stale HTML after either. compileJsx() now exposes cacheDigest()
  (esbuild/React versions + JsxCompilerOptions), copied onto the
  main/layout wrappers and folded into page-compiler's own
  cacheDigest via a new CompilerFunction<M>.cacheDigest hook (mirrors
  kamado/compiler's CustomCompileFunction.cacheDigest).
- compileJsx()'s component cache was keyed by resolveDir+source only,
  so two byte-identical files in the same directory would share a
  compiled module — and since the bundle embeds `//# sourceURL=` for
  whichever file compiled first, a second file's runtime errors would
  misattribute to the first file's path. filePath is now part of the
  cache key.
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

…cacheDigest caveat

Front-load "only relevant if you use a non-default jsxImportSource" so
default (react) users can tell at a glance the item doesn't apply to
them, instead of reading the whole paragraph to find that out.
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

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