FuseBoxというイケてるmodule bundlerがあるらしい
FuseBoxというmodule bundlerがイケてるらしいので使ってみた github.com
どこらへんがイケてるのか
公式サイトやGitHubのREADMEによると以下のような利点があるらしい。
- 早い
- デフォルトでTypeScriptやJSX記法に対応している
- devサーバー内蔵
- HMR(hot module replacement)がサポートされている
- configが楽
実際に使った感想としては、configファイルが簡素な点、で専用のCLIを使わない点、後述のWebIndexPlugin
などが主な魅力だと思われる。
実際に使ってみた
以降は、実際に書いたコードを提示しながら*1、FuseBoxの使い方を説明する。 「ただbuildするだけ→devサーバーを使う→pluginを入れる」のように後半になるにつれて高度な機能の説明になっていく。
とりあえずbuild
まず適当なtsファイルをbuildしてみる。src/
ディレクトレリを作って、その中にindex.ts
という名前で以下のソースコードを保存する。
// index.ts const msg: string = "Hello World"; console.log(msg);
次にyarn
を初期化して、fuse-box
とtypescript
をインストールする。
$ yarn init $ yarn add -D fuse-box typescript
READMEに習って以下のようなconfigファイルを書き、fuse.js
の名前で保存する。
// fuse.js const { FuseBox } = require("fuse-box"); const fuse = FuseBox.init({ homeDir: "src", output: "dist/$name.js", sourceMaps: true, }); fuse.bundle("app").instructions("> index.ts"); fuse.run();
fuse.bundle()
はソースコードを一つのjsファイルにまとめるコマンドで あり、.instructions()
でビルドの起点になるファイルを指定する(webpack.config.js
でいうところのentry
)。
設定ファイルを記述したら、以下のコマンドを実行する。FuseBoxはwebpack-cliの専用のCLIコマンドは用意されておらず、configファイルをそのまま実行する形式を取っている(npm i -g
したくない派の人喜びそう)。
node fuse
すると、dist/
ディレクトリが作られ、中にビルド結果がapp.js
、app.js.map
の名前で出力される。
ちなみに今回のように、
tsconfig.json
を作らずに実行すると、src/
以下に自動生成してくれる。target
やbaseUrl
などの設定項目はfuse.js
に書いた通りにしてくれるのでちょっと便利。
devサーバーを動かす
fuse.js
にdevサーバーを立てる設定を追記する。
ついでに出力されるjsとsource mapをブラウザ向けのものにしておく。
// fuse.js const { FuseBox } = require("fuse-box"); const fuse = FuseBox.init({ homeDir: "src", output: "dist/$name.js", target: "browser@es5", // ブラウザ向けのコードを吐かせる。ついでにバージョンもes5に指定 sourceMaps: {vender: true}, // ブラウザ向けのsourceMapを出すようにしておく }); // devサーバーの設定 fuse.dev({ port: 8888 // port番号。デフォルトは4444。 root: 'dist' // devサーバーのルートディレクトリ。デフォルトはFuseBox.init()の`output`で指定したディレクトリ。 }); fuse.bundle("app") .instructions(`> index.ts`) .watch() // tsを書き換えるたびに差分コンパイルを行う .hmr(); // hot module replacementを有効にする fuse.run();
ES5で出力するようにしたので、index.ts
もES6以降の機能を使ったやつにしてみる。
enum LogLevel { Info, Error, } type Message = { type: LogLevel; message: string }; const showMessage = (m: Message): string => { switch (m.type) { case LogLevel.Info: return `📢 ${m.message}`; case LogLevel.Error: return `⚠️ ${m.message}`; default: throw new Error("invalid message"); } }; const hello: Message = { type: LogLevel.Info, message: "Hello World" }; document.querySelector("body").innerHTML = showMessage(hello);
最後にdist/index.html
を作成すれば準備完了。
<!-- index.html --> <body> <div id="app"></div> <script src="/app.js"></script> </body>
node fuse
を実行し、localhost:8888にアクセスすると「📢 Hello World」が表示される。
ついでにapp.js
を確認すると、ES6やTypeScript特有の記法がES5に変換されていることが確認できる。
// app.js の抜粋 var LogLevel; // TypeScriptのenumで書いた箇所 (function (LogLevel) { LogLevel[LogLevel["Info"] = 0] = "Info"; LogLevel[LogLevel["Error"] = 1] = "Error"; })(LogLevel || (LogLevel = {})); var showMessage = function (m) { // ES6のarrow関数で書いた箇所 switch (m.type) { case LogLevel.Info: return "\uD83D\uDCE2 " + m.message;
WebIndexPluginでindex.html
を自動生成
前章ではindex.html
を手動でdist/
に置いた。しかし、dist/
は自動出力専用にして、手書きのコードは他のディレクトリで管理したいという要求もあるだろうと思われる(私はそうしたい)。
そこで、WebIndexPlugin
というものを使ってそれを実現する。
まず、src/public/template.html
を作る。
<!-- hemplate.html(Emmetのテンプレをちょっと書き換えただけ) --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>My Page</title> </head> $css<!-- ここにstyleタグが挿入される --> <body> <div id="app"></div> $bundles <!-- ここにscriptタグが挿入される。設定次第で、src=""で別ファイルから読み込むかinlineか選べる --> </body> </html>
fuse.js
にプラグインの設定を追加する。
// fuse.js const { FuseBox, WebIndexPlugin } = require("fuse-box"); // WebIndexPluginもimportする const fuse = FuseBox.init({ homeDir: "src", output: "dist/$name.js", target: "browser@es5", sourceMaps: {vender: true}, plugins: [WebIndexPlugin({ template: "src/public/template.html" })], // これを書き足す });
実行するとdist/
にindex.html
が自動生成される。ちなみに、WebIndexPluin
の引数でtemplate
を指定しない場合、適当なhtmlファイルを自動生成が作られる。便利。
まだまだあるぞ
まだ色々紹介したい機能があるが、長くなったので記事を分けたい。 残っているネタは以下の通り。
AltCSS
を使ってみたJSX
を使ってみたfuse-box/sparky
で静的ファイルのコピーをしてみた
ついでにwebpackあたりとの比較もやってみたい(未着手)。
ただ、7月17日時点で、依存ライブラリに脆弱性が見つかっているようなので本格的に使うなら次のバージョンを待った方がよさそう*2。
*1:清書の段階で修正した箇所もあるためgitのlogと不整合な箇所もある
*2:version 4の準備が進んでいるらしく、hot fixするのかv 4公開のタイミングで直すのかよくわからない。7月17日時点でdependencyのバージョンを上げるissue/PRは出てないのでcontributionチャンスかも?