Gruntを使ってJSやCSSの面倒なタスクを自動化しよう

もうお年玉はもらえないのでしょうか?こんにちわ、kudoxです。今更ですがGruntを導入してみましたので、使い方をレポートしたいと思います。最近では、Webサイト表示の高速化のため、JavaScriptやCSSの圧縮・結合といった作業が当たり前のようになってきましたが、これらの作業は何度も繰り返す可能性があり、非常に面倒なタスクです。Gruntを使うと、これらの面倒なタスクを自動で処理できるようになります。コマンドラインを使うため、難しいと思われるかもしれませんが、多少のJavaScriptの知識があれば簡単に導入することができます。

Gruntを使うための事前準備

まず、Gruntを使うには、Node.jsとパッケージ管理ツールnpmが必要になるのでインストールしましょう。私は、Macportsを使ってインストールしましたが、申し訳ないほど簡単です。既にインストールしているか分からないって方は、ターミナルで下記コマンドを実行してバージョン番号が返ってくればインストールされています。

ターミナル

node -v
npm -v

Node.jsとnpmのインストール

まだNode.jsとnpmをインストールしていない方は、ターミナルで下記コマンドを実行して、Node.jsとnpmをインストールしましょう。

ターミナル

sudo port install nodejs
sudo port install npm

grunt-cliのインストール

Node.jsとnpmをインストールしたら、ターミナルで下記コマンドを実行して、Gruntのコマンドラインインターフェイス grunt-cliをインストールします。

ターミナル

sudo npm install -g grunt-cli

package.jsonの作成

Gruntを使うには、プロジェクトのディレクトリにpackage.jsonとGruntfile.jsが必要になります。今回はデスクトップに置いたgrunt-testディレクトリをプロジェクトのルートディレクトリとして、ここにpackage.jsonファイルを作成してみます。

grunt-testディレクトリの構成

grunt-testディレクトリの構成

まず、ターミナルでcdコマンドを実行して、デスクトップのgrunt-testディレクトリをカレントディレクトリにします。続いて、npm initを実行します。

ターミナル

cd ~/Desktop/grunt-test
npm init

すると、ターミナルで下記項目を順番に質問されるので、答えを入力してreturnキーを押しましょう。基本的に各項目には初期値が用意されているので、entry point以外の項目で必要ないと思うものは、そのままreturnキーを押しても構いません。最後に Is this ok? と聞かれるので、returnキーを押すとカレントディレクトリにpackage.jsonが作成されます。

name:
プロジェクト名を入力します。URLセーフな文字列しか使用できません。
version:
プロジェクトのバージョンを入力します。
description:
プロジェクトの簡単な説明文を入力します。
entry point:
迷わず、Gruntfile.jsと入力します。
test command:
そのままreturnキーを押します。
git repository:
Gitレポジトリがあれば入力します。
keywords:
キーワードがあれば、カンマ区切りで入力します。
author:
製作者名を入力します。
license:
ライセンスを入力します。

この時点でpackage.jsonの中身を覗いてみると、こんな感じになっています。

package.json

{
  "name": "grunt-test",
  "version": "0.1.0",
  "description": "This is test.",
  "main": "Gruntfile.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "@u_kudox",
  "license": "public domain"
}

Gruntとプラグインのインストール

それでは、いよいよGruntとプラグインのインストールです。grunt-cliのインストールは最初の1回だけですが、Gruntとプラグインはプロジェクト毎にインストールする必要があります。

Gruntのインストール

ターミナルで下記コマンドを実行すると、プロジェクトのルートディレクトリにnode_modulesディレクトリが作成され、そこにGruntがインストールされます。–save-devオプションを付けることにより、package.jsonに必要な情報を追加してくれます。

ターミナル

cd ~/Desktop/grunt-test
npm install grunt --save-dev

プラグインのインストール

続いて、Gruntのプラグインをインストールしましょう。npm install プラグイン名 –save-dev コマンドで、指定したプラグインがnode_modulesディレクトリにインストールされます。

ターミナル

npm install grunt-contrib-cssmin --save-dev
npm install grunt-contrib-uglify --save-dev
npm install grunt-contrib-copy --save-dev
npm install grunt-contrib-watch --save-dev
npm install grunt-contrib-yuidoc --save-dev
npm install grunt-contrib-compress --save-dev

今回は、下記のプラグインだけをインストールしましたが、Gruntには数えきれない程のプラグインがあります。興味のある方は、こちらで探してみて下さい。

grunt-contrib-cssmin
CSSファイルを圧縮する。
grunt-contrib-uglify
JavaScriptファイルを圧縮する。
grunt-contrib-copy
ファイルやディレクトリをコピーする。
grunt-contrib-watch
ファイルの変更を監視してタスクをトリガーする。
grunt-contrib-yuidoc
YUIDocを書き出す。
grunt-contrib-compress
ファイルやディレクトリをzip/gzipなどの形式で圧縮する。

Gruntfile.jsの作成

これでGruntを使用する準備が整いましたが、実際にGruntを使用するには、プロジェクトのディレクトリにGruntfile.jsを作成し、タスクの設定やプラグインのロードを指定する必要があります。まず、Gruntfile.jsの基本的な書式を見てみましょう。

すべてのGrunt用のコードは、module.exportsというラッパー関数の内部に記述します。そして、grunt.initConfigで各タスクの設定をJSON形式で指定し、grunt.loadNpmTasksでプラグインのロードを指定します。

Gruntfile.jsの基本的な書式

module.exports = function(grunt) {
  grunt.initConfig({
    // タスクの設定
  });
  // プラグインのロード
  grunt.loadNpmTasks('プラグイン名');
};

タスクの設定とGruntの実行

それでは、Gruntfile.jsでタスクを設定し、実際にGruntを実行してみましょう。ターミナルでgruntコマンドを実行する際は、cdコマンドでGruntfile.jsを置いたディレクトリをカレントディレクトリにしてから、実行して下さい。

CSSの圧縮

CSSの圧縮は、grunt-contrib-cssminプラグインで行います。grunt.initConfigにcssminプロパティを追加し、ターゲットを指定します。srcは圧縮前の元ファイル、destは圧縮・結合後のファイルになります。下記の例では、pc, mobileというプロパティ名でターゲットを指定していますが、この名前は任意の文字列で構いません。

Gruntfile.js

grunt.initConfig({
  cssmin : {
    pc : {
      src : ['css/reset.css', 'css/styles.css'],
      dest : 'css/pc.min.css'
    },
    mobile : {
      src : ['css/reset.css', 'css/styles.css', 'css/mediaqueries.css'],
      dest : 'css/mobile.min.css'
    }
  }
});
grunt.loadNpmTasks('grunt-contrib-cssmin');

タスクを実行するには、ターミナルでgrunt cssminを実行します。また、grunt cssmin:mobileのようにすることで、実行するターゲットを指定することもできます。

ターミナル

grunt cssmin

JavaScriptの圧縮

JavaScriptの圧縮は、grunt-contrib-uglifyプラグインで行います。grunt.initConfigにuglifyプロパティを追加し、ターゲットを指定します。下記の例では、オプションにbannerを指定し、ファイルの先頭にライセンス表記のコメントを追加しています。

Gruntfile.js

grunt.initConfig({
  uglify : {
    build : {
      options : {
        banner : grunt.file.read('js/License.js'),
      },
      src : ['js/BitmapData.js', 'js/GlowFilter.js'],
      dest : 'js/all.min.js'
    }
  }
});
grunt.loadNpmTasks('grunt-contrib-uglify');

タスクを実行するには、ターミナルでgrunt uglifyを実行します。grunt uglify:ターゲット名で実行するターゲットを指定することもできます。

ターミナル

grunt uglify

ファイルやディレクトリのコピー

grunt-contrib-copyプラグインを使うとファイルやディレクトリをコピーすることができます。grunt.initConfigにcopyプロパティを追加し、ターゲットを指定します。下記の例では、cssディレクトリ内の’.min.css’で終わるファイルをsample/cssディレクトリに、jsディレクトリ内の’.min.js’で終わるファイルをsample/jsディレクトリにコピーするようにしています。

Gruntfile.js

grunt.initConfig({
  copy : {
    css : {
      files : [{expand:true, cwd:'css/', src:'*.min.css', dest:'sample/css/'}]
    },
    js : {
      files : [{expand:true, cwd:'js/', src:'*.min.js', dest:'sample/js/'}]
    }
  }
});
grunt.loadNpmTasks('grunt-contrib-copy');

タスクを実行するには、ターミナルでgrunt copyを実行します。grunt copy:ターゲット名で実行するターゲットを指定することもできます。

ターミナル

grunt copy

ファイルの変更を監視

grunt-contrib-watchプラグインを使うとファイルの変更を監視して、ファイルが変更された際に登録したタスクを実行することができます。grunt.initConfigにwatchプロパティを追加し、filesで監視するファイル、tasksでファイルが変更された際に実行するタスクを指定します。

Gruntfile.js

grunt.initConfig({
  watch : {
    css_pc : {
      files : ['css/reset.css', 'css/styles.css'],
      tasks : ['cssmin', 'copy:css']
    },
    css_mobile : {
      files : ['css/mediaqueries.css'],
      tasks : ['cssmin:mobile', 'copy:css']
    },
    js : {
      files : ['js/BitmapData.js', 'js/GlowFilter.js', 'js/License.js'],
      tasks : ['uglify', 'copy:js']
    }
  }
});
grunt.loadNpmTasks('grunt-contrib-watch');

ファイルの監視を開始するには、ターミナルでgrunt watchを実行します。grunt watch:ターゲット名で監視するターゲットを指定することもできます。監視を終了するには、ターミナルでcontrolキーを押しながらcキーをタイプします。

ターミナル

grunt watch

YUIDocの書き出し

grunt-contrib-yuidocプラグインを使うとGruntでYUIDocを書き出すことができます。下記の例では、grunt.file.readJSONでpackage.jsonを読み込み、その値をYUIDocのパラメータに利用しています。実行するとdocsディレクトリにYUIDocのドキュメントが書き出されます。

Gruntfile.js

grunt.initConfig({
  pkg: grunt.file.readJSON('package.json'),
  yuidoc : {
    compile : {
      name : '<%= pkg.name %>',
      description : '<%= pkg.description %>',
      version : '<%= pkg.version %>',
      options : {
        paths : 'js',
        outdir : 'docs'
      }
    }
  }
});
grunt.loadNpmTasks('grunt-contrib-yuidoc');

YUIDocを書き出すには、ターミナルでgrunt yuidocを実行します。grunt yuidoc:ターゲット名で、実行するターゲットを指定することもできます。

ターミナル

grunt yuidoc

ファイル・ディレクトリの圧縮

grunt-contrib-compressプラグインを使うとファイルやディレクトリをzipなどの形式で圧縮することができます。下記の例では、先程docsディレクトリに書き出したYUIDocのドキュメント全体を1つのzipファイルに圧縮してoutputディレクトリに保存しています。

Gruntfile.js

grunt.initConfig({
  compress : {
    docs : {
      options : {
        archive : 'output/docs.zip'
      },
      files : [{expand:true, src:'**', cwd:'docs'}]
    }
  }
});
grunt.loadNpmTasks('grunt-contrib-compress');

圧縮を実行するには、ターミナルでgrunt compressを実行します。grunt compress:ターゲット名で、実行するターゲットを指定することもできます。

ターミナル

grunt compress

カスタムタスク

grunt.registerTaskを使うと複数のタスクを組み合わせたり、オリジナルのタスクを登録することができます。下記の例では、先程の例で示したYUIDocの作成・ドキュメントのzip化という一連のタスクをdocsという名前で登録しています。

Gruntfile.js

grunt.registerTask('docs', ['yuidoc:compile', 'compress:docs']);

タスクを実行するには、ターミナルでgrunt ‘タスク名’ を実行します。

ターミナル

grunt docs

Gruntfile.jsのサンプル

最後に今回のテストで作成したGruntfile.jsをサンプルとして載せておきます。初めてGruntを使ってみた感想としては、こんなに便利ならもっと早く導入してれば良かったと後悔しています。まだGruntを試されていない方は、これを期にGruntを導入されてみては如何でしょうか? 以上、Gruntの使い方レポートでした。

Gruntfile.js

module.exports = function(grunt) {
  grunt.initConfig({
    pkg : grunt.file.readJSON('package.json'),
    cssmin : {
      pc : {
        src : ['css/reset.css', 'css/styles.css'],
        dest : 'css/pc.min.css'
      },
      mobile : {
        src : ['css/reset.css', 'css/styles.css', 'css/mediaqueries.css'],
        dest : 'css/mobile.min.css'
      }
    },
    uglify : {
      build : {
        options : {
          banner : grunt.file.read('js/License.js'),
        },
        src : ['js/BitmapData.js', 'js/GlowFilter.js'],
        dest : 'js/all.min.js'
      }
    },
    copy : {
      css : {
        files : [{expand:true, cwd:'css/', src:'*.min.css', dest:'sample/css/'}]
      },
      js : {
        files : [{expand:true, cwd:'js/', src:'*.min.js', dest:'sample/js/'}]
      }
    },
    watch : {
      css_pc : {
        files : ['css/reset.css', 'css/styles.css'],
        tasks : ['cssmin', 'copy:css']
      },
      css_mobile : {
        files : ['css/mediaqueries.css'],
        tasks : ['cssmin:mobile', 'copy:css']
      },
      js : {
        files : ['js/BitmapData.js', 'js/GlowFilter.js', 'js/License.js'],
        tasks : ['uglify', 'copy:js']
      }
    },
    yuidoc : {
      compile : {
        name : '<%= pkg.name %>',
        description : '<%= pkg.description %>',
        version : '<%= pkg.version %>',
        options : {
          paths : 'js',
          outdir : 'docs'
        }
      }
    },
    compress : {
      docs : {
        options : {
          archive : 'output/docs.zip'
        },
        files : [{expand:true, src:'**', cwd:'docs'}]
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-yuidoc');
  grunt.loadNpmTasks('grunt-contrib-compress');

  grunt.registerTask('docs', ['yuidoc:compile', 'compress:docs']);
};