Void・Vite+ の爆速ツールを利用しつつ、Cloudflare 依存を減らしたボイラープレートプロジェクトを作った
自分はココ1年くらい、React Router SPA + Hono + Cloudflare Workers といった構成でウェブアプリを作ることが多い。個別にセットアップするのも大変なので、以下のようにボイラープロジェクトを作っていた。
開発サーバとビルドは Vite、テストを書く場合は Vitest を採用していて、これらは VoidZero 社製であり、Vite+ でも Void でも利用されている。しかし Prettier は個人的好みから導入しておらず、ESLint にはオレオレルールを追加したクセの強い構成になっていた。中でも ESLint は実行速度がコードベース量に応じてどんどん遅くなっていく気はする…。
そこで今回は考え方を変え、ボイラープレートを刷新することにした。その名も「Boilerplate : Void Like Web App」。
以前のボイラープレートと同じなのは、「React Router SPA + Hono によるウェブアプリが最低限動くサンプルコードを含んでいる」点。とりあえずトップページが表示され、TailwindCSS と daisyUI によるスタイリングが効いており、ky を使った Fetch 処理、Zustand を使った永続化の動作サンプルも含んでいる。サーバサイドではリクエストに応じて D1 に接続して SQL が実行できることまでをサンプルとして含んでいる。サーバ・クライアントの両方から共用 import できる Zod も入れてあるので、スキーマ検証などを実装し始めるのも容易。
今回のボイラープレートとして考慮したことは以下のとおり。
- Void や Vite+ が内包するツール群 (Vite・Oxfmt・Oxlint・Vitest) と同じモノを利用することで、爆速処理を実現する
- Wrangler CLI は今後、
cfという次世代の Cloudflare CLI に移行するため、先行してcfコマンドを前提とした環境にするwrangler.jsoncよりも型チェックが強力なcloudflare.config.tsに Cloudflare Workers へのデプロイ設定を一元化- (現状の
cfコマンドはまだ構築途中であり、内部で Wrangler も併用しているため、依存パッケージとしてはcfが同時にwranglerもインストールしてくるが、ユーザがコマンドとして利用する機会はないようにする)
- Void は Cloudflare Workers へのデプロイを前提とした密結合なツールチェーンとなっているため、開発者の好みに応じて Cloudflare との依存を剥がしやすい構成とする
- → Cloudflare Workers へのデプロイは
void deployのような密結合したコマンドを使わず、cf deployを素朴に利用することで、パッケージ同士の責任範囲を分離する - Cloudflare を前提とする
void/dbのような密結合なモジュールをあえて使用せず、Wrangler 時代から同様のcontext.env.DBで素朴に扱えるようにする
- → Cloudflare Workers へのデプロイは
- Vite+ はマシングローバルに
vpコマンドをインストールするため、プロジェクトディレクトリ内に閉じて実行できる、ホスト環境を汚さない構成とする- → Vite+ が統合している各ツール (Oxfmt・Oxlint・Vitest など) を個別に npm パッケージとしてローカルインストールし、Vite+ 相当の構成を再現する
- Vite+ は
vite.config.ts1つに Oxfmt・Oxlint の設定などをまとめられるが、個別インストール時はその恩恵を受けられず設定ファイルが別れてしまう点は、止むを得ず許容
ココまでで、Void が目指す「Cloudflare Workers へのデプロイまでを一括管理するツール」という思想は真似できたと思う。Void も内部で Vite+ を利用しており、Vite+ は Vite・Oxfmt・Oxlint・Vitest あたりをオールインワンにまとめたツールだ。このボイラープレートではこれらを個別インストールする形で再現している。
まぁココまでは平たくいえば、「Vite と Vitest はそのまま」「Wrangler を実験的に cf にしてみました」「Prettier は Oxfmt にしました」「ESLint は Oxlint にしました」というだけで、実質的に Vite+ と変わらないじゃん?というのはそのとおり。ただ、Oxfmt・Oxlint の爆速さ加減は本当にビックリしたので、僕個人の独特なコーディングスタイルへのクセに合うか合わないかを無視すれば、機能自体も必要十分、とにかく動作が高速だから AI エージェント利用時も人間が開発してる場合でも、ガンガン繰り返し実行してコードベースを改良していける良いツールだと思った。
もうひとつ付加価値を付けるために考えたのが、「Cloudflare Workers へのデプロイなしでローカルサーバとして起動すること」という入口を作ってみた。
vite previewによるプレビューサーバは一見それっぽいが、ランタイムは Node.js ベースだったりする。それと比べて、Cloudflare Workers 本番環境が内部で使っているのはより軽量なworkerdというランタイムだ- Wrangler CLI は内部的に Miniflare というローカルシミュレータを使っており、Miniflare は内部で
workerdランタイムを使っている - Miniflare がシミュレーションする機能には D1 や R2 なども含まれる。D1 ならローカルに SQLite DB ファイルを用意したりして挙動を再現しており、リモート環境に一切繋がらず動作する
今回はこの Miniflare を使ってローカルサーバを起動するスクリプトを用意した。
ローカルサーバとして起動するメリットは何かというと、Miniflare では本番の Cloudflare Workers には存在する「1リクエストあたりの CPU 実行時間」といった制限に引っかからず、常時起動しっぱなしのサーバとして動かせること。Miniflare サーバに到達するトンネルを用意したりすれば一般公開も可能。永続データはローカルディスク内に保てるし、Cloudflare Workers デプロイ時はできなかったような重たいリクエスト処理も動いてしまうところが良い。おひとりさまウェブアプリなんかだったら、1アカウントあたり10個までしか作れない D1 データベースの個数を削減できる選択肢になるかもしれない。
くどいようだが、書くコードは Hono Context へのバインディングを利用していて、context.env.DB で D1 にアクセスできるといった部分は Wrangler 時代から何も変わらない。同じコードを書いて、Cloudflare Workers にもデプロイできるし、そのビルド済み資材をそのまま利用して Miniflare によるローカルサーバも立てられる。
cf・Wrangler・Miniflare のいずれも Cloudflare 社製であり、ベンダ依存が削れているかといつと微妙かもしれない。Cloudflare が大きな方針転換を行った場合はどうなるか分からないし、cf コマンドがまだ Beta 段階なこともあり、関連パッケージも現状は Beta バージョンでガンガン開発が進んでいる。そのため「workerd を使ったサーバ起動」という狙いに対しては半分マジメに考えたモノだが、「じゃあこの Miniflare サーバを今すぐ本番運用してウェブサービスとして使えるか」というと今はまだ微妙で旨味も少ないかなというところ。前述のとおりおひとりさまウェブアプリなんかの小さな用途には向いているかもね、程度。
個人的には、Oxfmt・Oxlint が内蔵する基本ルールに沿うことで、「Prettier + ESLint が一般化したコーディングスタイル」相当のコードフォーマットに慣れていくことにしようかなと思いつつある。どうせもうこれからは AI に大部分のコードを書かせるんだから、人間にとっての読みやすさを「個人のクセ」をベースに選ぶこともないかな、と思ったり。少なくともチーム開発の機会があれば、Prettier は Oxmt に、ESLint は Oxlint に移行して高速化したいなと思うくらい良いツールだと思った。
Wrangler 自体は機能的に困っていたことはないけど、より AI フレンドリーな設計で作り直す、一本化された cf コマンドというのも気持ちが良いので、ベータながら導入してみた。今後はこういうのがデファクトスタンダードになっていくんだろうか。Cloudflare Workers への密結合はどれくらいウケるんだろうか。コレからが楽しみだ。