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



## これまで

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

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

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

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

```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](https://github.com/halfzebra/create-elm-app)を使ったりしますが、この`webpack`の設定が分かりづらくて大嫌いなのです。未熟な自分は、この設定で一日潰れたりしたことも…

## parceljsの導入

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

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

```
# npm install -g parcel-bundler
```

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

```bash
index.html
js/index.js
css/style.css
src/Main.elm
```

```html: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:js/index.js
require('../css/style.css');
const { Elm } = require('../src/Main.elm');

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

```css:css/style.css
body {
  color: white;
  background-color: blue;
}
```

```elm:src/Main.elm
module Main exposing (main)
...
...
```

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

```
# parcel index.html
```

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

チョー便利ですｗ

## postcssとtailwindcssの導入

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

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

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

```
# npm install -D postcss-modules autoprefixer tailwindcss
```

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

```
# npx tailwind init js/tailwind-config.js
```

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

```css:css/style.css
@tailwind preflight;
@tailwind components;
@tailwind utilities;

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

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

```js: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`などのオプションの渡し方が今の所わかりませんｗ。

リポジトリ用意しましたのでお試しください…
[https://github.com/kyasu1/elm-parcel-tailwind](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`なる極悪なエラーが発生してコンパイル自体に失敗するケースがあります。`Array`や`Html.Attribute`を含む`Msg`があると必ずエラーとなるようです。自分自身のコードで使っていなくても、利用しているパッケージの中で似たようなことをしているとエラーとなって詰んでしまいます…

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

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

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

```shell
# npm install -D parcel@1.10.3
```

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

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

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

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