ゼロランタイムのミニマルな静的サイトジェネレーター『dodai』の開発と JSX First な世界観について

本記事では来たる2/3のKyoto.js #18に備え、年末年始に npm パッケージとして公開した『@potato4d/dodai』という静的サイトジェネレーター(以下SSG)について紹介します。なお、開発にあたって本ブログも @nuxt/content からこの dodai へと移行しています。移行作業の所要時間は 30 分程度でした。

@potato4d/dodai について

Image from Gyazo

ビルドのアーキテクチャ図

ウォッチのアーキテクチャ図

dodai は 1~5 つ程度の固定ページと、Nつの動的なページを持つ Web サイトの構築を想定した静的サイトジェネレーターです。 元々は私が複数の Web サイトの管理で使っていた Node.js 用の Script 群をパッケージとしてまとめたものになります。

技術的には複数ページとまとめて TSX で記述でき、成果物としては React Server Renderer が SSR した結果を HTML として書き出しています。

記事公開時点でのバージョンは v0.5.2 であり、今後破壊的変更が行われる可能性が存在します。

https://github.com/potato4d/dodai

dodai の最も特徴的な部分は、ミニマルであり、ランタイムでJSを使わない仕組みながらも、 JSX First な世界を実現していることとなります。 例として、 npm i @potato4d/dodai; npx dodai init を実行した場合、出力されるファイルは以下のスクリーンショットのもののみとなっています。

Image from Gyazo

見て分かる通りすべてが.tsファイル並びに.tsxファイルで完結しており、かつReactを使用したマークアップの構築が可能となっています。

現状で dodai ができることは、以下のとおりです。

  • JSX ベースでのページの記述
  • 開発サーバーとホットリロード
  • ランタイムJSのない完全に静的なWebサイトとしてのビルド
  • パスベースでのルーティング
    • /path/to 形式
    • /path/items/[items] 形式
  • ページ全体の共通レイアウトや <head> の設定
  • 静的ファイルの成果物への配置
    • /static ディレクトリと /public ディレクトリ

一方で以下は今後も dodai 自体がオピニオンを持つことはなく、利用者側に委ねています。

  • ランタイムJSの取り扱い
  • CSSや画像の最適化など、JavaScriptファイル以外の取り扱い
  • Node.js サーバーを持つ機能群ならびにサーバーサイドレンダリング
  • 他フレームワークとの連携など

もし興味の出たかたは、ぜひこの記事を読む裏で使ってみてください。コマンド2つで準備完了です。

$ npm i @potato4d/dodai
$ npx dodai init
$ npx dodai dev

開発意図と目指す世界観について

dodai を開発するにあたって、まず目指したものは3つ。「最小限の機能と依存関係であること」「TSXが一級市民であること」「データを基に動的にページを生成できること」です。世の中に静的サイトジェネレーターやテンプレートエンジンは多く存在しますが、多くの場合このいずれかが欠損しています。

例えば Next.js や Nuxt.js は高機能で便利ですが、大量の依存関係のもと作られており、かつ特殊なケースを除いてランタイムにJSが露出します。一方でNunjucksのような技術は静的なHTMLを生成できる一方で、TSXは一級市民ではありませんし、Next.jsがNuxt.jsが持つパスベースのルーティングもなく、大量のページを一度に生成することは苦手としています。

理想に一番近いジェネレーターは Astro になりますが、利用していてHydrationまで含めた幅広い技術である分機能過多だと感じること、TSXをデフォルトではサポートしているものの、技術としては DSL が第一に位置づけられている点が気になります。

JSX でページをたくさん作りたい

dodai が実現したい世界は、 Grant や Gulp + ejs で実現していた静的な生成システムを、動的なルーティングにコストをかけることなく、JSXで再現することです。普段アプリケーションを開発するフロントエンドエンジニアにとっては、今や HTML を書くという行為は JSX、ひいては TSX を書くという行為とほぼ同義となっています。

TSX はもはや HTML を記述するデファクト・スタンダードな記法と言えるまで至っており、我々は HTML を記述する際、JavaScript と連携できること、型定義によって実装のミスが事前に防げること、ループなどの処理を TypeScript で記述できることを求めています。

しかしその理想があるにも関わらず、通常のWebアプリケーション開発と遜色ない体験でWebサイトを作るためのソリューションが存在しませんでした。

毎日更新のブログメディア、お知らせだけは一意のURLが払い出されてデータを管理するだけで出力されてほしいが、ホームやお問い合わせ、概要ページなどは完全に固定なオフィシャルWebサイト、APIから取得したデータをもとに情報を組み立てるだけのポータルなどなど、動的に変わりうる複数のURLを持つWebサイトを、TSXで記述する。ただそれだけのことがうまくできない状況です。

私はこのようなシチュエーションにおいてはこれまで代替案として Nuxt.js のジェネレートモードを利用していましたが、解決したい事象に対して Nuxt.js 側の進化の方向性がズレてきたということもあり、Astro などを経た結果 dodai でその世界を実現することとしました。

余計なものを管理したくない

であれば dodai はどのようにあるべきでしょうか?おそらく静的なWebサイトとして作る時点で、以下のような需要が考えられます。

  • 動的な部分のWebサイトの更新はコンテンツデータの更新のみで済ませたい
  • 技術的な改修の頻度は低いことが想定されるため、一年越しの npm install を快適に済ませたい
  • それはそれとして作るときには React + TypeScript + Vite くらいの快適さはほしい

言語化すると「メンテナンスコストは極限まで下げたい」が「アプリケーション開発と同等のベロシティを出したい」といったところでしょうか。かなり贅沢な要求と言えます。通常であれば、開発者体験やアップデートコストを多少犠牲にするか、あるいはSSRサーバーを建てるなどで解決するのではないでしょうか。

ですが dodai は引き算で作ることによって、これらを実現しています。dodai を支えるのは TypeScript と React.js、そして十分に枯れた Node.js のいくつかのパッケージのみ。こと静的Webサイトを生成するという観点に至っては、大きな変革がないと予想される技術たちです。

半年後、一年後、ともすれば三年後に開いても、 npm install のあとにすぐ動くのではないでしょうか。

他方でこれらの技術は私達フロントエンド領域のWebアプリケーションエンジニアの Developer Experience の基盤になっている技術でもあり、これらが担保されている世界では、我々は快適な開発者体験のもと Web サイトを構築することができます。

WebサイトとWebアプリケーション

思い返すと、ここまでの主張はいずれも「Webアプリケーションエンジニア」としてのフロントエンドエンジニアが「Webサイト」を作るシチュエーションでの不満とその解消が焦点なのかもしれません。

近年、少し前に見られた「Web サイトすらも SPA 技術で作りきる」という流れは落ち着きをみせ、WebサイトとWebアプリケーションの技術が切り分けられるような主張が支持されている潮流を感じます。パフォーマンスに関する意識が高まったことはもちろん、そこに対して ISR を皮切りに、アイランドアーキテクチャが提唱されるなど、問題を解消するためのソリューションが生まれたことからでしょうか。

なにはともあれ、それぞれ別方向で進化をし続け、「Webアプリケーションエンジニア」はその領域の中で突き詰め、「Webサイト制作者」は従来通りのやり方をブラッシュアップする時代に逆戻りしたように感じられます。ただ「Webアプリケーションエンジニア」が「シンプルなWebサイト」を作ることは、今もニーズとしては確かに存在し、その領域だけが取り残されていると感じます。

dodai は少しニッチかもしれませんが、その課題に対しての処方箋となれば幸いです。

おわりに

この記事では主に思想やできることを中心に話していきましたが、Kyoto.js では dodai がどのように動いているかを発表しようと思っています。

後日資料を SpeakerDeck に掲載予定なので、ぜひそちらも合わせてご覧ください。

追記: こちらが当日のスライドとなります。

それでは。

Buy Me A Coffee

もしこの記事が役に立ったなら、
こちらから ☕ を一杯支援いただけると喜びます