elmチュートリアルのアプリケーションの開始を試してみた時に、つまづいたりしたのでメモ。
動作環境
- windows10
- vagrant2.0.2
- virtualbox5.1.26
- ubuntu-16.04
- Docker version 17.09.0-ce, build 19e2cf6
- docker-compose version 1.18.0, build 8dd22a9
動かした環境のpackage.json
チュートリアルと異なるのはwebpackのバージョンが4になっていること。
これで結構ハマった。
{
"name": "app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "nf start",
"client": "webpack-dev-server",
"build": "webpack",
"api": "node api.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"ace-css": "^1.1.0",
"css-loader": "^0.28.10",
"elm": "^0.18.0",
"elm-webpack-loader": "^4.4.0",
"file-loader": "^1.1.10",
"font-awesome": "^4.7.0",
"sanitize.css": "^5.0.0",
"style-loader": "^0.20.2",
"url-loader": "^0.6.2",
"webpack": "^4.0.1"
},
"devDependencies": {
"elm-test": "^0.18.12",
"foreman": "^2.0.0",
"json-server": "^0.12.1",
"webpack-cli": "^2.0.9",
"webpack-dev-server": "^3.0.0"
}
}
ソースコード
環境
チュートリアルのままでは、elm-webpack-loaderでエラーが起こりwebpack4で動かなかった。
エラーの原因であるwebpack3でdeprecateで4で消えたthis.optionsを、this.queryに置換している。*
これ、プルリクとか出したほうがいいのだろうか。やったことないけれど。
FROM node:9.6.0
# コンテナ上の作業ディレクトリ作成
WORKDIR /app
# 後で確認出来るようにpackage.jsonを作成
RUN npm init -y
# elmインストール
RUN npm i -S elm
RUN npm i -D elm-test
RUN npx elm package install elm-lang/html -y
# webpackv4
RUN npm i -S webpack
# v4から追加
RUN npm i -D webpack-cli
# 開発サーバ
RUN npm i -D webpack-dev-server
# loader
RUN npm i -S elm-webpack-loader
RUN npm i -S file-loader
RUN npm i -S css-loader
RUN npm i -S style-loader
RUN npm i -S url-loader
# バックエンド
RUN npm i -D json-server
# サーバ複数起動用
RUN npm i -D foreman
# フロントエンド
RUN npm i -S sanitize.css
RUN npm i -S ace-css
RUN npm i -S font-awesome
# "source-directories": [ "src" ], に変更
RUN sed -i -e "s/\".\"/\"src\"/" /app/elm-package.json
# package.json設定
RUN sed -i -e "s/\(\"scripts\": {\)/\1\n \"api\": \"node api.js\", /" /app/package.json
RUN sed -i -e "s/\(\"scripts\": {\)/\1\n \"build\": \"webpack\", /" /app/package.json
RUN sed -i -e "s/\(\"scripts\": {\)/\1\n \"client\": \"webpack-dev-server\", /" /app/package.json
RUN sed -i -e "s/\(\"scripts\": {\)/\1\n \"start\": \"nf start\", /" /app/package.json
# https://medium.com/webpack/webpack-4-migration-guide-for-plugins-loaders-20a79b927202
# deprecateなthis.optionsをthis.queryに置き換え。
RUN sed -i -e "s/this\.options/this\.query/g" /app/node_modules/elm-webpack-loader/index.js
version: '3'
services:
elm-dev:
build: ./elm-dev
volumes:
- ../tutorial/webpack.config.js:/app/webpack.config.js
- ../tutorial/api.js:/app/api.js
- ../tutorial/db.json:/app/db.json
- ../tutorial/src:/app/src
- ../tutorial/Procfile:/app/Procfile
- ../tutorial/dist:/app/dist
ports:
- 3000:3000
- 4000:4000
command: [yarn, start]
dev-serverの設定をdockerとvagrantを使用したときの書き方に。
var path = require("path");
const env = process.env.NODE_ENV;
module.exports = {
mode: env || `development`,
entry: {
app: [
'./src/index.js'
]
},
output: {
path: path.resolve(__dirname + '/dist'),
filename: '[name].js',
},
module: {
rules: [
{
test: /\.(css|scss)$/,
use: [
'style-loader',
'css-loader',
]
},
{
test: /\.html$/,
exclude: /node_modules/,
loader: 'file-loader?name=[name].[ext]',
},
{
test: /\.elm$/,
exclude: [/elm-stuff/, /node_modules/],
loader: 'elm-webpack-loader?verbose=true&warn=true',
},
{
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'url-loader?limit=10000&mimetype=application/font-woff',
},
{
test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'file-loader',
},
],
noParse: /\.elm$/,
},
// 開発サーバの設定
devServer: {
// インラインモード
inline: true,
stats: { colors: true },
// 3000番ポートで起動
port: 3000,
// dockerのコンテナ上でサーバを動かすときは以下の設定で全ての接続を受け入れる
host:"0.0.0.0",
},
// Windowsのvagrantの仕様でポーリングしないとファイルの変更を感知できない
watchOptions: {
aggregateTimeout: 300,
// 5秒毎にポーリング
poll: 5000
},
};
foreman用の設定。
yarnはdockerのnode9.6だと最初から入っている。
api: yarn api
client: yarn client
バックエンド
ここはチュートリアルどおり。
var jsonServer = require('json-server')
// Returns an Express server
var server = jsonServer.create()
// Set default middlewares (logger, static, cors and no-cache)
server.use(jsonServer.defaults())
var router = jsonServer.router('db.json')
server.use(router)
console.log('Listening at 4000')
server.listen(4000)
{
"players": [
{ "id": "1", "name": "Sally", "level": 2 },
{ "id": "2", "name": "Lance", "level": 1 },
{ "id": "3", "name": "Aki", "level": 3 },
{ "id": "4", "name": "Maria", "level": 4 },
{ "id": "5", "name": "Julian", "level": 1 },
{ "id": "6", "name": "Jaime", "level": 1 }
]
}
フロントエンド
こちらもチュートリアルどおり。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Elm SPA example</title>
</head>
<body>
<div id="main"></div>
<script src="/app.js"></script>
</body>
</html>
'use strict';
require('ace-css/css/ace.css');
require('font-awesome/css/font-awesome.css');
// index.htmlがdistにコピーされるようにRequireする
require('./index.html');
var Elm = require('./Main.elm');
var mountNode = document.getElementById('main');
// .embed()はオプションの第二引数を取り、プログラム開始に必要なデータを与えられる。たとえばuserIDや何らかのトークンなど
var app = Elm.Main.embed(mountNode);
module Main exposing (..)
import Html exposing (Html, div, text, program)
-- モデル
type alias Model =
String
init : ( Model, Cmd Msg )
init =
( "Hello", Cmd.none )
-- メッセージ
type Msg
= NoOp
-- ビュー
view : Model -> Html Msg
view model =
div []
[ text model ]
-- 更新
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
NoOp ->
( model, Cmd.none )
-- サブスクリプション(購読)
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none
-- MAIN
main : Program Never Model Msg
main =
program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
確認
docker-compose up
で起動。
vagrantで作った仮想環境のIPが192.168.50.10だとして以下のように確認。
サーバのテスト。db.jsonのid:1のデータが表示されればOK
http://192.168.50.10:4000/players/1
フロントエンドのテスト。Helloと出力されていればOK
http://192.168.50.10:3000
webpack-dev-server3.0ではホットロードがデフォルトらしく、Main.elmのHelloを書き換えるとブラウザが自動的に更新される。
参考
elmチュートリアル
elm思考法
tutorial source
アプリケーションの開始
最新版で学ぶWebpack4のモジュールハンドラ
gulpでwebpack4
次のリリースであるwebpack 4の主な変更点まとめ
webpack 4: migration guide for plugins/loaders
elm-webpack-loader
example
Elmでオブジェクト指向プログラマのための関数型入門
webpack4が出たのでメモ