BarefootJS

作成 2026-08-17 / markdown

signal ベースの TSX をビルド時にコンパイルして、バックエンドのネイティブなテンプレートを吐くフレームワーク。仮想 DOM も SPA も要求しない。キャッチコピーは “TSX in. Your stack out.”。

バックエンド非依存の IR を挟む

JSX でサーバーレンダリングしようとすると、普通はサーバーが Node.js になる。BarefootJS はコンパイラがバックエンド非依存の IR を作り、アダプタがそれを各言語のテンプレートに変換する構造をとることで、この制約を外している。

JSX → IR (backend-agnostic) → Adapter → Template

Go なら html/template.tmpl、Perl なら Mojolicious の .html.ep、Hono なら生成された .tsx。Node.js 以外のバックエンドでは、配信時に Node.js は一切動かない。

言語 アダプタ
TypeScript HonoAdapter
Go GoTemplateAdapter
Perl MojoliciousAdapter

細粒度のリアクティビティ

SolidJS の影響を受けていて、React との決定的な違いはコンポーネントが一度しか実行されないこと。

const [count, setCount] = createSignal(0)
const doubled = createMemo(() => count() * 2)

createEffect(() => {
  console.log('Count is:', count())
})

setCount(1)

getter が関数呼び出し(count ではなく count())なのがポイントで、ランタイムは各エフェクトがどの signal を読んだかを追跡する。依存配列は要らない。

コンパイラは「どの DOM ノードがどの signal に依存するか」を解析し、ハイドレーション時にそれらを繋ぐコードを生成する。状態が変わると該当の DOM ノードだけが更新され、ツリーの diff は走らない。

MPA に島を足す方向

既存のサーバーレンダリングされたページに、アーキテクチャを変えずにインタラクティブな部品を足す、という立ち位置。比較されているのは次の3つ。

  • jQuery / 素の JS — コンポーネントモデルがなく、規模が大きくなると保つのが難しい
  • SPA フレームワーク — サーバーコンポーネントを使ってもビルド・ルーティング・デプロイのモデルごと引き受けることになる
  • アイランド(Astro, Fresh) — 選択的ハイドレーションは得られるが、サーバーがそのフレームワークである必要がある

BarefootJS はコンパイラがビルドステップとして挟まるだけなので、ルーティングもデプロイも変わらない。"use client" が付いたコンポーネントだけが JavaScript を出す。

IR に対するテスト

renderToTest() はコンポーネントのソース文字列を受け取り、コンパイラの IR に対して構造・signal・イベント・アクセシビリティを検証する。ブラウザを起動しないのでミリ秒で終わる。

const ir = renderToTest(source, 'Counter.tsx')
expect(ir.errors).toEqual([])
expect(ir.signals).toContain('count')

const button = ir.find({ tag: 'button' })
expect(button!.events).toContain('click')

実際の操作や見た目は結局 E2E が要るが、構造の壊れはその手前で捕まえられる。bf CLI が全コマンドで --json を持っていることと合わせて、AI エージェントがソースを読まずにコンポーネントを組み立て・検証できるように設計されている。

出典

#barefootjs #signals #jsx #hono