LoginSignup
4
2

More than 5 years have passed since last update.

Elm 0.19で作るTodoアプリ?(0) docker + webpack で環境構築

Last updated at Posted at 2018-09-16

はじめに

前回、elm0.19の開発環境を構築したので、
elmを試すために、Elm 0.18で作るTodoアプリ(1)を試してみる。
ElmをSPAではなく一部適用にしたいので調整する。

開発環境

  • Windows 10
  • Vagrant 2.1.5
  • Virtualbox 5.2.18
  • Ubuntu 18.04 LTS (Bionic Beaver)
  • Docker version 18.06.0-ce, build 0ffa825
  • docker-compose version 1.22.0, build f46880fe

ファイル構成イメージ

以下のようなファイル構成としたい。

- src
  - card.js # elmを読み込むためのjsファイル
  - html
    - card.html # jsファイルを読み込むhtmlファイル
  - Card
    - Main.elm     # 大元のプログラム
    - Handout.elm  # 見た目を設定するためのファイル

ファイル変更点

環境設定


webpack.config.js全文
webpack.config.js
const path = require("path");
const webpack = require("webpack");
const merge = require("webpack-merge");
const history = require('koa-connect-history-api-fallback');
const CopyWebpackPlugin = require("copy-webpack-plugin");
const HTMLWebpackPlugin = require("html-webpack-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

var MODE = process.env.npm_lifecycle_event === "prod" ? "production" : "development";
const filename = MODE == "production" ? "[name]-[hash].js" : "[name].js";
const files = {index:"./src/index.js", card:"./src/card.js"};

var common = {
    mode: MODE,
    entry: files,
    output: {
        path: path.join(__dirname, "dist"),
        filename: filename
    },
    plugins: [
        new HTMLWebpackPlugin({
            template: "src/index.html",
            inject: "body",
            chunks:['index']
        }),
        new HTMLWebpackPlugin({
          filename: "card.html",
          template: "src/html/card.html",
          chunks:['card']
        })
    ],
    resolve: {
        modules: [path.join(__dirname, "src"), "node_modules"],
        extensions: [".js", ".elm", ".scss", ".png"]
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: "babel-loader"
                }
            },
            {
                test: /\.scss$/,
                exclude: [/elm-stuff/, /node_modules/],
                loaders: ["style-loader", "css-loader", "sass-loader"]
            },
            {
                test: /\.css$/,
                exclude: [/elm-stuff/, /node_modules/],
                loaders: ["style-loader", "css-loader"]
            },
            {
                test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
                exclude: [/elm-stuff/, /node_modules/],
                loader: "url-loader",
                options: {
                    limit: 10000,
                    mimetype: "application/font-woff"
                }
            },
            {
                test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
                exclude: [/elm-stuff/, /node_modules/],
                loader: "file-loader"
            },
            {
                test: /\.(jpe?g|png|gif|svg)$/i,
                loader: "file-loader"
            }
        ]
    },
};

if (MODE === "development") {
    console.log("Building for dev...");
    module.exports = merge(common, {
        plugins: [
            new webpack.NamedModulesPlugin(),
            new webpack.NoEmitOnErrorsPlugin()
        ],
        module: {
            rules: [
                {
                    test: /\.elm$/,
                    exclude: [/elm-stuff/, /node_modules/],
                    use: [
                        { loader: 'elm-hot-webpack-loader' },
                        {
                            loader: "elm-webpack-loader",
                            options: {
                                debug: true,
                                forceWatch: true,
                                // optimize: true // debugと同時には使えない
                            }
                        }
                    ]
                }
            ]
        },
        serve: {
            inline: true,
            stats: "errors-only",
            content: [path.join(__dirname, "src/assets")],
            add: (app, middleware, options) => {
                app.use(history());
            },
            devMiddleware: {
              watch:true, 
              watchOptions:{
                aggregateTimeout: 300,
                poll:1000
              }
            },
            hotClient:{
              host: {
                client: '192.168.50.10',
                server: '0.0.0.0', 
              },
              port:{
                server:3002,
                client: 3002
                },
              allEntries: true
            },
        },
      watch:true,
      watchOptions: {
        ignored: /node_modules/,
        aggregateTimeout: 300,
        poll: 5000
      },
    });
}

if (MODE === "production") {
    console.log("Building for Production...");
    module.exports = merge(common, {
        plugins: [
            new CleanWebpackPlugin(["dist"], {
                root: __dirname,
                exclude: [],
                verbose: true,
                dry: false
            }),
            new CopyWebpackPlugin([
                {
                    from: "src/assets"
                }
            ]),
            new MiniCssExtractPlugin({
                filename: "[name]-[hash].css"
            })
        ],
        module: {
            rules: [
                {
                    test: /\.elm$/,
                    exclude: [/elm-stuff/, /node_modules/],
                    use: [
                        { loader: "elm-webpack-loader", options:{optimize: true} }
                    ]
                },
            {
                test: /\.css$/,
                exclude: [/elm-stuff/, /node_modules/],
                loaders: [MiniCssExtractPlugin.loader, "css-loader"]
            },
            {
                test: /\.scss$/,
                exclude: [/elm-stuff/, /node_modules/],
                loaders: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"]
            }
            ]
        }
    });
}

card.jsのバンドルファイルを作る設定を追加する

var MODE = process.env.npm_lifecycle_event === "prod" ? "production" : "development";
const filename = MODE == "production" ? "[name]-[hash].js" : "[name].js";
const files = {index:"./src/index.js", card:"./src/card.js"};

var common = {
    mode: MODE,
    entry: files,

card.htmlをwebpackに読み込ませるための設定。

    plugins: [
        new HTMLWebpackPlugin({
            template: "src/index.html",
            inject: "body",
            chunks:['index'] // card.jsを適用したくないのでindexのみに
        }),
        new HTMLWebpackPlugin({
          filename: "card.html",
          template: "src/html/card.html",
          inject: false // htmlのほうにjsも記述
        })
    ],

hot-loadをindex.html以外のcard.htmlにも適用する。

            hotClient:{
              allEntries: true
            },

HTMLファイル

マテリアルデザインを適用してみる。 → 失敗。elmと相性が悪い?

card.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-rc.2/css/materialize.min.css" />
    <!--materialize.jsは後から読むと一瞬デザインがちらつくのでhead内で読み込む-->
    <script src='https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-rc.2/js/materialize.min.js'></script>
    <title>ハンドアウト</title>
  </head>
  <h1>ハンドアウト</h1>
  <body>
    <div class="print">
        <div id="cards"></div>
    </div>

  </body>
</html>

jsファイル

id="cards"の要素にelm要素をひもづける。

card.js
'use strict';

require("./styles.scss");

const {Elm} = require('./Card/Main');
const mountNode = document.getElementById('cards');
const app = Elm.Main.init({flags: 6, node: mountNode});

ライブラリ

package.json
{
  "name": "app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "elm-test",
    "start": "npm run dev",
    "dev": "webpack-serve --port 3000",
    "build": "webpack",
    "prod": "webpack -p"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.0.1",
    "@babel/preset-env": "^7.0.0",
    "babel-loader": "^8.0.2",
    "clean-webpack-plugin": "^0.1.19",
    "copy-webpack-plugin": "^4.5.2",
    "css-loader": "^1.0.0",
    "elm": "^0.19.0-bugfix2",
    "elm-format": "^0.8.0",
    "elm-hot-webpack-loader": "^1.0.1",
    "elm-verify-examples": "^3.0.0",
    "elm-webpack-loader": "^5.0.0",
    "file-loader": "^2.0.0",
    "html-webpack-plugin": "^3.2.0",
    "koa-connect-history-api-fallback": "^0.3.1",
    "mini-css-extract-plugin": "^0.4.2",
    "node-sass": "^4.9.3",
    "resolve-url-loader": "^2.3.1",
    "sass-loader": "^7.1.0",
    "style-loader": "^0.23.0",
    "url-loader": "^1.1.1",
    "webpack": "^4.18.0",
    "webpack-cli": "^3.1.0",
    "webpack-merge": "^4.1.4",
    "webpack-serve": "^2.0.2"
  },
  "dependencies": {
    "purecss": "^1.0.0"
  }
}

実行

http://192.168.50.10:3000/card.html (仮想環境のIP:今回設定したポート)にアクセスし、hot loadができていることを確認する。

4
2
0

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
4
2