要約
Emacsを使って既存のファイルを編集していると,gulp.watch
内でクラッシュが発生する.問題を回避するため,gulp.watch
をglob-chokidarで置き換えた.
この問題自体は既知の問題であり,各モジュールのGitHub IssueやStack Overflowでも記述を見かける.同じ問題で悩んでいる人の参考になれば幸いである.
もし,他の回避方法を知っている人がいれば,ぜひ教えてほしい.
本記事の内容の確認は,以下の環境で行っている.他の環境では,ここに書かれてあるように動作しないかもしれない.
- Xubuntu 14.04 on VirtualBox 4.3.14 with Guest Additions
- GNU Emacs 24.3.1
事の発端
Emacsを使って既存のファイルを編集していると,gulp.watch
内でクラッシュする.
実行しているgulp.watch
は以下のようなものだ.
gulp.watch ['path/to/**/*.coffee'], ['compile']
Emacsですでに存在するCoffeeScriptファイルを編集し,そのバッファを未保存とすると,Emacsのインターロック機能により,編集したファイルの先頭に.#
を付与したシンボリックリンクが作成される.例えば,編集したファイルがpath/to/file.coffee
だとすると,path/to/.#file.coffee
が作成される.
この状態でgulp.watch
が反応すると,以下のような例外が発生し,例外を捕捉していなければプログラムは停止する.
[20:35:04] Error: ENOENT, no such file or directory 'path/to/.#file.coffee'
この問題は,gulp.watch
に渡すglobパターンに除外パターンを含めても回避できない.
問題の原因
原因は,gulpが間接的に依存しているgazeというモジュールの既知の問題であり,現時点のgazeの最新版である0.6.4では修正されている(0.6.4以外は未確認だが,0.6系列で修正されたのかもしれない).
しかし,gulp 3.8.7は古いgaze 0.5.1を使用しているため問題が発生している.
この問題はgulp 3.8.7だけでなく,古いgazeを使っているモジュールであればあれば発生する可能性がある.再現を確認してはいないが,恐らく以下のモジュールでも発生するだろう.
回避方法1
glob-chokidarを使って,gulp.watch
を置き換える.
元ソースだと複数のglobパターンを指定できないため,すこし改造を加えた.
これをインストールする.
$ npm install git+https://github.com/masnagam/glob-chokidar.git --save-dev
その後,gulp.watch
を置き換える.
watch = require 'glob-chokidar'
gulp.task 'watch', ->
watch ['path/to/**/*.coffee'], -> gulp.start ['compile']
ここで,gulp.run
ではなく,gulp.start
を使っている理由は以下の通り.
-
gulp.run
は廃止予定 -
gulp.watch
はgulp.start
を呼び出してる
なお,gulp.watch
と同様にon
も使える.
watch ['path/to/compiled/assets/**']
.on 'change', (path) ->
...
発生するイベントの種類については,Chokidarを参照.
最後に,glob-chokidarの引数について補足しておく.
gulp.wath
では,以下のように記述することで除外パターンを指定できる.
gulp.watch ['path/to/*.include', '!path/to/*.exclude'], ['task']
一方,glob-chokidarはChokidarの流儀に従い,除外パターンをオプションで指定する.
watch ['path/to/*.include'], ignored: ['path/to/*.exclude'], ->
...
怪我の功名?
例えば,以下のようなパターンでgulp.watch
を行ったとする.
gulp.watch ['path/to/**'], ['task']
この場合,監視しているフォルダ配下に
touch path/to/new.coffee
新しいファイルを作成してもtask
タスクは実行されない.つまり,new.coffee
は監視対象とはみなされない.
new.coffee
を監視対象にしたい場合には,gulpfile.coffee
を再読み込みする必要がある.
なお,監視中のファイルの削除は検出し,task
タスクが実行される.
まとめると,以下のように振る舞うようだ.
-
gulp.watch
実行時のパターンでファイルを集めて,これを監視対象とする - 新しくファイルを追加しても監視対象にならない
- 監視対象に含めるためには,
gulpfile.coffee
を再読み込みする必要がある
- 監視対象に含めるためには,
一方,Chokidarは,ディレクトリを指定した場合,その配下が監視対象となる.そのため,gulp.watch
のようにgulpfile.coffee
の再読み込みが必要ない.
回避方法2
一応,上記以外の回避方法もある.npm-shrinkwrap.json
を使う方法だ.
npm shrinkwrap
もしくはnpm shrinkwrap --dev
を実行し,npm-shrinkwrap.json
のgazeのバージョンを書き換える.このとき,gazeが依存しているモジュールのエントリはすべて消してしまったほうが良いだろう.
gulp 3.8.7に対して上記を行ったところ,問題は発生しなくなった.
ただ,この方法は依存関係を無視して強引にバージョンを書き換えるため,別の問題が発生する可能性を否定できない.また,npm shrinkwrap
のたびに該当箇所を書き換える必要があるため面倒だ(書き換えを自動がすることは可能だろう).そのため,おすすめはできない.