Elm + Webpack の Electron 化 - Elm + Electron + Webpack 覚書(3)

はじめに

electron-webpack を拡張しようと思ったが、ブラックボックスの部分が多くて、何をやっているのかがわかりにくいので、 GitHub - johnomarkid/elm-electron-webpack: A guide, not a template, for building electron apps with elm and webpack をベースに作業してみる。elm が 0.18 の時代で少し古いので、このままではうまく動かなかったので、いろいろな場所で修正している。

やってみたこと

ファイルの準備

ここではサイトの手順通りに作業を記録してみる。下の記事は npm 使っているのだが、yarn でやってみる。まずはフォルダの作成と、yarn の初期化である。ここから下で main.js をエントリーにしているので、index.js ではなく main.js にしておく。

mkdir -p elm-electron-webpack
cd elm-electron-webpack
touch README.md webpack.config.js
mkdir -p src/{elm,static}
yarn init

次に electron を global にインストールしておく。

yarn global add electron

index.html と main.js の作成

最初に二つのファイルを準備する。

touch main.js src/static/index.html

src/static/index.html には以下のように記述する。

<!DOCTYPE html>
<html>
  <head>
    <title>This title shows at the top</title>
  </head>

  <body>
    <h1>Hello Electron</h1>
  </body>
</html>

main.js は以下のように記述する。こちらはメインプロセスの Javascript である。

'use strict'
const electron = require('electron')

const app = electron.app // アプリを得る
const BrowserWindow = electron.BrowserWindow // ブラウザウインドウのモジュール

let mainWindow // ガベージコレクトされないようにグローバルな参照を用意しておく

app.on('ready', createWindow) // 初期化時に createWindow が呼ばれるように設定

// 最初に呼ばれる createWindow
function createWindow() {
    mainWindow = new BrowserWindow({
        width: 1024,
        height: 768,
        webPreferencs: {
            nodeIntegration: true
        }
    })

    // index.html を mainWindow に表示
    mainWindow.loadURL(`file://${__dirname}/src/static/index.html`)

    // コンソールエラーが見えるように devTool を開く
    mainWindow.webContents.openDevTools()

    mainWindow.on('closed', function () {
        mainWindow = null
    })
}

// macOS 用の設定

// macOS 以外のアプリではウィンドウが閉じた時にアプリも終了する
app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') { app.quit() }
})

// mainWindow がなかった時には、一つウィンドウを作成する
app.on('activate', () => {
    if (mainWindow == null) { createWindow() }
})

ここで、以下のコマンドをタイプすることで electron が起動する。

electron main.js

f:id:hkob:20200606112052p:plain
Electron アプリの起動

Elm の記述

(2) までですでに global に elm をインストールしているが、(3) からみている人はここで、elm と elm-format を global の node_modules にインストールしておく。

yarn global add elm elm-format

まず、elm を初期化する。

elm init

src/elm/Main.elm ファイルを作成する。

touch src/elm/Main.elm

Main.elm を以下のように記述する。

module Main exposing (..)

import Html exposing (text)


main =
    text "Hello Electron. I'm Elm."

elm.json の src の場所を「src/elm」に変更しておく。

    "source-directories": [
        "src/elm"
    ],

elm をコンパイルして、bundle.js を作成する。

elm make src/elm/Main.elm --output src/static/bundle.js 

index.html の中身を以下のように書き換える。

<!DOCTYPE html>
<html>
  <head>
    <title>This title shows at the top</title>
  </head>

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

これで、再度以下のコマンドをタイプすることで electron が起動する。

electron .

f:id:hkob:20200606203003p:plain
elm バージョンの Electron

Webpack の導入

dist フォルダを作成し、src/static/index.js ファイルを作成する。また、elm make で作成した、src/static/bundle.js はもう要らないので削除する。

mkdir -p dist
touch src/static/index.js
rm src/static/bundle.js

src/static/index.js を以下のように記述する。Elm.Main.embed も 0.19 でなくなっているので、init にしている。

import { Elm } from '../elm/Main.elm'

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

webpack webpack-cli elm-webpack-loader を yarn でインストールしておく。

yarn add webpack webpack-cli elm-webpack-loader -D

先に作成した webpack.config.js を次のように記述する。warn=true は Elm 0.19 ではなくなったので取り外した。

module.exports = {
    entry: './src/static/index.js',
    output: {
        path: __dirname + '/dist',
        filename: 'bundle.js'
    },
    module: {
        rules: [
            {
                test: /\.elm$/,
                exclude: [/elm-stuff/, /node_modules/],
                use: {
                    loader: 'elm-webpack-loader?verbose=true'
                }
            }
        ]
    },
    resolve: {
        extensions: ['.js', '.elm']
    }
}

最後に、index.html のスクリプトを webpack で生成した、dist/bundle.js の呼び出しに書き換える。

  <body>
    <div id='container'></div>
    <script src='../../dist/bundle.js'></script>
  </body>

これで、再度以下のコマンドをタイプすることで electron が起動する。webpack に対応したので、electron を実行する前に、毎回 webpack を実施しなければならなくなった。

yarn run webpack
electron .

webpack-dev-server

開発するたびに webpack を実行するのは大変なので、開発環境では webpack-dev-server を起動しておき、この部分を自動化する。最初に webpack-dev-server を開発環境としてインストールしておく。

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

すでに書いてあった webpack.config.js に publicPath を追加する。この結果、http://localhost:8080/assets/bundle.jsでアクセスできるようになる。

module.exports = {
    entry: './src/static/index.js',
    output: {
        path: __dirname + '/dist',
        publicPath: '/assets/',
        filename: 'bundle.js'
    },
    module: {
        rules: [
            {
                test: /\.elm$/,
                exclude: [/elm-stuff/, /node_modules/],
                use: {
                    loader: 'elm-webpack-loader?verbose=true'
                }
            }
        ]
    },
    resolve: {
        extensions: ['.js', '.elm']
    },
    devServer: { inline: true }
}

そこで、index.html の bundle.js の部分を修正する。

<!DOCTYPE html>
<html>
  <head>
    <title>This title shows at the top</title>
  </head>

  <body>
    <div id='container'></div>
    <!--<script src='../../dist/bundle.js'></script>-->
    <script src='http://localhost:8080/assets/bundle.js'></script>
  </body>
</html>

webpack-dev-server を起動しておく。

yarn run webpack-dev-server --content-base /dist

この上で、別のターミナルで electron を起動する。webpack を起動することなく、electron が実行できていることがわかる。

electron .

とりあえず、リンク先の記事はここまで。ここから、electron-webpack などを参考にパッケージングなどを実施してみる。