grunt-sprockets で複数の CoffeeScript を一つの無名関数内にまとめる


以前書いた

[Grunt で複数の CoffeeScript を一つの無名関数内にまとめる](http://qiita.com/akishin/items/8454786dc12e534531be "Grunt で複数の CoffeeScript を一つの無名関数内にまとめる") 

[browserify で複数の CoffeeScript を一つにまとめる](http://qiita.com/akishin/items/e2b84ab8a7ace903b738 "browserify で複数の CoffeeScript を一つにまとめる") 

の続き。

今回は以下の記事で ysugimoto さんが作成された grunt-sprockets を使って CoffeeScript を一つの無名関数内にまとめてみる。

grunt-sprocketsタスクプラグイン作成とテストとTravis CIまでやってみる | ブログ :: Web notes.log
http://blog.wnotes.net/blog/article/create-grunt-task-plugin-and-testing-travis-ci

GitHub のリポジトリは以下にある。

ysugimoto/grunt-sprockets
https://github.com/ysugimoto/grunt-sprockets

## 準備

ディレクトリを作成して grunt-sprockets をインストール。
Coffee からコンパイルしたかったので grunt-contrib-coffee も入れている。

```shell-session
% mkdir -p sprockets-test/{src/{js,coffee},dest} && cd sprockets-test
% npm init
% npm install grunt grunt-contrib-coffee grunt-sprockets --save-dev
```

これでディレクトリ構造は以下のようになる。

```shell-session
sprockets-test
 ├── dest
 ├── node_modules
 │   ├── grunt
 │   ├── grunt-contrib-coffee
 │   └── grunt-sprockets
 ├── package.json
 └── src
     ├── coffee
     └── js
```

## スクリプトの作成

以下のスクリプトを作成。

```coffee:sprockets-test/src/coffee/foo.coffee
class Foo
  name: ->
    'Foo'
```

```coffee:sprockets-test/src/coffee/bar.coffee
class Bar
  name: ->
    'Bar'
```

```javascript:sprockets-test/src/js/main.js
(function() {
//= require foo.js
//= require bar.js

  var foo = new Foo();
  var bar = new Bar();

  console.log(foo.name());
  console.log(bar.name());
}).call(this);
```

ここで気をつけなくてはいけないのだが、sprockets のディレクティブ( `//= require ～` )がインデントされていると上手く動作しない。

node_modules/grunt-sprockets/tasks/sprockets.js 内でディレクティブを抜き出している正規表現が `/^\/\/=\srequire(_tree)?(?:\s+)?(.+)$/mg` と行頭から指定されている事によるっぽいので、この辺弄ればどうにか出来そうではある。

ただ、元々 Rails で使う時は確かにディレクティブは行頭開始なのでそういう仕様という事かもしれない。

少なくとも確認時点のバージョン 0.7.0 の grunt-sprockets では行頭から開始していないと動作しなかった。

## Gruntfile.js

Gruntfile.js は以下のようにした。

```javascript:sprockets-test/Gruntfile.js
module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        coffee: {
            compile: {
                options: {
                    bare: true,
                },
                files: [{
                    expand: true,
                    cwd: 'src/coffee',
                    src: ['*.coffee'],
                    dest: 'src/js/',
                    ext: '.js',
                }]
            }
        },
        sprockets: {
            main: {
                files: [
                   'src/js/main.js',
                ],
                dest: 'dest/main.js',
            },
        },
    });
    grunt.loadNpmTasks('grunt-contrib-coffee');
    grunt.loadNpmTasks('grunt-sprockets');
    grunt.registerTask('default', ['coffee', 'sprockets']);
};
```

sprockets タスクの設定はほとんど concat と同じ。
coffee 側で `bare: true` を指定して個々のスクリプトを無名関数でラップしないようにしておく必要があるのも同じ。

## 実行

用意ができたので grunt を実行してみる。

```shell-session
% grunt
Running "coffee:compile" (coffee) task
>> 2 files created.

Running "sprockets:main" (sprockets) task
[Processing]: "src/js/main.js" ...
[Processing]: "src/js/foo.js" ...
[Processing]: "src/js/bar.js" ...
Build file: dest/main.js ...

Done, without errors.
```

生成された dest/main.js は以下のような内容。

```javascript:sprockets-test/dest/main.js
(function() {
var Foo;

Foo = (function() {
  function Foo() {}

  Foo.prototype.name = function() {
    return 'Foo';
  };

  return Foo;

})();

var Bar;

Bar = (function() {
  function Bar() {}

  Bar.prototype.name = function() {
    return 'Bar';
  };

  return Bar;

})();


  var foo = new Foo();
  var bar = new Bar();

  console.log(foo.name());
  console.log(bar.name());
}).call(this);
```

require ディレクティブで指定した位置にスクリプトが挿入されている事が判る。
実行してみる。

```shell-session
% node dest/main.js 
Foo
Bar
```

問題なく動作した。

Rails で見慣れている Sprockets のディレクティブがそのまま使えるのは馴染みやすくてやはり嬉しい。

concat とほぼ同じような挙動をするが、個人的には concat で結合順を Gruntfile.js 内で指定するよりは、ソースコード内で依存関係を明示出来る grunt-sprockets の方が良いんじゃないかと思った。