Electron & Gulp 環境で Watch して LiveReload な感じのことする時にハマったのでメモ


趣味でプログラミングを触っていて、友人と一緒にチーム開発をしているのですが、Electron & Gulp の環境でライブリロードみたいなことできたらすごい楽だよなーとか軽い気持ちやってみたらすごいハマったのでメモ。

環境

package.json
{
  ...

  "scripts": {
    "start": "gulp start",
    "build": "gulp build"
  },
  "devDependencies": {
    ...

    "electron-connect": "^0.6.3",
    "gulp": "^4.0.0",
    "gulp-plumber": "^1.2.1",
    "gulp-useref": "^3.1.6",
  },
  "dependencies": {
    "npm": "^6.5.0",
    "electron": "^4.0.0",
    ...
  }
}

dependencies と devDependencies の整理ができていないので使い分け的に正しいとは限りません...

Babel, SCSS/SASS, ファイルのバンドルなどは webpack です。

全て webpack で終始したかったのですが、色々あって Gulp でビルドなどのタスク管理はすることになりました。

また Electron の起動・リロードなどの処理は electron-connect を使いました。

最初の実装 (正しく動かなかった)

Gulp にはwatchという、ファイルの状態を監視して、変更が発生すれば渡された処理を行う、というメソッドが用意されています。

そこでこの watch メソッドを使って、以下のようなコードを書いてみました:

gulpfile.js
const gulp = require('gulp');
const useref = require('gulp-useref');

const electron = require('electron-connect').server.create();

// ...略...

gulp.task('build', gulp.series( gulp.parallel(
  'compile-js',
  'compile-styles',
  'compile-renderer'
)));

gulp.task('start', gulp.series('build', () => {
  electron.start();
  gulp.watch(['src/scripts/main/**/*.js'], gulp.series('compile-js', electron.restart()));
  gulp.watch(['src/styles/**/*.{scss,sass}'], gulp.series('compile-styles', electron.reload()));
  gulp.watch(['src/**/*.html'], gulp.series('compile-renderer', electron.reload()));
}));

electron.start() で Electron を起動、 reload() でレンダラープロセスの方を再起動、
restart() で再起動、といった感じです。

index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello!</title>
    <!-- build:remove -->
    <!-- Connect to server process -->
    <script>require('electron-connect').client.create()</script>
    <!-- end:build -->
  </head>
  <body>
    <!-- ...略... -->
  </body>
</html>

Electron に用いるHTMLファイル。

これだとうまく動かない

これだと正しく動きませんでした。
具体的には、一度は変更を検知してくれますが、二度目以降、それぞれの watch タスクがきちんと動いてくれない... というものです。

最初の index.html の更新は検知するけど、二度目は検知してくれない... といった感じ。

解決した

ログは貼れないのですが、よくよくみたら、 electron.reloadelectron.resart のタスクの finished の出力がされていない... ということがわかりました。

額にしわを寄せつつ英語の Docs を読むなどして色々と試して、

electron.reloadelectron.resart の終了がきちんとされているかどうかがGulp 側が検知されてない
= Gulp にとってはそれらがまだ動いているつもり、(実際は終わってる)
= 新しいファイル検知が行われない (Gulp がまだビルド・再起動の途中と認識してるから)

ということではないかと仮説を立てました。

Gulp の仕様としては callback を返せば良い (= 逆に何も帰ってこなかったら処理中とみなされる) ということが Docs を頑張って解読した結果解釈できたので、全部無名関数で囲って callback を返してあげればいいのでは...? と思い、

gulpfile.js
gulp.watch(['src/scripts/main/**/*.js'], gulp.series('compile-js', 
  done => {
    electron.restart();
    done();
  }
));

gulp.watch(['src/styles/**/*.{scss,sass}'], gulp.series('compile-styles',
  done => {
    electron.reload();
    done();
  }
));

gulp.watch(['src/**/*.html'], gulp.series('compile-renderer',
  done => {
    electron.reload();
    done();
  }
));

これによってきちんと動くようになりました。

英語だろうがなんだろうが Docs を読むことはめんどくさがってはいけないということがわかった一件でした。