gulpのアプローチ "なぜグローバルとローカルにインストールが必要なのか"

gulp srtucture

gulpのGetting Startedにもあるように、gulpを使うにはグローバルインストールとローカルインストールが必要になる。なんで同じものを2つインストールするのか、不思議に思ってソース読んで「へー」と思ったのでまとめてみる。

Gruntの場合

Gruntの場合はgrunt-cliをグローバルにインストールしてgruntはローカルにインストールする。grunt-cliはローカルにインストールしたgruntを呼び出すためだけのモジュールだ。タスクを走らすgruntと、そのgruntを実行するgrunt-cli、2つのモジュールは明確に役割が切り離されている。

Gruntがこうなってる理由は、異なるプロジェクトで使ってるgruntのバージョンが違うと、グローバルにインストールされてるgruntのバージョン次第で、そのタスク(Gruntfile)が動かせない可能性が出てくるから。

(参考: Grunt v0.4.0 での変更点 | Takazudolog

gulpの場合

gulpもGruntと同じで、グローバルにインストールされたgulpはローカルにインストールされたgulpを実行するためにインストールする。でもこのグローバルとローカルの2つのモジュールの中身は完全に同じもので、まったく同じモジュールをグローバルとローカルにインストールすることになる。ここにGruntとのアプローチの違いがある

gulpのアプローチ

gulpのソースを見ると次のようなコードがある。

1
2
3
...
var gulpInst = require(env.modulePath);
...

envには実行しているディテクトリのパスやgulpfileのパスなどが入っている。env.modulePathにはローカルのnode_modulesにあるgulpのパスが入っている。gulpコマンドが実行されると、このrequireでローカルのgulpをインポートして、実際の処理(gulpfileに書かれたタスク)はrequireされたローカルのgulpモジュールが全て行う。自分自身が自分自身と同じモジュールをrequireして使うという仕組みだ(厳密には同じじゃないけどまぁ同じということで)。おもしろい!

ローカルにgulpがインストールされてない場合

ローカルにgulpがインストールされてない場合には次のようなチェックではじいている。

1
2
3
4
5
6
7
8
9
10
...
if (!env.modulePath) {
gutil.log(
chalk.red('Local gulp not found in'),
chalk.magenta(tildify(env.cwd))
);
gutil.log(chalk.red('Try running: npm install gulp'));
process.exit(1);
}
...

ローカルにgulpがインストールされてない場合はenv.modulePathundefinedになる。ここではじくことで処理が終わる。

まとめ

Gruntはgunrt-cligruntを別のモジュールにして役割を分けた。
gulpは同じgulpの中で別のgulpをモジュールとしてrequireするというアプローチを取っている。グローバルとローカル、同じモジュールに2つの役割を持たせて、プロジェクトごとのgulpのバージョンの違いを吸収している。個人的にはgulpのアプローチのほうがカッコイイと思う。スマートだ。

今回gulpのソースを読んでみて、gulpというかNode.jsがおもしろいと思った。


おまけ

gulpは最近何かと話題のsemverのチェックもしている。

1
2
3
4
5
6
7
8
...
// check for semver difference between cli and local installation
if (semver.gt(cliPackage.version, env.modulePackage.version)) {
gutil.log(chalk.red('Warning: gulp version mismatch:'));
gutil.log(chalk.red('Global gulp is', cliPackage.version));
gutil.log(chalk.red('Local gulp is', env.modulePackage.version));
}
...

グローバルとローカルのバージョンがsemver的に合ってない場合は警告を出してくる。もうすぐ4.x系が出てくるだろうから、このメッセージを見れる日は近い。