Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

Emacsを使って既存のファイルを編集していると,gulp.watch内でクラッシュが発生する問題の回避方法

More than 3 years have passed since last update.

要約

Emacsを使って既存のファイルを編集していると,gulp.watch内でクラッシュが発生する.問題を回避するため,gulp.watchglob-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.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のたびに該当箇所を書き換える必要があるため面倒だ(書き換えを自動がすることは可能だろう).そのため,おすすめはできない.

masnagam
A freelance software engineer
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away