[Sails.js] フロントエンドパッケージをnpmで管理する

sails 1.0でbootstrapを使おうと思ってsails.jsのチュートリアルやドキュメントを見ていると、jQueryやbootstrapなどのフロントエンドライブラリについて、「assets/js/dependenciesにコピーして置いとけ」っていう回答が多い。（[How to add bootstrap to our sails application?](http://blog.zot24.com/how-to-add-bootstrap-to-our-sails-js-0-10-project/)）
今時パッケージマネージャーを使わずに外部ライブラリを導入するやり方に違和感が半端なかったので、npmでパッケージ管理する方法を考えました。

# webpackを使う
まず、フロントエンドをVue.jsで作ろうと思ってたので、いずれにしろwebpackでフロントエンドのJSをコンパイルする必要がありました。ので、フロントエンドのライブラリーもnpmでインストールしwebpackでコンパイルする方針で行きます。
Sails.jsはデフォルトでgruntをビルドに使っているので、grunt-webpackを入れる。
`npm install grunt-webpack --save-dev`

ES6も使いたいのでbabel-loaderも入れておく。
`npm install babel=loader --save-dev`
`npm install babel-preset-es2015 --save-dev`

# ディレクトリ構成
参考にした構成は[hackernews](https://github.com/denismars/hackernews)。
sails.jsのデフォルトのビルドフローと区別するためにclient/webというディレクトリを作って、webpackでコンパイルするJSはここに置きます。コンパイルされたファイルはassets/js/buildに出力されるようにします。

# gruntタスク設定
webpackのgrunt taskを追加します。

```javascript:tasks/config/webpack.js
module.exports = function (grunt) {
  grunt.config.set('webpack', {
    dev: {
      entry: {
        main: './client/web/main.js',
        index: './client/web/index.js'
      },
      output: {
        path: __dirname + '/../../assets/js/build',
        filename: '[name].js'
      },
      stats: {
        colors: false,
        modules: false,
        reasons: false
      },
      progress: false,
      failOnError: true,
      devtool: '#source-map',
      module: {
        loaders: [
          {test: /\.js$/, loader: 'babel-loader', query: {presets: ['es2015']}}
        ]
      }
    }
  });

  grunt.registerTask('webpack', [
    'webpack:dev'
  ]);

  grunt.loadNpmTasks('grunt-webpack');
};
```

webpackタスクをcompileAssetsに追加する。これで`sails lift`した時にフロントエンドがコンパイルされるようになります。

```javascript:tasks/register/compileAssets.js
module.exports = function(grunt) {
   grunt.registerTask('compileAssets', [
    'webpack:dev',
     'clean:dev',
    {　以下略　}
```

# jQuery、Bootstrapインストール
npmでjQueryとbootstrap-lessをインストール。
`npm install jquery --save`
`npm install bootstrap-less --save`

sailsはデフォルトでlessのコンパイルタスクを持っているので、このタスクのpathsにbootstrap-lessを追加してbootstrapをimportできるようにします。

```javascript:tasks/config/less.js
module.exports = function(grunt) {

  grunt.config.set('less', {
    dev: {
      files: [{
        expand: true,
        cwd: 'assets/styles/',
        src: ['importer.less'],
        dest: '.tmp/public/styles/',
        ext: '.css'
      }],
      options: {
        paths: ['.', 'node_modules/bootstrap-less']
      }
    }
  });
};
```
importer.lessでimport

```assets/styles/less:importer.less
@import 'bootstrap/index';
```

JSでもbootstrapをimportする。

```javascript:client/web/index.js
import $ from 'jquery';
import 'bootstrap-less';
```
これで`sails lift`してブラウザでアクセスすると、コンソールに「jQuery is undefined」のエラーが出ます。
jqueryをbootstrapが参照できるようにwebpackのpluginsという機能を使います。

```javascript:tasks/config/webpack.js
var webpack = require('webpack');

module.exports = function (grunt) {
  grunt.config.set('webpack', {
    dev: {
      entry: {
        main: './client/web/main.js',
        index: './client/web/index.js'
      },

      /* { 中略 } */

      plugins: [new webpack.ProvidePlugin({$: 'jquery', jQuery: 'jquery'})],

      /* { 中略 } */

    }
  });

  grunt.registerTask('webpack', [
    'webpack:dev'
  ]);

  grunt.loadNpmTasks('grunt-webpack');
};
```
これでwebpackは未定義のjQueryという変数をjqueryモジュールから参照してくれるようになります。

# 感想
webpackすごい便利。