zowのプログラムな日々

日々のプログラミングとか

osxにnodeでgulpなフロントエンド開発環境を作ってみる

「父ちゃん、js嫌いな俺だけどさ、nodeは使ってみたいんだよ」みたいな横浜銀蝿的な囁きが聞こえてくるぐらいにnodeを使ってみたくなったので、とりあえず定番的なnodeを使ったフロントエンドの開発環境を整えてみたいと思います。

node.jsのインストール

brewから一発。

~ 13:49:53 zow$ brew install node
==> Downloading https://homebrew.bintray.com/bottles/node-0.12.7.yosemite.bottle
######################################################################## 100.0%
==> Pouring node-0.12.7.yosemite.bottle.tar.gz
==> Caveats

Bash completion has been installed to:
  /usr/local/etc/bash_completion.d
==> Summary
🍺  /usr/local/Cellar/node/0.12.7: 2726 files, 31M
~ 13:51:54 zow$ 

brewでnodeをインストールするとnpmも同時に入れてくれるらしい。npmってのはnodeで利用されているパッケージマネージャで、pythonで言うpipみたいなもんらしい。

gulpのインストール

gulpってのはなんて言えばいいんだろ。世間ではタスクランナーって呼ばれてるらしい。要するにタスクを実行してくれる。どっかのディレクトリを監視して、変化があったら何かを実行、みたいな使い方が出来るらしい。今回はフロントエンド開発環境を作る訳だけど、静的ファイル(html)が置いてあるディレクトリを監視して、変化があったらhttpサーバを再起動させる?みたいな使い方が出来るらしい。つまりhtmlファイルをガンガン書いて保存すると、自動でhttpサーバを起動してブラウザで変化を確認したり出来るようになる。いちいちリロードとかしなくていいみたいだ。emmetなんかでhtmlを書く様な人の場合結構重宝するだろう。

gulpはnpmコマンドでインストールする。

~ 14:03:30 zow$ npm install -g gulp
/usr/local/bin/gulp -> /usr/local/lib/node_modules/gulp/bin/gulp.js
gulp@3.9.0 /usr/local/lib/node_modules/gulp
├── pretty-hrtime@1.0.0
├── interpret@0.6.4
├── deprecated@0.0.1
├── archy@1.0.0
├── minimist@1.1.1
├── tildify@1.1.0 (os-homedir@1.0.0)
├── v8flags@2.0.9 (user-home@1.1.1)
├── semver@4.3.6
├── chalk@1.1.0 (escape-string-regexp@1.0.3, supports-color@2.0.0, ansi-styles@2.1.0, strip-ansi@3.0.0, has-ansi@2.0.0)
├── orchestrator@0.3.7 (sequencify@0.0.7, stream-consume@0.1.0, end-of-stream@0.1.5)
├── liftoff@2.1.0 (extend@2.0.1, rechoir@0.6.1, flagged-respawn@0.3.1, resolve@1.1.6, findup-sync@0.2.1)
├── gulp-util@3.0.6 (array-differ@1.0.0, array-uniq@1.0.2, beeper@1.1.0, lodash._reescape@3.0.0, lodash._reinterpolate@3.0.0, lodash._reevaluate@3.0.0, object-assign@3.0.0, replace-ext@0.0.1, vinyl@0.5.0, lodash.template@3.6.2, multipipe@0.1.2, through2@2.0.0, dateformat@1.0.11)
└── vinyl-fs@0.3.13 (graceful-fs@3.0.8, strip-bom@1.0.0, defaults@1.0.2, vinyl@0.4.6, mkdirp@0.5.1, through2@0.6.5, glob-stream@3.1.18, glob-watcher@0.0.6)
~ 14:03:48 zow$

これで終わり。楽ちん。

gulpに監視させる

gulpのインストール(プロジェクトディレクトリ)

とりあえずなんか開発するとして、ホームディレクトリの配下に「work/gulp_test」を作成する。

~ 14:19:48 zow$ mkdir -p ~/work/gulp_test
~ 14:20:06 zow$ cd work/gulp_test/
gulp_test 14:20:45 zow$ 

ここを開発ディレクトリと想定してgulpを使ってみる。

まず、このプロジェクトディレクトリにgulpをインストールする。

gulp_test 14:22:34 zow$ npm install --save-dev gulp
gulp@3.9.0 node_modules/gulp
├── pretty-hrtime@1.0.0
├── interpret@0.6.4
├── deprecated@0.0.1
├── archy@1.0.0
├── tildify@1.1.0 (os-homedir@1.0.0)
├── minimist@1.1.1
├── v8flags@2.0.9 (user-home@1.1.1)
├── chalk@1.1.0 (supports-color@2.0.0, escape-string-regexp@1.0.3, ansi-styles@2.1.0, strip-ansi@3.0.0, has-ansi@2.0.0)
├── semver@4.3.6
├── orchestrator@0.3.7 (stream-consume@0.1.0, sequencify@0.0.7, end-of-stream@0.1.5)
├── gulp-util@3.0.6 (array-differ@1.0.0, array-uniq@1.0.2, beeper@1.1.0, lodash._reescape@3.0.0, lodash._reevaluate@3.0.0, lodash._reinterpolate@3.0.0, object-assign@3.0.0, replace-ext@0.0.1, vinyl@0.5.0, lodash.template@3.6.2, through2@2.0.0, multipipe@0.1.2, dateformat@1.0.11)
├── liftoff@2.1.0 (extend@2.0.1, rechoir@0.6.1, flagged-respawn@0.3.1, resolve@1.1.6, findup-sync@0.2.1)
└── vinyl-fs@0.3.13 (graceful-fs@3.0.8, strip-bom@1.0.0, defaults@1.0.2, vinyl@0.4.6, mkdirp@0.5.1, through2@0.6.5, glob-stream@3.1.18, glob-watcher@0.0.6)
gulp_test 14:22:48 zow$ ls -l
total 0
drwxr-xr-x  4 zow  staff  136  7 10 14:22 node_modules
gulp_test 14:22:54 zow$

プロジェクトディレクトリ直下に「node_modules」ってディレクトリが出来た。gulpはこの中にインストールされているらしい。

browser-syncのインストール

次に「browser-sync」をインストールする。こちらもnpmコマンドで行う。

gulp_test 14:38:00 zow$ npm install browser-sync --save-dev
 
> fsevents@0.3.6 install /Users/zow/work/gulp_test/node_modules/browser-sync/node_modules/chokidar/node_modules/fsevents
> node-gyp rebuild

gyp ERR! configure error 
gyp ERR! stack Error: Python executable "python" is v3.4.0, which is not supported by gyp.
gyp ERR! stack You can pass the --python switch to point to Python >= v2.5.0 & < 3.0.0.
gyp ERR! stack     at failPythonVersion (/usr/local/lib/node_modules/npm/node_modules/node-gyp/lib/configure.js:119:14)
gyp ERR! stack     at /usr/local/lib/node_modules/npm/node_modules/node-gyp/lib/configure.js:108:9
gyp ERR! stack     at ChildProcess.exithandler (child_process.js:742:7)
gyp ERR! stack     at ChildProcess.emit (events.js:110:17)
gyp ERR! stack     at maybeClose (child_process.js:1015:16)
gyp ERR! stack     at Socket.<anonymous> (child_process.js:1183:11)
gyp ERR! stack     at Socket.emit (events.js:107:17)
gyp ERR! stack     at Pipe.close (net.js:485:12)
gyp ERR! System Darwin 14.4.0
gyp ERR! command "node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /Users/zow/work/gulp_test/node_modules/browser-sync/node_modules/chokidar/node_modules/fsevents
gyp ERR! node -v v0.12.7
gyp ERR! node-gyp -v v2.0.1
gyp ERR! not ok 
npm WARN optional dep failed, continuing fsevents@0.3.6
 
> ws@0.5.0 install /Users/zow/work/gulp_test/node_modules/browser-sync/node_modules/socket.io/node_modules/engine.io/node_modules/ws
> (node-gyp rebuild 2> builderror.log) || (exit 0)

 
> ws@0.4.31 install /Users/zow/work/gulp_test/node_modules/browser-sync/node_modules/socket.io/node_modules/socket.io-client/node_modules/engine.io-client/node_modules/ws
> (node-gyp rebuild 2> builderror.log) || (exit 0)

browser-sync@2.7.13 node_modules/browser-sync
├── async-each-series@0.1.1
├── longest@1.0.1
├── query-string@2.3.0
├── emitter-steward@0.0.1
├── ucfirst@0.0.1
├── opn@2.0.1
├── dev-ip@1.0.1
├── pad-left@1.0.2 (repeat-string@1.5.2)
├── ua-parser-js@0.7.7
├── meow@3.3.0 (object-assign@3.0.0, camelcase-keys@1.0.0, minimist@1.1.1, indent-string@1.2.1)
├── browser-sync-client@2.2.1 (etag@1.7.0, fresh@0.3.0)
├── portscanner@1.0.0 (async@0.1.15)
├── immutable@3.7.4
├── resp-modifier@4.0.2 (minimatch@2.0.8)
├── foxy@11.0.4 (cookie@0.1.3, http-proxy@1.11.1, lodash.merge@3.3.2)
├── connect@3.4.0 (utils-merge@1.0.0, parseurl@1.3.0, debug@2.2.0, finalhandler@0.4.0)
├── serve-static@1.10.0 (escape-html@1.0.2, parseurl@1.3.0, send@0.13.0)
├── anymatch@1.3.0 (arrify@1.0.0, micromatch@2.1.6)
├── chokidar@1.0.3 (arrify@1.0.0, path-is-absolute@1.0.0, is-glob@1.1.3, glob-parent@1.2.0, async-each@0.1.6, is-binary-path@1.0.1, readdirp@1.3.0)
├── serve-index@1.7.1 (escape-html@1.0.2, parseurl@1.3.0, batch@0.5.2, http-errors@1.3.1, debug@2.2.0, accepts@1.2.10, mime-types@2.1.2)
├── easy-extender@2.3.1 (lodash@2.4.2)
├── eazy-logger@2.1.2 (tfunk@3.0.1, opt-merger@1.1.0)
├── localtunnel@1.5.1 (debug@0.7.4, optimist@0.3.4, request@2.11.4)
├── lodash@3.10.0
├── socket.io@1.3.5 (has-binary-data@0.1.3, debug@2.1.0, socket.io-adapter@0.3.1, engine.io@1.5.1, socket.io-parser@2.2.4, socket.io-client@1.3.5)
└── browser-sync-ui@0.5.12 (connect-history-api-fallback@0.0.5, angular-touch@1.4.2, angular-sanitize@1.4.2, angular-route@1.4.2, angular@1.4.2, stream-throttle@0.1.3, weinre@2.0.0-pre-I0Z7U9OV)
gulp_test 14:38:32 zow$

なんとエラー発生。pythonが3.4.0だからサポートしてないって文句を言ってる。そりゃnodeでpython使ってると思わないから普段の環境のままさ。先に言っといてくれ。

とりあえずPythonをSystemに戻す。

gulp_test 14:42:20 zow$ pyenv versions
  system
  3.4.0
* 3.4.0-spider (set by /Users/zow/.pyenv/version)
  3.4.2
gulp_test 14:42:28 zow$ pyenv global system
gulp_test 14:42:41 zow$ pyenv rehash 
gulp_test 14:42:49 zow$ pyenv versions
* system (set by /Users/zow/.pyenv/version)
  3.4.0
  3.4.0-spider
  3.4.2
gulp_test 14:42:55 zow$ python -V
Python 2.7.6
gulp_test 14:43:02 zow$ 

Python環境をシステムに戻した所で再度入れなおす。

gulp_test 14:43:02 zow$ npm install browser-sync --save-dev
/
> fsevents@0.3.6 install /Users/zow/work/gulp_test/node_modules/browser-sync/node_modules/chokidar/node_modules/fsevents
> node-gyp rebuild

  SOLINK_MODULE(target) Release/.node
  CXX(target) Release/obj.target/fse/fsevents.o
  SOLINK_MODULE(target) Release/fse.node

> ws@0.5.0 install /Users/zow/work/gulp_test/node_modules/browser-sync/node_modules/socket.io/node_modules/engine.io/node_modules/ws
> (node-gyp rebuild 2> builderror.log) || (exit 0)

  CXX(target) Release/obj.target/bufferutil/src/bufferutil.o
  SOLINK_MODULE(target) Release/bufferutil.node
  CXX(target) Release/obj.target/validation/src/validation.o
  SOLINK_MODULE(target) Release/validation.node

> ws@0.4.31 install /Users/zow/work/gulp_test/node_modules/browser-sync/node_modules/socket.io/node_modules/socket.io-client/node_modules/engine.io-client/node_modules/ws
> (node-gyp rebuild 2> builderror.log) || (exit 0)

  CXX(target) Release/obj.target/bufferutil/src/bufferutil.o
browser-sync@2.7.13 node_modules/browser-sync
├── async-each-series@0.1.1
├── longest@1.0.1
├── query-string@2.3.0
├── emitter-steward@0.0.1
├── ucfirst@0.0.1
├── opn@2.0.1
├── dev-ip@1.0.1
├── pad-left@1.0.2 (repeat-string@1.5.2)
├── ua-parser-js@0.7.7
├── serve-static@1.10.0 (escape-html@1.0.2, parseurl@1.3.0, send@0.13.0)
├── portscanner@1.0.0 (async@0.1.15)
├── foxy@11.0.4 (cookie@0.1.3, http-proxy@1.11.1, lodash.merge@3.3.2)
├── meow@3.3.0 (object-assign@3.0.0, camelcase-keys@1.0.0, minimist@1.1.1, indent-string@1.2.1)
├── browser-sync-client@2.2.1 (fresh@0.3.0, etag@1.7.0)
├── connect@3.4.0 (utils-merge@1.0.0, parseurl@1.3.0, debug@2.2.0, finalhandler@0.4.0)
├── immutable@3.7.4
├── resp-modifier@4.0.2 (minimatch@2.0.8)
├── anymatch@1.3.0 (arrify@1.0.0, micromatch@2.1.6)
├── serve-index@1.7.1 (escape-html@1.0.2, parseurl@1.3.0, batch@0.5.2, http-errors@1.3.1, debug@2.2.0, accepts@1.2.10, mime-types@2.1.2)
├── easy-extender@2.3.1 (lodash@2.4.2)
├── eazy-logger@2.1.2 (tfunk@3.0.1, opt-merger@1.1.0)
├── localtunnel@1.5.1 (debug@0.7.4, optimist@0.3.4, request@2.11.4)
├── lodash@3.10.0
├── browser-sync-ui@0.5.12 (connect-history-api-fallback@0.0.5, angular-touch@1.4.2, angular-sanitize@1.4.2, angular-route@1.4.2, stream-throttle@0.1.3, angular@1.4.2, weinre@2.0.0-pre-I0Z7U9OV)
├── chokidar@1.0.3 (arrify@1.0.0, path-is-absolute@1.0.0, is-glob@1.1.3, glob-parent@1.2.0, async-each@0.1.6, is-binary-path@1.0.1, readdirp@1.3.0, fsevents@0.3.6)
└── socket.io@1.3.5 (debug@2.1.0, has-binary-data@0.1.3, socket.io-adapter@0.3.1, socket.io-parser@2.2.4, engine.io@1.5.1, socket.io-client@1.3.5)
gulp_test 14:46:12 zow$ 

無事にインストール出来たっぽい。

gulpfile.jsの作成

必要なパッケージは用意できたので、gulpの設定を記述する「gulpfile.js」を作成する。

var gulp = require('gulp');
var browserSync = require('browser-sync');

gulp.task('browser-sync', function(){
    browserSync({
        server: {
            baseDir: "./html"
        }
    });
});

gulp.task('default', ['browser-sync']);

requireでnodeパッケージをインスタンス化して、gulp.taskでタスクの定義をしている感じ。gulp.taskの中で「default」が標準で起動するタスクになる。今回は「browser-sync」のタスクを作って、defaultでbrowser-syncを起動する形にしている。

次にhtmlを置く「html」ディレクトリを作成する。

gulp_test 15:06:16 zow$ mkdir html
gulp_test 15:06:20 zow$ 

htmlディレクトリを作成したらその中に適当なhtmlファイルを作成しておく。で、この状態でgulpを起動するとブラウザが勝手に起動する。

ディレクトリを監視する

gulpfile.jsを以下の様に変更

var gulp = require('gulp');
var browserSync = require('browser-sync');

gulp.task('browser-sync', function(){
    browserSync({
        server: {
            baseDir: "./html"
        }
    });
});

gulp.task('default', ['browser-sync'], function(){
    gulp.watch(['./html/*.html'], function(){
        browserSync.reload();
    });  
});

デフォルトの動作部分に「gulp.watch」で監視ディレクトリを追加してやる。今回は「html」ディレクトリの中を監視して、変更があった場合はリロードするようになっている。

この状態でhtmlファイルを変更すると自動でブラウザにリロードがかかる。なかなか楽しい感じになる。

今回はブラウザののリロード自動化だけだが、他にもscssとかlessとか使ってればコンパイルをさせる事ができたり、jsのminifyなんかをさせたりといろいろ出来る。基本的にファイル監視してそれをトリガに何かをさせる形になるのだが、かなりプラグインが用意されてるので、開発環境として思いつく限りの事は大体出来そうだ。

jsが嫌いと言うことで、今まであまりnodeなんかを触ることはなかったのだけど、それだけで随分と時代から遅れているんだなー、なんて事を今更ながらに実感した。好き嫌いしちゃダメだよねー。ホントに。