Parcelがシンプルで楽すぎた

とある作業をしているときに、npmパッケージを使いたいけどいちいちwebpack.config.jsを書くのは面倒だなーという自堕落が発動したので試しにParcelを使ってみたところすごく楽で良かったので紹介します。

Parcel

f:id:ponday:20180916142614p:plain

公式サイト

WebpackやRollup.js、FuseBoxなんかと同じモジュールバンドラーです。ロゴにも書いてあるとおりビルドの速さとzero configuration = 設定ファイルなしで動作するという点を売りにしているらしいですね。

リリースは2017/12月ごろですが、既にGitHubのスター数は26,000を超えていて、注目度の高いプロジェクトです。

モジュールバンドラーの面倒さ

モジュールバンドラーはすごく便利なツールですが、導入するためにはまずビルドの設定ファイルを準備しなければいけません。例えばWebpackならば、webpack.config.jsには入出力ファイル、拡張子ごとのビルド設定やビルドオプションなどを指定する必要があります。

詳細なオプションを備えているからこそ様々な用途に利用できるという面はもちろんあるのですが、ボイラープレート的な部分も多く、バージョンアップ毎に設定ファイルの書き方がわずかに変更されることも多いので、ライトユースには面倒に感じる場面があります。

対してParcelは面倒な設定部分を極力省略してビルドができるようになっており、この辺りの面倒さを解消してくれています。

触ってみる

ParcelはWebpackなどと同様、npmパッケージとして提供されています。 公式を見るとグローバルにインストールするよう記載がありますがnpxでも動作するので、とりあえず試したい場合はそちらで試しても良いと思います。 今回はnpxを利用していきます。

まずはインストール

npm install --save-dev parcel

Parcelは内部的にBabelを使っているらしいので、Babelのプリセットやプラグインを使う時は.babelrcを用意します。今回は以下のような設定を準備しました。

{
  "presets": [
    ["env", { "modules": false } ]
  ]
}

(ちなみにこの表記、Babelのバージョン7用の@babel/preset-envではなくバージョン6用のbabel-preset-env用のものなのですが、2018/09/17現在ParcelがBabelのバージョン7に対応していないようです。)

ファイルのディレクトリ構成は以下のようにしました。

root
├─ dist
│
├─ src
│   ├─ app.js
│   └─ index.html
│
├─ .babelrc
└─ package.json

モジュールバンドルを試せないことにはどうしようもないので、(わざわざ)RxJSを使います。

npm install --save rxjs

index.htmlは↓

<!DOCTYPE html>
<html lang="ja">
<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>Document</title>
</head>
<body>
    <article id="container"></article>
    <script src="app.js"></script>
</body>
</html>

index.jsは↓

import { map, concatMap, reduce } from 'rxjs/operators';
import { ajax } from 'rxjs/ajax';

window.addEventListener('DOMContentLoaded', () => {
    const url = 'https://accounts.google.com/.well-known/openid-configuration';
    ajax(url).pipe(
        concatMap(({ response }) => Object.entries(response)),
        map(([key, value]) => createElement(key, value)),
        reduce((df, element) => {
            df.appendChild(element);
            return df;
        }, document.createDocumentFragment())
    ).subscribe(df => {
        document.getElementById('container').appendChild(df);
    });
});

function createElement(key, value) {
    const section = document.createElement('section');
    const h2 = document.createElement('h2');
    h2.textContent = key;
    section.appendChild(h2);

    const createContent = Array.isArray(value) ? createContentForArray : createContentForValue;
    section.appendChild(createContent(value));

    return section;
}

function createContentForArray(value) {
    const ul = document.createElement('ul');
    value.forEach(v => {
        const li = document.createElement('li');
        li.textContent = v;
        ul.appendChild(li);
    });
    return ul;
}

function createContentForValue(value) {
    const p = document.createElement('p');
    p.textContent = value;
    return p;
}

それでは、Parcelでビルドをしてみます。

npx parcel src/index.html

上記コマンドを実行して、http://localhost:1234/にアクセスするとビルド結果が確認できます。また、ビルド対象ファイルに変更が走ると自動で再ビルドが走ります。

ちなみにこの時はdevelopmentモードなのでビルドされたソースコードのファイルサイズはそれほど小さくないです。

  • index.html : 4KB
  • app.js : 463KB

productionビルドはparcel buildで実行します。

npx parcel build src/index.html -d dist

ビルド結果は以下の通りで、ファイルサイズが圧縮できていることが分かります。

  • index.html : 317B
  • app.js : 201KB

ほとんどパッケージを追加することもなくビルドできてしまいました。楽!

Tree Shakingについて

上記のビルド結果を見た時、「あれ、ビルドサイズってこんなもんだっけ...?」という疑問が浮かびました。RxJSのajaxなんてあまり使わない関数を使ってしまったので、それのサイズが大きいのかとも思いましたがあの関数自体はそれほど大きなものではないはずです。

念のため、Webpackでもapp.jsをビルドしてみました。

  • app.js : 29KB

あれ、小さい...
というより、想像しているサイズ感はこんな感じです。

調べてみたところ、Parcelのv1.9ではTree Shakingは実験的な機能扱いらしく、--experimental-scope-hoistingオプションをつけてビルドする必要があるそうです。

npx parcel build src/index.html -d dist --experimental-scope-hoisting

上記の結果は以下の通り。

  • app.js : 28KB

小さい!!!
求めている結果はコレですね。まだ実験的な機能扱いなので、早めに本実装されて欲しいところです。

まとめ

  • ParcelはWebpackなどと同じモジュールバンドラー
  • 設定ファイルなしでビルドができる
  • とにかく楽でライトユースにやさしい
  • Tree Shakingは実験的サポート。本実装が待たれる

Parcelは何よりそのシンプルさが魅力的です。

触ってみた印象では、多機能で柔軟なWebpackに対して、シンプルで簡単なParcelと言った具合で、良い住み分け、使い分けができそうなツールという感じでした。

例えば新しいライブラリを使いたいとき、細かくビルド設定を整えるより先にまずは色々と試してみたいといったニーズには良いツールではないでしょうか。