datchの日記

気がついたら社会人。気になる技術的なことについて少しずつ書いていけたらと思っております。

Gruntを触ってみた

どうも、昨晩借りているウィークリーマンションに人が押し寄せてしまい、ブログの投稿が遅れてしまいました。
インターン生とかを見ているとエンジニアの技術力が年々上昇しているのをひしひしと感じています。

Gruntとは?



Javascriptビルド自動化ツール
Linux上でC、C++で開発を行っている人は Make と言えば分かりやすいかな。 *1
上記のBowerはパッケージ管理ツールであり、依存しているパッケージを適時ダウンロードしてくれる。
Gruntはひとつ上のレイヤーあり、Node.jsでのコンパイルやテストの実行、最小化などをGruntfileに記述することで自動化を行うことが出来る。

Gruntのインストール



既に前回のbowerでNode.jsとnpmのインストールの説明をしているので、npmに関しては省略。
こちらもnpmを使ってインストールを行う。

$ npm install -g grunt-cli
$ grunt
grunt-cli: The grunt command line interface. (v0.1.13)
***

これでターミナル上でgruntが叩けるようになった。
実はGruntは grunt-cli とは別に grunt をプロジェクトのフィルダ毎にインストールする必要がある。
これはgruntのバージョンが変わっても、古いバージョンのGruntを使うことで、プロジェクトが正常にビルド出来るようにするためだ。

$ npm init
色々と聞かれるが何も入力せず、Enter連打で大丈夫です
$ npm install --save-dev grunt

補足だが -g というオプションはグローバルインストールのことを示し、ディレクトリを移動してもパッケージが使用できる。
オプションがない場合はフォルダに node_modules というフォルダが作成され、そのディレクトリのみで有効になる。
コマンドライン上で使用しない限りはグローバルインストールは避けた方がいいだろう。

Gruntのチュートリアル



導入も済んだところで早速Gruntの設定をしていきましょう。
といっても、Gruntを使用するとき、主に編集するのはGruntfile.jsというファイルだ。
Gruntfile.js は自動的に作成されないので自分で作成しよう。

今回、以下の様な文面で作成した。

module.exports = function(grunt) {
  //Gruntの設定
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    /// @ref https://github.com/gruntjs/grunt-contrib-coffee
    coffee: {
      compile: {
        // コンパイル対象のファイルを設定
        files: {
          'src/<%= pkg.name %>.js':'src/<%= pkg.name %>.coffee' // 1対1
        }
      }
    },
    uglify: {
      options: {
        banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */'
      },
      build: {
        src: 'src/<%= pkg.name %>.js',
        dest: 'build/<%= pkg.name %>.min.js'
      }
    }
  });

  // Gruntと特定のパッケージを結びつける、プラグインを読み込む
  grunt.loadNpmTasks('grunt-contrib-coffee');
  grunt.loadNpmTasks('grunt-contrib-uglify');

  // logタスクの定義
  grunt.registerTask('log', 'Log some stuff.', function() {
    //ログメッセージの出力
    grunt.log.write('Logging some stuff...').ok();
  });

  // defaultタスクの定義
  grunt.registerTask('default', ['coffee', 'uglify']);

  grunt.registerTask('my_uglify', ['uglify']);
};

いきなり変なJavaScriptの中身を見せつけられて、頭の中が???となっている人も多いだろう。
細かい単位で説明して行く。

grunt.initConfig
//Gruntの設定
grunt.initConfig({
  pkg: grunt.file.readJSON('package.json'),
  ***:{
  }
});

ここらへんは決まり文句だ。
npmの設定ファイルであるpackage.jsonを読み込んで、pkgを指定することで連想配列としてアクセス出来るようにしている。
読み込むJSONファイルのを増やす場合は別で指定すると良いだろう。
重要なのは***の部分だ。
こちらはgrunt側で登録したいタスクを指定する。
今回登録しているタスクは2つだ。

  1. coffee : CoffeeScriptコンパイル
  2. uglify : ファイルの圧縮

ここらへんの書き方はプラグインが置いてあるGithubのREADMEに書いてあるので、そちらを参考にした方が早いだろう。
gruntjs/grunt-contrib-coffee · GitHub
gruntjs/grunt-contrib-uglify · GitHub

grunt.loadNpmTasks

次にプラグインの読み込みだ。
ここで読み込みを行わなければタスクの設定をしてもエラーが出る。

grunt.loadNpmTasks('grunt-contrib-coffee');
grunt.loadNpmTasks('grunt-contrib-uglify');

Gruntからパッケージを呼び出すには間にプラグインを咬ませる必要がある。
プラグインなので、もちろんパッケージ本体も必要だ。
パッケージをインストールしてない場合は npm でインストールを行おう。

grunt.registerTask

最後にタスクの定義だ。
今回は log, default, my_uglifyという3つのタスクを定義している。
ちなみにわざわざ"my_uglify"と定義したのには理由があり、"uglify"だとエラーが出たためだ。
以下がその問題について触れている記事。
Recursive process.nextTick detected. · Issue #1154 · gruntjs/grunt · GitHub

定義された処理をコマンドで叩くと呼ばれる。

$ grunt log
ログの出力
$ grunt my_uglify
ファイルの圧縮のみ
$ grunt default
$ grunt # 何も入力しなくても default が呼ばれる
CoffeeScriptのコンパイル後、出力されたファイルを圧縮

という処理になっている。

試しに動かす

それでは、試しに動かしてみよう。
まずは、package.jsonの中身を覗いて pkg.name がどのような値になるか確認する。

{
  "name": "test_grunt",
  "version": "0.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "grunt": "^0.4.5",
    "grunt-contrib-coffee": "^0.11.1",
    "grunt-contrib-uglify": "^0.5.1",
    "uglify-js": "^2.4.15"
  }
}

これで "pkg.name" は "test_grunt" と変換されることがわかった。

このGruntは src/test_grunt.coffee をコンパイルし、src//test_grunt.jsに出力。
src//test_grunt.jsを圧縮して、build/test_grunt.min.jsに出力して処理が終わる。

CoffeeScriptコンパイル -> 最小化

これだけだ。
では、以下のソースをgruntで処理する。

a = 1;
b = 2;

logOut: (c, d)->
  console.log(c + d)

logOut a, b

するとbuild/test_grunt.min.jsが以下のように生成されている。

(function(){var a,b;a=1,b=2,logOut(a,b)}).call(this);

以上がGruntについてのチュートリアル

Gruntを使ってみて



Makeとは違いファイルにコマンドをベタ書きする必要がなく、オプションを指定するだけ呼び出せるのは非常にありがたい。
ただ、プラグインをわざわざnpmでインストールしなければいけないのは少し手間に感じた。
今のところ、Javascriptで大規模なプロジェクトを扱っていないので使ったことも無く、有り難みもわかないが将来的にはお世話になりそうなだ。

*1:ちなみに当方VisualStudioで開発を行っているのでMakeが全然書けない雑魚です