Edited at

Parcel でまとめる Elm と css と js のススメ


これまで

例えば、elm make src/Main.elmとするとindex.htmlを生成してくれて、これをブラウザで開けば結果が表示されます。

また、elm reactorとすれば、開発用のサーバーを立ち上げてくれて、そこからElmのコードの実行が出来ます。


CssフレームワークやJavaScriptライブラリの導入

ただ、やっぱportsflagsを利用したり、何らかのjsライブラリやcssフレームワークを取り入れたくなってきます。これを実現するには、エントリーポイントとなるindex.htmlを用意し、


index.html

<!DOCTYPE HTML>

<html>
<head>
<meta charset="UTF-8">
<title>Main</title>
<link rel="stylesheet" href="whatever-you-want.css">
<script src="main.js"></script>
</head>

<body>
<div id="elm"></div>
<script>
var app = Elm.Main.init({
node: document.getElementById('elm')
});
</script>
</body>
</html>


コマンドラインからelm make src/Main.elm --output main.jsとして、ElmのソースコードをJavaScriptにトランスパイルします。

そして、このindex.htmlをブラウザで直接開いたり、適当なWebサーバーを立ち上げてからブラウザでアクセスします。

しかし、毎回これを手動でやるのも面倒、もし間違えて--outputオプションを忘れるとindex.htmlが上書きされてしまうという悲惨な事故に繋がりかねないので、なんらかのバンドラーを使うのが一般的です。

メジャーなのはwebpack、自分で設定を用意したり、webpackをベースにしたcreate-elm-appを使ったりしますが、このwebpackの設定が分かりづらくて大嫌いなのです。未熟な自分は、この設定で一日潰れたりしたことも…


parceljsの導入

そこで登場するのが、最近になってElmをサポートするようになった、parceljsです。設定不要で高速が謳い文句のバンドラーです。

グローバルにインストールしてみます。

# npm install -g parcel-bundler

適当なディレクトリを作成し、以下の様な構成で、外部jscssの読み込みを考慮し、下記のようにファイルを用意します。

index.html

js/index.js
css/style.css
src/Main.elm


index.html

<!DOCTYPE HTML>

<html>
<head>
<meta charset="UTF-8">
<title>Main</title>
</head>

<body>
<div id="elm"></div>
<script src="js/index.js"></script>
</body>
</html>



js/index.js

require('../css/style.css');

const { Elm } = require('../src/Main.elm');

var app = Elm.Main.init({
node: document.getElementById('elm')
});



css/style.css

body {

color: white;
background-color: blue;
}


src/Main.elm

module Main exposing (main)

...
...

これらの用意が出来たら、

# parcel index.html

とすると、開発用のサーバーがポート1234で立ち上がります。もし任意のファイルに変更があれば、検知して自動でコンパイルし直してくれます。

チョー便利ですw


postcssとtailwindcssの導入

自分はcssが苦手なので、何らかのフレームワークを使うことが多いのですが、最近はtailwindcssにハマっています。いわゆるAtomic Cssと呼ばれる系のライブラリですが、自分にはとても合っているようです。

CDNからデフォルトのcssファイルを読み込んでも良いのですが、やはりちょっとはカスタマイズをしたくなってくるので、parceljsのツールチェインに組み込んでしまうと、とても楽が出来ます。

ただ、parceljsも流石にこの辺までは設定なしでは面倒を見てくれないので、必要なツール群一式を用意します。

# npm install -D postcss-modules autoprefixer tailwindcss

tailwindcssのカスタマイズ用のファイルが必要になりますので次のように生成します。

# npx tailwind init js/tailwind-config.js

必要に応じてこのコンフィグファイルに修正を加えます。また、cssファイルを以下のように変更します。


css/style.css

@tailwind preflight;

@tailwind components;
@tailwind utilities;

/* 生cssが必要ならこの辺に記述か@importする */
body {
color: white;
background-color: blue;
}


そして、postcssの設定を用意してtailwindcssを実行し、さらにその結果をautoprefixerで後処理をします。


postcss.config.js

module.exports = {

plugins: [
require('tailwindcss')('./js/tailwind-config.js'),
require('autoprefixer')
]
}

これで準備完了、

# parcel index.html

とすると、めでたくtailwindcssのクラスが反映された環境で開発用サーバーが立ち上がります。Elmコード内で必要なクラス名を埋め込むと自動的に反映されます。


最後に

正直なところparceljsがどのように動作するのか全くわかりませんし調べてもいません。

ファイルの変更の検知に失敗したり、それに伴うホットリローディングも失敗する時があります。

また、Elmのコンパイルでフォーマット的なエラー(カッコを忘れたとか)があった場合に、parcelを再起動しないとホットリローディングしてくれない時があります。

Elmコンパイラへの--debug--optimizeなどのオプションの渡し方が今の所わかりませんw。

リポジトリ用意しましたのでお試しください…

https://github.com/kyasu1/elm-parcel-tailwind


2019/02/11

最新版のparcelではElmでもHMRに対応して更に便利になりました。同時に--debugオプションが自動的に追加されるようになって、Time Travelling Debuggerが有効になります。

ただし、ここに大きな罠がありまして、--debugをつけると、謎のMap.!: given key is not an element in the mapなる極悪なエラーが発生してコンパイル自体に失敗するケースがあります。ArrayHtml.Attributeを含むMsgがあると必ずエラーとなるようです。自分自身のコードで使っていなくても、利用しているパッケージの中で似たようなことをしているとエラーとなって詰んでしまいます…

elm/compilerの方での根本的な解決が望ましいですが、--debugオプションを付けなければコンパイルに成功するので、もしこのエラーに遭遇したらelm makeでオプションなしでコンパイルしてください。

だと身も蓋もないですので、parcelの方に聞いたら、以前のバージョンをインストールすればとりあえず大丈夫でしょ( https://github.com/parcel-bundler/parcel/pull/2626#issuecomment-462212763 )と教えていただきました。

自分のプロジェクトフォルダで

# npm install -D parcel@1.10.3

とすれば無事に解決です。このバージョンではHMRが組み込まれていないので、

# npm install -D parcel-plugin-elm-hot

としてインストールします。

以上で引き続きparcelで楽ちん開発が続けられます!