最小のJSX/TSX構成を作ってみる
公開日:
カテゴリ: Javascript
React系のJSX/TSXを使ってみたかったのですが、普通にプロジェクトを作成すると大量の依存関係がインストールされてげんなりするので、なるべく依存関係の少ない代替の環境を探してみました。
なお、あくまで趣味レベルの小規模なプロジェクトを想定しているので、大規模なプロジェクトや本番環境対応などは特に考えていません。そのレベルの開発であれば、素直に公式で推奨されているフレームワークを使うのが良いと思います。
TL;DR
esbuild
が便利- JS / TS / JSX / TSX のバンドル・トランスパイルが可能
- ファイル監視による自動ビルド機能・簡易開発サーバ機能あり
npm
からインストールせずに、バイナリ1個でも動作する
普通にReact+TSX環境を作ってみる
とりあえずReact公式からチュートリアルを探しに行きます。しかし、探せど探せどフレームワークへの案内しか見当たりません。
どうやら2025/02現在ではフレームワークなしのReactは標準から外れたようです。昔はcreate-react-app
があったような気がしますが、現在は公式ページには影も形もありません。フレームワーク必須のライブラリはどうなのかと思わなくもないですが、今回の趣旨とは関係ないのでひとまず置いておきます。
本項はフレームワークを使うことが目的ではないので、単純にReact+TSX環境を作る方法を探します。Deep Diveとして折りたたんだ先で紹介されているvite
でインストールしてみます。
npm create vite@latest my-react-app -- --template react-ts
cd my-react-app
npm i
これ一つで開発環境の構築が完了し、開発に必要/開発体験が向上するツールがセットでインストールされるので、普通に開発する分にはこれで十分でしょう。
さて、依存関係を確認してみます。まずは直接インストールされたパッケージを調べます。
npm ls
[email protected] FILEPATH/my-react-app
+-- @eslint/[email protected]
+-- @types/[email protected]
+-- @types/[email protected]
+-- @vitejs/[email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
`-- [email protected]
次に依存関係の全リストを確認してみます。
npm ls --all
実行結果は長いので畳みます。
[email protected] FILEPATH/my-react-app
+-- @eslint/[email protected]
+-- @types/[email protected]
| `-- @types/[email protected] deduped
+-- @types/[email protected]
| +-- @types/[email protected]
| `-- [email protected]
+-- @vitejs/[email protected]
| +-- @babel/[email protected]
| | +-- @ampproject/[email protected]
| | | +-- @jridgewell/[email protected]
| | | | +-- @jridgewell/[email protected]
| | | | +-- @jridgewell/[email protected]
| | | | `-- @jridgewell/[email protected] deduped
| | | `-- @jridgewell/[email protected]
| | | +-- @jridgewell/[email protected]
| | | `-- @jridgewell/[email protected] deduped
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected]
| | | +-- [email protected] deduped
| | | `-- [email protected] deduped
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected] deduped
| | | +-- @babel/[email protected] deduped
| | | +-- @jridgewell/[email protected] deduped
| | | +-- @jridgewell/[email protected] deduped
| | | `-- [email protected]
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected]
| | | +-- @babel/[email protected]
| | | +-- [email protected]
| | | | +-- [email protected]
| | | | +-- [email protected]
| | | | +-- [email protected]
| | | | `-- [email protected]
| | | | +-- [email protected] deduped
| | | | +-- [email protected]
| | | | `-- [email protected] deduped
| | | +-- [email protected]
| | | | `-- [email protected]
| | | `-- [email protected] deduped
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected] deduped
| | | +-- @babel/[email protected]
| | | | +-- @babel/[email protected] deduped
| | | | `-- @babel/[email protected] deduped
| | | +-- @babel/[email protected] deduped
| | | `-- @babel/[email protected] deduped
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected] deduped
| | | `-- @babel/[email protected] deduped
| | +-- @babel/[email protected]
| | | `-- @babel/[email protected] deduped
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected] deduped
| | | +-- @babel/[email protected] deduped
| | | `-- @babel/[email protected] deduped
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected] deduped
| | | +-- @babel/[email protected] deduped
| | | +-- @babel/[email protected] deduped
| | | +-- @babel/[email protected] deduped
| | | +-- @babel/[email protected] deduped
| | | +-- [email protected] deduped
| | | `-- [email protected]
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected]
| | | `-- @babel/[email protected] deduped
| | +-- [email protected]
| | +-- [email protected] deduped
| | +-- [email protected]
| | +-- [email protected]
| | `-- [email protected]
| +-- @babel/[email protected]
| | +-- @babel/[email protected] deduped
| | `-- @babel/[email protected]
| +-- @babel/[email protected]
| | +-- @babel/[email protected] deduped
| | `-- @babel/[email protected] deduped
| +-- @types/[email protected]
| | +-- @babel/[email protected] deduped
| | +-- @babel/[email protected] deduped
| | +-- @types/[email protected]
| | | `-- @babel/[email protected] deduped
| | +-- @types/[email protected]
| | | +-- @babel/[email protected] deduped
| | | `-- @babel/[email protected] deduped
| | `-- @types/[email protected]
| | `-- @babel/[email protected] deduped
| +-- [email protected]
| `-- [email protected] deduped
+-- [email protected]
| `-- [email protected] deduped
+-- [email protected]
| `-- [email protected] deduped
+-- [email protected]
| +-- @eslint-community/[email protected]
| | +-- [email protected]
| | `-- [email protected] deduped
| +-- @eslint-community/[email protected]
| +-- @eslint/[email protected]
| | +-- @eslint/[email protected]
| | +-- [email protected] deduped
| | `-- [email protected] deduped
| +-- @eslint/[email protected]
| | `-- @types/[email protected] deduped
| +-- @eslint/[email protected]
| | +-- [email protected] deduped
| | +-- [email protected] deduped
| | +-- [email protected] deduped
| | +-- [email protected]
| | +-- [email protected] deduped
| | +-- [email protected]
| | | +-- [email protected]
| | | | `-- [email protected]
| | | `-- [email protected]
| | +-- [email protected]
| | | `-- [email protected]
| | +-- [email protected] deduped
| | `-- [email protected]
| +-- @eslint/[email protected] deduped
| +-- @eslint/[email protected]
| | +-- @eslint/[email protected] deduped
| | `-- [email protected]
| | +-- [email protected] deduped
| | `-- [email protected] deduped
| +-- @humanfs/[email protected]
| | +-- @humanfs/[email protected]
| | `-- @humanwhocodes/[email protected]
| +-- @humanwhocodes/[email protected]
| +-- @humanwhocodes/[email protected]
| +-- @types/[email protected]
| +-- @types/[email protected]
| +-- [email protected]
| | +-- [email protected] deduped
| | +-- [email protected]
| | +-- [email protected]
| | `-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | | `-- [email protected]
| | | `-- [email protected]
| | `-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | | `-- [email protected]
| | `-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | | `-- [email protected] deduped
| | `-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | | `-- [email protected] deduped
| | +-- [email protected]
| | `-- [email protected] deduped
| +-- [email protected]
| | `-- [email protected] deduped
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | `-- [email protected]
| | +-- [email protected]
| | `-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | | `-- [email protected]
| | | `-- [email protected]
| | | `-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| | `-- [email protected] deduped
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | `-- [email protected]
| +-- UNMET OPTIONAL DEPENDENCY jiti@*
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | `-- [email protected]
| | +-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| `-- [email protected]
| +-- [email protected]
| +-- [email protected]
| +-- [email protected] deduped
| +-- [email protected]
| +-- [email protected]
| | `-- [email protected] deduped
| `-- [email protected]
+-- [email protected]
+-- [email protected]
| +-- [email protected]
| | `-- [email protected]
| +-- [email protected] deduped
| `-- [email protected]
| `-- [email protected] deduped
+-- [email protected]
| `-- [email protected] deduped
+-- [email protected]
| +-- @typescript-eslint/[email protected]
| | +-- @eslint-community/[email protected] deduped
| | +-- @typescript-eslint/[email protected] deduped
| | +-- @typescript-eslint/[email protected]
| | | +-- @typescript-eslint/[email protected] deduped
| | | `-- @typescript-eslint/[email protected] deduped
| | +-- @typescript-eslint/[email protected]
| | | +-- @typescript-eslint/[email protected] deduped
| | | +-- @typescript-eslint/[email protected] deduped
| | | +-- [email protected] deduped
| | | +-- [email protected] deduped
| | | +-- [email protected] deduped
| | | `-- [email protected] deduped
| | +-- @typescript-eslint/[email protected] deduped
| | +-- @typescript-eslint/[email protected]
| | | +-- @typescript-eslint/[email protected] deduped
| | | `-- [email protected] deduped
| | +-- [email protected] deduped
| | +-- [email protected]
| | +-- [email protected] deduped
| | +-- [email protected] deduped
| | +-- [email protected]
| | | `-- [email protected] deduped
| | `-- [email protected] deduped
| +-- @typescript-eslint/[email protected]
| | +-- @typescript-eslint/[email protected] deduped
| | +-- @typescript-eslint/[email protected]
| | +-- @typescript-eslint/[email protected]
| | | +-- @typescript-eslint/[email protected] deduped
| | | +-- @typescript-eslint/[email protected] deduped
| | | +-- [email protected] deduped
| | | +-- [email protected]
| | | | +-- @nodelib/[email protected]
| | | | +-- @nodelib/[email protected]
| | | | | +-- @nodelib/[email protected]
| | | | | | +-- @nodelib/[email protected] deduped
| | | | | | `-- [email protected]
| | | | | | `-- [email protected]
| | | | | `-- [email protected]
| | | | | `-- [email protected]
| | | | +-- [email protected]
| | | | | `-- [email protected] deduped
| | | | +-- [email protected]
| | | | `-- [email protected]
| | | | +-- [email protected]
| | | | | `-- [email protected]
| | | | | `-- [email protected]
| | | | | `-- [email protected]
| | | | `-- [email protected]
| | | +-- [email protected] deduped
| | | +-- [email protected]
| | | | `-- [email protected]
| | | | `-- [email protected] deduped
| | | +-- [email protected]
| | | +-- [email protected] deduped
| | | `-- [email protected] deduped
| | +-- @typescript-eslint/[email protected] deduped
| | +-- [email protected] deduped
| | +-- [email protected] deduped
| | `-- [email protected] deduped
| +-- @typescript-eslint/[email protected]
| | +-- @eslint-community/[email protected] deduped
| | +-- @typescript-eslint/[email protected] deduped
| | +-- @typescript-eslint/[email protected] deduped
| | +-- @typescript-eslint/[email protected] deduped
| | +-- [email protected] deduped
| | `-- [email protected] deduped
| +-- [email protected] deduped
| `-- [email protected] deduped
+-- [email protected]
`-- [email protected]
+-- UNMET OPTIONAL DEPENDENCY @types/node@^18.0.0 || ^20.0.0 || >=22.0.0
+-- [email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
| `-- @esbuild/[email protected]
+-- UNMET OPTIONAL DEPENDENCY fsevents@~2.3.3
+-- UNMET OPTIONAL DEPENDENCY jiti@>=1.21.0
+-- UNMET OPTIONAL DEPENDENCY less@*
+-- UNMET OPTIONAL DEPENDENCY lightningcss@^1.21.0
+-- [email protected]
| +-- [email protected]
| +-- [email protected]
| `-- [email protected]
+-- [email protected]
| +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
| +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
| +-- @rollup/[email protected]
| +-- @types/[email protected] deduped
| `-- UNMET OPTIONAL DEPENDENCY fsevents@~2.3.2
+-- UNMET OPTIONAL DEPENDENCY sass-embedded@*
+-- UNMET OPTIONAL DEPENDENCY sass@*
+-- UNMET OPTIONAL DEPENDENCY stylus@*
+-- UNMET OPTIONAL DEPENDENCY sugarss@*
+-- UNMET OPTIONAL DEPENDENCY terser@^5.16.0
+-- UNMET OPTIONAL DEPENDENCY tsx@^4.8.1
`-- UNMET OPTIONAL DEPENDENCY yaml@^2.4.2
UNMET OPTIONAL DEPENDENCY
と出力されている行は、他の環境向けのパッケージなので実際にはインストールされていません(今回はWindows環境での実行結果です)。deduped
と出力されている行は重複するため除外されたものです。
最終的にインストールされたものは以下のようになります。
npm ls --all | findstr /v "UNMET OPTIONAL DEPENDENCY" | findstr /v "deduped"
npm ls --all | grep -v -e "UNMET OPTIONAL DEPENDENCY" -e "deduped"
[email protected] FILEPATH/my-react-app
+-- @eslint/[email protected]
+-- @types/[email protected]
+-- @types/[email protected]
| +-- @types/[email protected]
| `-- [email protected]
+-- @vitejs/[email protected]
| +-- @babel/[email protected]
| | +-- @ampproject/[email protected]
| | | +-- @jridgewell/[email protected]
| | | | +-- @jridgewell/[email protected]
| | | | +-- @jridgewell/[email protected]
| | | `-- @jridgewell/[email protected]
| | | +-- @jridgewell/[email protected]
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected]
| | +-- @babel/[email protected]
| | | `-- [email protected]
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected]
| | | +-- @babel/[email protected]
| | | +-- [email protected]
| | | | +-- [email protected]
| | | | +-- [email protected]
| | | | +-- [email protected]
| | | | `-- [email protected]
| | | | +-- [email protected]
| | | +-- [email protected]
| | | | `-- [email protected]
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected]
| | +-- @babel/[email protected]
| | +-- @babel/[email protected]
| | +-- @babel/[email protected]
| | +-- @babel/[email protected]
| | | `-- [email protected]
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected]
| | +-- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | `-- [email protected]
| +-- @babel/[email protected]
| | `-- @babel/[email protected]
| +-- @babel/[email protected]
| +-- @types/[email protected]
| | +-- @types/[email protected]
| | +-- @types/[email protected]
| | `-- @types/[email protected]
| +-- [email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
| +-- @eslint-community/[email protected]
| | +-- [email protected]
| +-- @eslint-community/[email protected]
| +-- @eslint/[email protected]
| | +-- @eslint/[email protected]
| +-- @eslint/[email protected]
| +-- @eslint/[email protected]
| | +-- [email protected]
| | +-- [email protected]
| | | +-- [email protected]
| | | | `-- [email protected]
| | | `-- [email protected]
| | +-- [email protected]
| | | `-- [email protected]
| | `-- [email protected]
| +-- @eslint/[email protected]
| | `-- [email protected]
| +-- @humanfs/[email protected]
| | +-- @humanfs/[email protected]
| | `-- @humanwhocodes/[email protected]
| +-- @humanwhocodes/[email protected]
| +-- @humanwhocodes/[email protected]
| +-- @types/[email protected]
| +-- @types/[email protected]
| +-- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | `-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | | `-- [email protected]
| | | `-- [email protected]
| | `-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | | `-- [email protected]
| | `-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | `-- [email protected]
| | +-- [email protected]
| | `-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | | `-- [email protected]
| | | `-- [email protected]
| | | `-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | `-- [email protected]
| | +-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| `-- [email protected]
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| `-- [email protected]
+-- [email protected]
+-- [email protected]
| +-- [email protected]
| | `-- [email protected]
| `-- [email protected]
+-- [email protected]
+-- [email protected]
| +-- @typescript-eslint/[email protected]
| | +-- @typescript-eslint/[email protected]
| | +-- @typescript-eslint/[email protected]
| | +-- @typescript-eslint/[email protected]
| | +-- [email protected]
| | +-- [email protected]
| +-- @typescript-eslint/[email protected]
| | +-- @typescript-eslint/[email protected]
| | +-- @typescript-eslint/[email protected]
| | | +-- [email protected]
| | | | +-- @nodelib/[email protected]
| | | | +-- @nodelib/[email protected]
| | | | | +-- @nodelib/[email protected]
| | | | | | `-- [email protected]
| | | | | | `-- [email protected]
| | | | | `-- [email protected]
| | | | | `-- [email protected]
| | | | +-- [email protected]
| | | | +-- [email protected]
| | | | `-- [email protected]
| | | | +-- [email protected]
| | | | | `-- [email protected]
| | | | | `-- [email protected]
| | | | | `-- [email protected]
| | | | `-- [email protected]
| | | +-- [email protected]
| | | | `-- [email protected]
| | | +-- [email protected]
| +-- @typescript-eslint/[email protected]
+-- [email protected]
`-- [email protected]
+-- [email protected]
| `-- @esbuild/[email protected]
+-- [email protected]
| +-- [email protected]
| +-- [email protected]
| `-- [email protected]
+-- [email protected]
| +-- @rollup/[email protected]
次に、ファイル数やフォルダサイズを見ていきます。
ファイル数
dir node_modules /A-D /S /B | find /c /v ""
ls ./node_modules -F | grep -v / | wc -l
5541
フォルダサイズ
(New-Object -ComObject Scripting.FileSystemObject).GetFolder('node_modules').size
du --bytes ./node_modules
78565449
ファイル数もファイルサイズもなかなかの数値です(これでもwebpackよりは格段にマシですが)。SSDから別ディスクへのコピーや移動はお茶を二口くらい飲めそうですし、HDDにインストールした場合ファイル操作はあまりやりたくないですね。パッケージのダウンロードは圧縮されているとはいえ、モバイルネットワークでnpm i
はちょっと避けたくなります。
ちょっとしたアプリを作るのに75MiB使うとすると、npmではプロジェクトごとにインストールすることになるので、14個も作れば1GiBになります。実際には各アプリで使うモジュールを更にインストールするので、ベースとなるサイズは少しでも落としておきたいところです。
しっかり開発する場合はそれでいい(というかそうするべき)のですが、ちょっと触りたい場合にこれではげんなりしますね。・・・それぐらい小さいアプリなら、Reactなんか使わずに生JS書けよってツッコミが入りそうですがw
余談ですが、npm create
した後のnpmのキャッシュは230MB程になっていました。
代替を探す
現在の依存関係を知ったところで、代わりになるものを探していきます。
Reactは、本体は依存無し、react-dom
も少量の依存関係なのでそのまま使っても良さそうですが、preact
の方が軽量らしいので、今回はそちらを試してみます。
TSXは、TSX
→TypeScript
→JavaScript
とトランスパイルしていくことになるので、これらをトランスパイルするツールを探します。
TypeScript
→JavaScript
は公式ツールがありますが、TSX
やJSX
のトランスパイルは、何らかのバンドルツールしか情報が見当たりませんでした。
tsc
TypeScript標準のトランスパイラでnpm i typescript
でインストールするとついてきます。主にTypeScript
→JavaScript
にトランスパイルするのに使われています。TSX
→TypeScript
もできるらしいのですが、あまり情報がありません。
webpack
言わずと知れたバンドルツールでcreate-react-app
で作成したプロジェクトで使われていました。すでに開発は事実上終了していて、後継のTurboPackへ移行が進められているようです。
TSX
やTypeScript
のトランスパイルは追加Loaderのインストールが必要です。
開発・本番として必要な機能が一通り揃っており、プラグインでの機能拡張も非常に豊富ですが、とにかくビルドが重いです。
babel
こちらも言わずと知れた、本番向けに自動でフォールバックコードを適用するツールです。TSX
やTypeScript
のトランスパイルもプリセットを追加インストールすることで可能です。
esbuild
Go製のバンドルツールです。ネイティブで動作し、Goの得意な並列処理というのもあってか非常に高速です。Viteも開発用ビルドの内部では、これが使われています。
TSX
やTypeScript
もネイティブでトランスパイルします。minify機能もありますが、rollup
よりはサイズが大きくなる傾向にあるようです。
ちなみにnpm
でインストールしなくても、esbuildの実行バイナリ1個があればコマンドラインで単独で動かすことが可能です。また、実行ファイル自体がnpmパッケージに含まれているので、node-gyp
などでのビルドも必要ありません。
環境作成
ということで、今回の目的にはesbuild
が近そうなので、esbuild
で環境を作成してみます。
npm i preact
npm i -D esbuild
esbuildは実行時のオプションで色々設定することも可能ですが、ベースとなる設定はtsconfig.json
で指定します。tsconfig.json
は自動で読み取ってくれます。
以下のサンプルは、とりあえず必要最小限のみ記載しています。
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "preact"
}
}
esbuild
ではwatch機能や簡易サーバ機能も内蔵されています。Viteでは本番向けはesbuild
ではなくrollup
を使いますが、今回はesbuild
で本番用もビルドします。
開発向け
import { context } from "esbuild";
const ctx = await context({
entryPoints: ["./src/main.tsx"],
outdir: "./dest",
bundle: true,
minify: true
});
await ctx.watch(); // watch機能
await ctx.serve({ servedir: "./dest" }); // 簡易サーバ機能
※コマンドラインで実行する場合
esbuild ./src/main.tsx --outdir=dest --bundle --minify --watch --servedir=dest
本番向け
import { build } from "esbuild";
await build({
entryPoints: ["./src/main.tsx"],
outdir: "./dest",
bundle: true,
minify: true
});
特に設定を変えない場合は、実質的にcontext
を使うかbuild
を使うかの違いしかありません。
※コマンドラインで実行する場合
esbuild ./src/main.tsx --outdir=dest --bundle --minify
なお、いずれの場合もHTMLファイルは自動でoutDir
へコピーされないので、ビルドスクリプトにコピーするスクリプトを組むか、手動でコピーします。
動作確認
適当にコードを書いて動作確認します。
ちなみにesbuild
はhtmlファイルのminify機能はありません。CSSファイルはTSX
内でimport
すると自動で1つにまとめたCSSファイルとして出力する機能があります(<link>
タグや<style>
タグを自動挿入する機能は無し)。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Example</title>
<script src="./main.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
import { render } from "preact";
const App = () => {
return (<p>Hello, preact!</p>);
};
document.addEventListener("DOMContentLoaded", () => {
const root = document.getElementById("root");
if (root) {
render(<App />, root);
}
});
Hello, preact! が画面に表示されれば成功です。
おまけ
今回動かしたソースは以下のURLにあります。
https://github.com/Raintensity/JunkBox/tree/main/template/project/preact-minimal
カテゴリ: Javascript