Node.js開発環境まとめ


macでの環境構築です。エディターはvimを使います。
クライアントサイドのことはほぼ考えてないです。

# nodenv

昔はnodebrewでしたが、今はnodenvです。(ちなみにndenvはDeprecatedになっていてnodenvを推奨しています。)
nodebrewに比べて、```.node-version``` によって自動でバージョンが切り替わるので良いです。

homebrewで入れます。

```
$ brew install nodenv
```

PATHを通します。

```.bash_profile
PATH="$HOME/.nodenv/bin:$PATH"
if which nodenv > /dev/null; then 
  eval "$(nodenv init -)"
fi
```

```
$ source ~/.bash_profile
```

これだけだと ```install```コマンドが使えないので、node-buildをnodenvのpluginsに起きます。

```
$ git clone https://github.com/riywo/node-build.git $(nodenv root)/plugins/node-build
```

インストールできるnode.jsのバージョンを表示します。

```
$ nodenv install -l
```

インスールの仕方です。

```
$ nodenv install [version]
```

グローバルで利用するバージョンを指定します。

```
$ nodenv global [version]
$ nodenv rehash
```

特定のディレクトリで以下をすると指定したバージョンのnodeが利用できます。

```
$ nodenv local [version]
```

# ~~nodebrew~~

___nodenvを利用するなら、nodebrewは利用しません。___

~~node.js自体のバージョン管理するものです。
rubyでいうrbenvです。似たものにnvmがあります。~~

~~自分はhomebrewで入れます。~~

```
$ brew install nodebrew
```

~~PATHを通します。~~

```.bash_profile
PATH=$HOME/.nodebrew/current/bin:$PATH
```

```
$ source ~/.bash_profile
```

~~インストールできるnode.jsのバージョンを表示します。~~

```
$ nodebrew ls-remote
```

~~ここでは最新のバージョンを入れます。```nodebrew install``` だとソースからコンパイルしてインストールするので時間がかかります。~~


```
$ nodebrew install-binary latest
```

~~___2015/12追記___~~

~~上記コマンドで以下のエラーが出るようになったので、~~

```
nodebrew install-binary latest
fetch: http://nodejs.org/dist/v5.0.0/node-v5.0.0-darwin-x64.tar.gz
Warning: Failed to create the file
Warning: /Users/ishii_ko/.nodebrew/src/v5.0.0/node-v5.0.0-darwin-x64.tar.gz:
Warning: No such file or directory

curl: (23) Failed writing body (0 != 2493)
download faild: http://nodejs.org/dist/v5.0.0/node-v5.0.0-darwin-x64.tar.gz
```

~~直接curlでnodebrewをインストールすると解決しました。~~

```
$ curl -L git.io/nodebrew | perl - setup
```

~~___2015/12追記ここまで___~~

~~インストール済みのバージョンを確認します。~~

```
$ nodebrew list
v0.12.4
current: none
```

~~利用するバージョンを選択します。~~

```
$ nodebrew use v0.12.4
use v0.12.4
```

~~ここまでで ```node``` コマンドが使えるようになります。~~

```
$ node -v
v0.12.4
```

試しにHello Worldを作ります。

```hello.js
var http = require('http');

var server = http.createServer();
server.on('request', doRequest);
server.listen(8080);

function doRequest(req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.write('Hello World\n');
  res.end();
}
```

試します。

```
$ node hello.js
$ curl -i http://localhost:8080
HTTP/1.1 200 OK
Content-Type: text/plain
Date: Sun, 14 Jun 2015 14:52:11 GMT
Connection: keep-alive
Transfer-Encoding: chunked

Hello World
```

Ctrl + cで終了です。

# npm

Node.jsのモジュールを管理するツールです。

nodeインストール時に一緒に入っています。

```
$ npm -v
2.10.1
```

___ExpressのようなScaffold機能があるフレームワークを利用している場合は、package.jsonも作成されるので```npm init```は必要ありません。___

まず作業ディレクトリに移動しておきます。
利用したいモジュールとそのバージョンを管理するためのファイル ```package.json ``` を作成します。
インタラクティブに聞かれます。

```
$ npm init
name: (spike-node)
version: (1.0.0)
description: sample of node.js
entry point: (index.js)
test command: test
git repository:
keywords:
author: ko2ic
license: (ISC) MIT
About to write to /Users/ko2ic/Sources/javascript/spike-node/package.json:

{
  "name": "spike-node",
  "version": "1.0.0",
  "description": "sample of node.js",
  "main": "index.js",
  "scripts": {
    "test": "test"
  },
  "author": "ko2ic",
  "license": "MIT"
}


Is this ok? (yes)
```

パッケージをインストールします。インストールと同時にpackage.jsonに追記するには、```--save``` や ```--save-dev```　をつけます。

```--save-dev``` は単体テストなど開発時にしか利用しないライブラリの場合に指定します。
例えばBDDフレームワークのjasmineを入れてみます。

```
$ npm install --save-dev jasmine-node
・・・
$ tree -L 2
.
├── node_modules
│   └── jasmine-node
└── package.json

```

# Gulp

ビルドツールです。
CoffeeScriptなどのAltJS、SassなどのCSSプリプロセッサをコンパイルしたり、JSを圧縮したりとタスクの自動化に使います。似たようなものにGruntがあります。

```
$ npm install -g gulp
$ cd <プロジェクトルート>
$ npm install gulp --save-dev
```

gulpfile.jsを作成します。
この場合は、単純にdefaultタスクでログを出力するだけです。

```gulpfile.js
var gulp = require('gulp');

gulp.task('default', function() {
  // place code for your default task here
  console.log('Hello Gulp!');
});
```

必要なGulpのPluginは、http://gulpjs.com/plugins/ や https://www.npmjs.com/ から検索します。

インストールは、npmでします。例えば、coffeescriptを使いたい場合は以下のようになります。

```
$ npm install gulp-coffee --save-dev
```

# Debugツール

## 組み込みデバッガ
node.jsでは組み込みのデバッガがあります。
こちらは、Rubyのpry-byebugみたいなものです。

起動時に ```debug``` を付けることで動作します。

```
$ node debug hello.js
```

```s``` でステップ実行(メソッド内に入る)、```n``` で次の行、```o``` でステップアウト(メソッドを抜けてる)、```c``` でブレークポイントまで。
```watch('<評価する式>')``` で変数の中身をwatchできます。解除は、 ```unwatch('<評価する式>')``` です。
```sb(<ファイル名>, <行数>)``` でブレークポイント、```cb(<ファイル名>, <行数>)``` で解除です。
Ctrl+Cを二回で終了

```
< Debugger listening on port 5858
debug> . ok
break in hello.js:1
> 1 var http = require('http');
  2
  3 var server = http.createServer();
debug> s
break in module.js:384
 382
 383   function require(path) {
>384     return self.require(path);
 385   }
 386
debug> watch('path')
debug> n
break in module.js:385
Watchers:
  0: path = "http"

 383   function require(path) {
 384     return self.require(path);
>385   }
 386
 387   require.resolve = function(request) {
debug> c
debug>
(^C again to quit)
debug>
```

## node-inspector

GUIでDebugするためのツールです。

```-g``` でシステム環境(```~/.nodebrew/current/```)にインストールします。

```
$ npm install -g node-inspector
```

起動しときます。ここに表示されるURLをChromeに打ち込むとnode-inspectorが表示されます。

```
$ ~/.nodebrew/current/bin/node-inspector
Node Inspector v0.10.2
Visit http://127.0.0.1:8080/?ws=127.0.0.1:8080&port=5858 to start debugging.
```

対象のプオグラムを ```--debug-brk```  をつけて動作させます。

```
$ node --debug-brk hello.js
Debugger listening on port 5858
```

あとはGUIでぐりぐりと試せばわかると思います。

# プロファイラ

## Node Webkit Agent

Node.jsのHeapとCPUのプロファイルをするツールです。
v0.11.xやv0.12.xではインストールできないので、v0.10.38に変えます。また ```-g``` に入れると起動エラーになりますので気をつけてください。

```
$ nodebrew install-binary v0.10.38
$ nodebrew use v0.10.38
$ npm install webkit-devtools-agent
```

以下をソースに記述します。

```hello.js
var agent = require('webkit-devtools-agent');
agent.start()
```

起動します。

```
$ node hello.js
```

以下をChromeで立ち上げてProfileでCPUやヒープメモリが確認できます。
http://c4milo.github.io/node-webkit-agent/26.0.1410.65/inspector.html?host=localhost:9999&page=0

# 構文チェック

## ESLint

以前は、JSHintやJSLintでしたが最近はこちらを使います。
ユーザ独自のルールを追加することができるのが特徴です。

```
$ npm install -g eslint
```

プロジェクトに移動して以下のコマンドで対話式に設定ファイル ```.eslintrc``` を作成できます。

```
$ eslint --init
```

ルールの詳細は、[公式サイト](http://eslint.org/docs/rules/)　で確認。

# Vim

jsに直接関係するpluginだけ書いときます。

## vim-jsdoc

function行で<C-l>もしくは ```:JsDoc``` でJSDocコメントができる。
これは ```~/.vim/after/ftplugin/javascript.vim``` に記述しても動作しません。

```.vimrc
NeoBundleLazy 'heavenshell/vim-jsdoc' , {'autoload': {'filetypes': ['javascript']}}
```

## vim-node

```gf``` でrequire()のモジュールにジャンプ

```~/.vim/after/ftplugin/javascript.vim
NeoBundle 'moll/vim-node'
```

## jscomplete-vim

jsの補完

```~/.vim/after/ftplugin/javascript.vim
NeoBundle 'mattn/jscomplete-vim'
:setl omnifunc=jscomplete#CompleteJS
```

## vim-nodejs-complete

node.jsの補完

```~/.vim/after/ftplugin/javascript.vim
NeoBundle 'myhere/vim-nodejs-complete'
:setl omnifunc=jscomplete#CompleteJS
if !exists('g:neocomplcache_omni_functions')
  let g:neocomplcache_omni_functions = {}
endif
let g:neocomplcache_omni_functions.javascript = 'nodejscomplete#CompleteJS'
let g:node_usejscomplete = 1
```

## vim-javascript

いい感じのjsのインデントとシンタックスカラーです。

```
NeoBundle 'pangloss/vim-javascript'
```

## syntastic

save時に構文チェックをします。

```
NeoBundle 'scrooloose/syntastic'
let g:syntastic_check_on_open=0 "ファイルを開いたときはチェックしない
let g:syntastic_check_on_save=1 "保存時にはチェック
let g:syntastic_check_on_wq = 0 " wqではチェックしない
let g:syntastic_auto_loc_list=1 "エラーがあったら自動でロケーションリストを開く
let g:syntastic_loc_list_height=6 "エラー表示ウィンドウの高さ
set statusline+=%#warningmsg# "エラーメッセージの書式
set statusline+=%{SyntasticStatuslineFlag()}
set statusline+=%*
let g:syntastic_javascript_checkers = ['eslint'] "ESLintを使う
let g:syntastic_mode_map = {
      \ 'mode': 'active',
      \ 'active_filetypes': ['javascript'],
      \ 'passive_filetypes': []
      \ }
```

# Express

最もメジャーなフレームワークの一つです。
ここではとりあえず、Expressを入れますが、Metetor,Koaなど他のフレームワークでも構いません。

## インストール

Expressコマンドツールも使うのでグローバルにインストールしときます。
Express4からプロジェクトを作成する機能が別に切り出されたので、express-generatorもインストールします。

```
$ npm install -g express
$ npm install -g express-generator
```

## プロジェクト作成

```
$ express spike-expess
$ cd spike-express
$ npm install
```

## 起動

起動スクリプトは以下に記述してあります。

```package.json
  "scripts": {
    "start": "node ./bin/www"
  },
```

以下で起動します。

```
$ npm start
```

http://localhost:3000/ で確認できます。

## Gulp

Expressプロジェクトでgulpを入れときます。

```
$ npm install gulp --save-dev
```

## Less

Lessにしたい場合は、GulpでLessをコンパイルするタスクを追加します。

```
$ npm install gulp-less --save-dev
```

```gulpfile.js
var gulp = require('gulp');
・・・
var less = require('gulp-less');
var path = require('path');

gulp.task('less', function () {
  return gulp.src('./assets/less/*.less')
    .pipe(less({
      paths: [ path.join(__dirname, 'less', 'includes') ]
    }))
    .pipe(gulp.dest('./public/stylesheets'));
});
```

```
$ mkdir -p ./assets/less/
```

```./assets/less/style.less
body {
  padding: 50px;
  font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}

a {
  color: #00B7FF;
}

h1{
  color: #00aa00;
}
```

```
$ gulp less
```

これで ```./public/stylesheets/style.css``` が変わっていれば成功です。

## CoffeeScript

CoffeeScriptにしたい場合は、
まずは、既存のjsをCoffeScriptに変換します。

```
$ npm install -g js2coffee
```

```
$ js2coffee app.js > app.coffee
```

まとめて変換する方法がなさそうなので、シェルを作成します。

```alljs2coffee.sh
#!/bin/bash

for FILE in `find . -name "*.js" -type f -o -path './node_modules' -prune -o -path './components' -prune`
do 
    if [ -e $FILE ] ; then        
        COFFEE=${FILE//.js/.coffee}

        echo "converting ${FILE} to ${COFFEE}"
        js2coffee "$FILE" > "$COFFEE"
    else     
        echo "File: {$1} does not exist!"
    fi
done
```
これは
http://stackoverflow.com/questions/14091818/converting-whole-project-to-coffeescript-with-js2coffee
 のままです。

そして、実行します。

```
$ chmod +x alljs2coffee.sh
$ ./alljs2coffee.sh
```

インストールします。

```
$ npm install coffee-script --save
```

以下を追加

```bin/www
require('coffee-script/register');
```

./public/javascriptsもCoffeeScriptにします。

GulpでCoffeeScriptをjsにコンパイルするタスクを追加します。

```
$ npm install gulp-coffee --save-dev
```

```gulpfile.js
var gulp = require('gulp');
・・・
var coffee = require('gulp-coffee');

gulp.task('coffee', function() {
  gulp.src('./assets/coffee/*.coffee')
    .pipe(coffee({bare: true}).on('error', gutil.log))
    .pipe(gulp.dest('./public/javascripts'))
});
```

# 自動リロード

gulpの機能でファイルを監視して、自動でコンパイルさせます。

```gulpfile.js
gulp.task('default', function () {
  gulp.watch('./assets/less/*.less', ['less']);
  gulp.watch('./assets/coffee/*.coffee', ['coffee']);
});

```

ブラウザも自動リロードさせます。
また、コンパイルエラーで監視がとまらないようにします。

```
$ npm install -g nodemon
$ npm install gulp-nodemon --save-dev
$ npm install browser-sync --save-dev
$ npm install gulp-plumber --save-dev
```

それぞれのタスクの最初でエラーになっても継続するように ```.pipe(plumber()) ``` を記述します。
ファイル変更でブラウザがリロードするようにnodemonでファイルを監視して、サーバー再起動時にBrowerSyncを使っています。

いままでのgulpfile.jsは以下です。

```gulpfile.js
var gulp = require('gulp');
var less = require('gulp-less');
var path = require('path');
var coffee = require('gulp-coffee');
var plumber = require('gulp-plumber');
var nodemon = require('gulp-nodemon');
var browserSync = require('browser-sync');

gulp.task('less', function () {
  return gulp.src('./assets/less/*.less')
    .pipe(plumber()) 
    .pipe(less({
      paths: [ path.join(__dirname, 'less', 'includes') ]
    }))
    .pipe(gulp.dest('./public/stylesheets'))
});

gulp.task('coffee', function() {
  gulp.src('./assets/coffee/*.coffee')
    .pipe(plumber()) 
    .pipe(coffee({bare: true}).on('error', gutil.log))
    .pipe(gulp.dest('./public/javascripts'))
});

gulp.task('nodemon', function(cb) {
  var called = false;
  nodemon({
        script: 'bin/www',
        ext: 'js html css jade coffee',
        env: {
            NODE_ENV: 'development'
        }
  })
  .on('start', function() {
    if (!called) {
        called = true;
        cb();
    }
  })
  .on('restart', function() {
        setTimeout(function() {
        browserSync.reload();
     }, 500);
  });
});

gulp.task('browser-sync', ['nodemon'], function() {
    browserSync.init(null, {
        proxy: 'http://localhost:3000',
        port: 8000
    });
});

gulp.task('default', ['browser-sync'],  function () {
  gulp.watch('./assets/less/*.less', ['less']);
  gulp.watch('./assets/coffee/*.coffee', ['coffee']);
});

```

Gulp + browser-sync + nodemonについては以下の記事を参考にしました。
http://engineer.blue-corporation.jp/dev/gulp-nodemon-browser-sync/