LoginSignup
2
1

More than 1 year has passed since last update.

webpackでElm + Sassな開発環境を作る〜ついでにHMR〜

Last updated at Posted at 2020-01-22

はじめに

Elmで書かれたアプリケーションをSassで装飾したい。
ついでにホットリロード(以下HMR)な環境にしたい。
あると思います。

開発環境

macOS Catalina 10.15.2
docker desktop 2.1.0.5
Elm 0.19.1

なお、以下の作業は全てElmがインストールされたDockerコンテナ上で行っております。

プロジェクトの初期化

まずは開発環境上でElmがインストールされてるかを確認してみます。

$ elm --help
Hi, thank you for trying out Elm 0.19.1. I hope you like it!

バッチリですね。
プロジェクトディレクトリを作成していきましょう。

$ mkdir qiita && cd qiita
$ yarn init
$ elm init
$ ls
elm.json  package.json  src

ここまででプロジェクトの初期化は完了です!

依存ライブラリのインストール

yarn addで必要なライブラリをインストールしていきます。

webpack関連

$ yarn add webpack webpack-cli webpack-dev-server -D

Babel関連

$ yarn add @babel/core @babel/preset-env babel-loader -D

必須ではないのですが、
エントリーポイントのJavaScriptにES6を使いたかったためインストールしています。
ついでに.babelrcファイルも追加しておきましょう。

$ touch .babelrc
.babelrc
{
  "presets": [
    "@babel/preset-env"
  ]
}

Sass関連

$ yarn add node-sass sass-loader css-loader style-loader -D

Elm関連

$ yarn add elm-webpack-loader elm-hot-webpack-loader -D

webpackで.elmファイルを処理するためにはローダーを用意する必要があります。
今回はHMRを利用したいのでそのためのローダーも追加でインストールしています。

elm-webpack-loader
elm-hot-webpack-loader

依存ライブラリのインストールは以上となります!
最後にpackage.jsonにコマンドを登録しておきましょう。

package.json
{
  "name": "qiita",
〜中略〜
  "scripts": {
    "build": "webpack --mode production",
    "dev": "webpack-dev-server"
  },
〜後略〜
}

webpack.config.jsの記述

下準備が整ったところで設定ファイルを書いていきましょう!

$ touch webpack.config.js

まずは雛形を書いてみます。

webpack.config.js
const path = require('path');
const webpack = require('webpack');

module.exports = (env, arg) => {
  return {
    plugins: [],
    entry: './src/index.js',
    output: {
      filename: 'main.js',
      path: path.resolve(__dirname),
    },
    devServer: {},
    module: {
      rules: []
    }
  }
};

ここから各種設定項目を埋めてまいります。

プラグインの追加

plugins: [
  new webpack.HotModuleReplacementPlugin()
],

今回はHMRを利用したいのでプラグインを追加しています。

開発サーバの設定

devServer: {
  hot: true,
  stats: 'errors-only',
  port: 3000,
  host: '0.0.0.0',
  historyApiFallback: {
    rewrites: [
      { from: /.*/, to: '/' }
    ]
  }
},

devServer.hottrueにすることでHMRを有効化しています。
また、SPA開発時はdevServer.historyApiFallbackオプションを設定することで、
常にSPAのエントリーポイントとなるファイルを返すことが可能です。
上記の例では常にindex.htmlを返すようにしています。

ローダーの設定

module: {
  rules: [
    {
      test: /\.elm$/,
      exclude: [/elm-stuff/, /node_modules/],
      use: [
        { loader: 'elm-hot-webpack-loader' },
        {
          loader: 'elm-webpack-loader',
          options: {
            cwd: __dirname,
            optimize: (argv.mode == 'production') ? true : false
            debug: (argv.mode == 'development') ? true : false,
          }
        }
      ]
    },
    {
      test: /\.s[ac]ss$/i,
      use: [
        'style-loader',
        'css-loader',
        'sass-loader',
      ],
    },
    {
      test: /\.js$/,
      exclude: [/elm-stuff/, /node_modules/],
      loader: 'babel-loader'
    }
  ]
}

.elmファイルを処理する際、webpackのmodeによって
Elmのビルド時にデバッグモードか最適化モードかが切り替わるよう設定しています。
また、ローダーの適用順ですが、elm-hot-webpack-loaderのドキュメントに従い、
「elm-webpack-loader」-> 「elm-hot-webpack-loader」となるようにします。

まとめ

設定は以上となります!

最後に出来上がったコードを再掲します。

webpack.config.js
const path = require('path');
const webpack = require('webpack');

module.exports = (env, argv) => {

  return {
    plugins: [
      new webpack.HotModuleReplacementPlugin()
    ],
    entry: './src/index.js',
    output: {
      filename: 'main.js',
      path: path.resolve(__dirname),
    },
    devServer: {
      hot: true,
      stats: 'errors-only',
      port: 3000,
      host: '0.0.0.0',
      historyApiFallback: {
        rewrites: [
          { from: /.*/, to: '/' }
        ]
      }
    },
    module: {
      rules: [
        {
          test: /\.elm$/,
          exclude: [/elm-stuff/, /node_modules/],
          use: [
            { loader: 'elm-hot-webpack-loader' },
            {
              loader: 'elm-webpack-loader',
              options: {
                cwd: __dirname,
                optimize: (argv.mode == 'production') ? true : false,
                debug: (argv.mode == 'development') ? true : false,
              }
            }
          ]
        },
        {
          test: /\.s[ac]ss$/i,
          use: [
            'style-loader',
            'css-loader',
            'sass-loader',
          ],
        },
        {
          test: /\.js$/,
          exclude: [/elm-stuff/, /node_modules/],
          loader: 'babel-loader'
        }
      ]
    }
  }
};

エントリーポイントとindex.htmlの作成

まずはindex.htmlファイルを作成しましょう。

$ touch index.html
index.html
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport"
        content="width=device-width, initial-scale=1">
</head>

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

</html>

続いてエントリーポイントとなるJSファイルです。

$ touch ./src/index.js
src/index.js;
import { Elm } from './Main.elm'
import './sass/main.scss'

Elm.Main.init({
  node: document.getElementById('elm')
})

Main.elmとmain.scssの作成

$ touch src/Main.elm
src/Main.elm
module Main exposing (main)

import Html exposing (..)
import Html.Attributes exposing (..)


main =
    div [ class "container" ]
        [ h1 [] [ text "hello elm!!" ] ]

最後にmain.scssファイルを作成します。

$ mkdir src/sass
$ touch src/sass/main.scss
src/sass/main.scss

.container {
  display: flex;
  justify-content: center;
  align-items: center;

  h1 {
    font-size: 48px;
    color: #428A09;
  }
}

お疲れ様でした!
全ての初期ファイルがこれで完成しました!

開発サーバの起動

それでは早速開発サーバを起動してみましょう。

$ yarn dev

http://localhost:3000/

Main.elmmain.scssを編集すると自動的に反映される様子が分かるかと思います!

さいごに

ここまでで、プロジェクトディレクトリ下の構成は以下のようになります。

.
├── src
│   ├── Main.elm
│   ├── index.js
│   └── sass
│       └── main.scss
├── node_modules
├── elm_stuff
├── .babelrc
├── elm.json
├── index.html
├── package.json
├── webpack.config.js
└── yarn.lock

ファイル数が少ないためsrcディレクトリ下に.elm.js.scssもまとめて入れてしまっていますが、
実際のプロジェクトではディレクトリ構成はもう少し工夫する必要はありそうです。

ともあれ、私達はElmとSassが使えてHMRな環境を手に入れることができました!
今年はElmも勉強してきたいです。

2
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1