
昔ながらのWebサイトのためのParcelレシピ
Parcel はよりシンプルな小規模Webサイトの制作に向いている、というのは先日の記事で紹介した通りです。それはひとえにParcel自体のシンプルさ故にでありますが、そのメリットは融通の効かなさというデメリットと表裏一体であり、自在にコントロールする事の難しさでもあります。 本稿はそんなParcelをうまいこと調理するレシピを徒然なるままに書き連ねていく回です。
「昔ながらのWebサイト」とは
タイトルで言う「昔ながらのWebサイト」とは、例えばオーソドックスなコーポレートサイトや非ECなショップサイトなどの、HTMLとCSSとJSから構成されたシンプルで静的なWebサイトを指します。
そういったWebサイトを運用するのはクライアントである事も多く、我々制作者の手を離れる事も少なくありません。そのため、納品するデータはクライアントにとってわかりやすく、編集が容易な状態でなければなりません。あるいはそれは、WordPressのようなCMSかもしれません。
この要件をParcelを用いてどのようにしてバンドルするか、考えていきたいと思います。
ノーコントロールで出力して仕様・挙動を確かめる
まずはParcelの挙動を知るため、デフォルトでどのように出力されるのかを確認してみましょう。
src/
├── about/
│ └── index.html
├── assets/
│ ├── css/
│ │ └── style.scss
│ ├── images/
│ │ ├── 01.jpg
│ │ ├── 02.jpg
│ │ ├── 03.jpg
│ │ └── 04.jpg
│ └── js/
│ └── main.js
└── index.html
上のようなファイル構成で、なんのオプションも渡さずに出力をしてみます。 index.html
では、 main.js
style.scss
01-04.jpg
が読み込まれています。
$ parcel build 'src/**/*.html'
デフォルトの出力先ディレクトリは dist
なので、ビルドの完了と同時に dist
ディレクトリに出力結果が保存されます。さあ、中身はどうなっているでしょうか。じゃじゃん。
dist/
├── 01.be0ed084.jpg
├── 02.8270bec8.jpg
├── 03.3864a649.jpg
├── 04.0df788c6.jpg
├── about/
│ └── index.html
├── index.html
├── main.17b58856.js
├── main.17b58856.js.map
├── style.2cdee1ef.css
└── style.2cdee1ef.css.map
なんか期待してたのと違う。
ご覧のように、ディレクトリ階層構造は概ね平坦にされ、読み込まれたファイルにはハッシュが挿入され冗長になっています。実際このデータを渡された側は、どうしたものかと困惑してしまいますね。
出力をコントロールする
上の結果を踏まえて、ビルドの出力結果のディレクトリ構造ならびにファイル名を上手にコントロールしてみたいと思います。
- 元のファイル名のまま出力したい
- 元の階層構造を維持したい
この課題を解決するために、Parcelのエントリーポイントについて学びましょう。
エントリーポイントの特徴

ここで言うエントリーポイントとは、すなわち parcel
コマンドに渡した引数のパスであり、上のコマンドで言うところの 'src/**/*.html'
の部分を指します。指定されたファイルは起点となり、読み込まれているリソースを走査して自動的にバンドルしていきます。そして重要なのは、 エントリーポイントはパスとファイル名を維持したまま書き出される という事です。
- エントリーポイントとして指定されたファイルは、元のファイル名と階層位置を維持して出力される
- 非エントリーポイントとして読み込まれたファイルは自動的にバンドルされ、ファイル名にハッシュが挿入され、出力ディレクトリのルートに出力される
つまり、元のパスとファイル名を維持したいファイルはエントリーポイントとして指定してしまえば良いのです。
エントリーポイントの指定方法
エントリーポイントの引数は、内部で glob
に渡されるので glob
の記法で柔軟に制御する事ができます。主な記法はREADMEを参考にしていただくとして、特に良く使うのが次の記法です。
- ワイルドカード :
*.html
or**/*.html
- 括弧によるパターン展開 :
file.(html|css|js)
- 大括弧によるパターン展開 :
src/{'foo','bar/baz'}
→ この例ではsrc/foo
とsrc/bar/baz
に展開される
これらの記法を駆使して課題を解決してみましょう。今一度ファイル構成を確認しておきます。
src/
├── about/
│ └── index.html
├── assets/
│ ├── css/
│ │ └── style.scss
│ ├── images/
│ │ ├── 01.jpg
│ │ ├── 02.jpg
│ │ ├── 03.jpg
│ │ └── 04.jpg
│ └── js/
│ └── main.js
└── index.html
エントリーポイントに指定したいファイルをタイプごとに分類すると :
src/**/*.html
→src
以下(サブディレクトリ内含む)のHTMLファイルを全て指定src/assets/css/*.scss
→src/assets/css
直下のscssファイルのみ指定src/assets/images/**/*.(jpg|png)
→src/assets/images
以下(サブディレクトリ内含む)のJPG,PNGファイルを全て指定src/assets/js/*.js
→src/assets/js
直下のjsファイルのみ指定
こういった具合になります。これらを共通項 src/
で括って大括弧でパターン分けすれば、引数の出来上がりですね。カンマの前後にスペースなどが入っていると動作しないのでご注意ください。
$ parcel build src/{'**/*.html','assets/css/*.scss','assets/images/**/*.(jpg|png)','assets/js/*.js'}
これでビルドを実行するとどうなるか。こうなります。じゃん。
dist/
├── about/
│ └── index.html
├── assets/
│ ├── css/
│ │ ├── style.css
│ │ └── style.css.map
│ ├── images/
│ │ ├── 01.jpg
│ │ ├── 02.jpg
│ │ ├── 03.jpg
│ │ └── 04.jpg
│ └── js/
│ ├── main.js
│ └── main.js.map
└── index.html
エクセレント!憂いなく納品できそうです 🎉
エントリーポイントを制する者がParcelを制す
と言うと少し過言に聞こえるかもしれませんが、実際のところ、Parcelをコントロールする方法はエントリーポイントの指定とあまり多くないオプション渡しくらいしかありません。 プログラマティックに制御することも出来るようですが、それをするならばParcelを選択しないでしょう。
エントリーポイントを理解したならば、それはすなわち「Parcelチョットワカル」と言って良いと思います。
serve | watch | build を使い分ける
ここまで parcel build
というコマンドを使用してきましたが、 Parcel には build
の他に、 serve
と watch
という2つのコマンドが用意されています。簡単に紹介しましょう。
-
serve
最も頻繁に使われるコマンドです。コマンド省略時のデフォルトでもあるので、ただparcel
と実行した場合はparcel serve
と等価になります。serve
コマンドはソースファイルを監視し、変更を検知した場合に自動的に再ビルドを行いながら、開発用の簡易Webサーバーを起動します。制作中はこれを起動しながらコーディングを進めていくことになるでしょう。 -
watch
ファイルを監視して変更時に自動再ビルドをします。serve
と異なり、開発用サーバーは起動しません。
開発用サーバーが別で立ち上がっていて、ファイルの監視とリビルドだけ行いたい場合に使います。 -
build
ビルドをします。監視もサーバー起動もしません。ただビルドをします。 主に納品用のファイルを出力する為に使います。
出力ディレクトリは分けよう
全てのコマンドにおいて、出力ディレクトリの初期値は共通して dist
とされていますが、 serve|watch
と build
では基本的に出力結果が異なる為、いずれかを実行するたびに dist
の中身が書き換わって差分が生まれてしまいます。生まれた差分が検証の邪魔をする事もあるでしょう。
したがって、 serve|watch
と build
では出力ディレクトリは分けるのが賢明だと考えます。
$ parcel serve src/**/*.html
$ parcel watch src/**/*.html
$ parcel build src/**/*.html -d build
出力ディレクトリは -d
または --out-dir
オプションで設定可能なので、 build
する場合だけ別のディレクトリに書き換えてやると良いでしょう。
parcel serve
のエントリーポイントはシンプルに
出力をコントロールする では出力結果を制御するために細かくエントリーポイントを指定しましたが、これは build
用であって、出力結果の体裁が問題にならない serve
では極めて単純な設定 'src/**/*.html'
で大抵事足ります。 serve
で dist
の中にどのようにファイルが出力されようが、それはどうでもよいことです。つまり、頑張るところではありません。
$ parcel serve 'src/**/*.html'
また、詳細な発生条件はわからないのですが、 serve
コマンドで複雑なエントリーポイントを渡すと、開発サーバーが正常にディレクトリインデックスを返さない不具合が起きる事があるので、それを回避するためというのも理由のひとつです。
不具合の症状としては、デフォルトではhttp://localhost:1234
で開発サーバーが起動するのですが、ブラウザでアクセスしても 404
が返ってきてしまいます。なお、 /index.html
でアクセスするとちゃんと表示されます。
どうしても複雑に指定したい時は
とはいえ、 serve
に glob
記法でゴリゴリ書いたエントリーポイントを渡したい場面というのはあると思います。例えば、動的に読み込まれる画像リソースを扱いたかったり。そんな時は serve
ではなく watch
を使用し、別途 http-server 等でWebサーバーを立ち上げるのも一つの手段です。
"scripts": {
"dev": "parcel watch 'src/**/*.(html|png|jpg)' & http-server dist -p 1234"
},
WordPress のテーマ制作にParcelを活用する
これもまた頻出の課題。WordPressの独自テーマで使うJS/CSSファイルをビルドしたい。例えばテーマ名を my-awesome-theme
としてこのように構築するとします。
public/
└── wordpress/
└── wp-content/
└── themes/
└── my-awesome-theme/
├── functions.php
├── index.php
├── style.css
└── assets/
├── js/
│ └── main.js
├── css/
│ └── style.css
└── images/
├── 01.png
├── 02.png
└── 03.png
はじめに確認しておきたいのは、Parcel でビルドするのは assets
の中身だけです。 Parcel はエントリーポイントとしてPHPファイルを扱うことができませんし、テーマルートの style.css
はテーマの基本情報を記述するためのファイルなので、完全に静的であるべきです。したがって、それ以外の、Parcelを通したい物をまとめて assets
につっこんでしまおうという考えです。
この assets
の中身をビルドするためのソースは、いままで通り src
に設置するとしましょう。
src/
└── assets/
├── js/
│ └── main.js
├── css/
│ └── style.scss
└── images/
├── 01.png
├── 02.png
└── 03.png
あとはこれまでと同様にエントリーポイントを指定して出力先をテーマルートにしてあげればOK。WordPressをどのように起動するかについてはここでは触れませんが(DockerなりXamppなり仮想マシンなりお好みで)Parcelの開発サーバーはここでは完全に無駄なので、 serve
ではなく watch
を使います。
$ parcel watch src/assets/{'css/*.scss','images/**/*.(jpg|png)','js/*.js'} -d public/wordpress/wp-content/themes/my-awesome-theme/assets
これでファイルを監視しつつ自動ビルドを行ってくれます。ここで注意事項が2つ。
- この場合、CSSのホットリロードは効きません。CSSのホットリロードが働くのは、エントリーポイントにHTMLが含まれる場合のみなので、諦めてマニュアルリロードしましょう。(JSのホットリロードはちゃんと機能します)
watch
コマンドは、出力ファイルをミニファイする事ができません。最終的にJS/CSSファイルをミニファイしたい場合は、同じ引数でbuild
コマンドを実行しましょう。
納品用のHTMLファイルを整形する
HTMLの更新がクライアントのタスクになる場合、「ミニファイされたHTMLでは管理がつらすぎるので、整形されたHTMLを渡して欲しい」というケースもあると思います。
Parcelの機能では賄えないので記事の内容からやや外れてしまいますが、せっかくなので。
そんな時は、 build
した後で整形するためのスクリプトを実行してあげましょう。
まず整形のための js-beautify
と、ファイルを走査するための glob
をインストールします。
$ npm i js-beautify glob -D
次に整形スクリプトを用意しましょう。書き方には色々あると思いますが、今回は process.argv
で引数を受け取って処理する形をとりました。
// tasks/beautify.js
const path = require('path')
const fs = require('fs')
const glob = require('glob')
const beautify = require('js-beautify')
if (process.argv.length > 2) {
glob.sync(path.join('./', process.argv[2]))
.forEach(filePath => {
fs.readFile(filePath, { encoding: 'utf-8' }, (error, data) => {
if (!error) {
fs.writeFileSync(filePath, beautify.html(data, { indent_size: 2 }), 'utf-8')
}
})
})
}
使い方は、コマンドの引数としてパスを渡すだけです。glob記法を使う場合には、クォーテーションで囲ってあげましょう。
$ node tasks/beautify.js 'build/**/*.html'
こんな具合で build で出力されたHTMLに対して実行してやることで、改行・インデントした読みやすいHTMLに整形してくれます。 build する度に実行しなおすのは手間なので、 package.json
の npm script に parcel build
と一緒に登録しておくと良いでしょう。
"scripts": {
"build": "parcel build 'src/**/*.html' -d build && npm run beautify:html",
"beautify:html": "node tasks/beautify.js 'build/**/*.html'"
},
まとめ
非常にまとまりなく、ただ連連とレシピを紹介する回となってしまいましたが、是非とも知っていただきたいのは、Parcelの設定の9割方はエントリーポイントが握っているということです。 繰り返しになりますが、エントリーポイントを制すればParcelを制したも同然でしょう。
快適なビルドライフをお送りいただけますように! 🙏🏻