2013/04/23更新

[grunt] Gruntfile.jsをチーム共有用と個人用にファイル分割して、gitやsvnのコンフリクトを減らす方法

このエントリーをはてなブックマークに追加            

こんにちは、@yoheiMuneです。
本日はチームでgruntJSを使った開発を行う際に、よく困るGruntファイルのコンフリクト。 チーム共有のGruntfile.jsと、個人が自由に利用するGruntfile.jsを別々のファイルに分割して管理してコンフリクトを減らす方法を、ブログに書きたいと思います。

画像



GruntFile.jsをチームで共有するとファイルのコンフリクトが大変

GruntJSは、JSのミニファイが出来たりCSSプリプロセッサのコンパイルが出来たりと便利なのですが、数人のチームで開発していると、個人毎にGruntfile.jsをカスタマイズしたくなることが多いです。
例えばある人は、ファイルサーバーへアップロードするためのsftpやrsync用のタスクを追加したり、またある人は特定の機能のみをコンパイルするタスクを作ったりと。
そーなってくると個人で利用したい最適なGruntFile.jsは人それぞれになってしまい、Gruntfileが個人毎にバラバラになってしまいます:)

しかし個人毎の管理していると、プロジェクト内で共通でやれば良いようなタスクも個人で定義してしまい開発効率も悪いし、ノウハウの共有がされづらくなるし。。
ということで、今回はプロジェクト共通のGruntfile.jsと個人用のGruntfile.jsをそれぞれファイル分割しつつ、利用するときはまるで1つのGruntfileであるかのように利用する方法をブログで書こうと思います。



プロジェクト共通のGruntfile.jsと個人のgruntfile.js

今回は、こんな感じの作りにしてみました。
画像
プロジェクト共通のGruntfile.jsが、個人のGruntfile.jsを読み込む感じです。
それではさっそく実現するための実装を記載していきます!



まずは下準備

今回の機能を実現するために、grunt.config()の設定を、各個人のGruntfile.jsで拡張するような実装にしています。 JavaScriptオブジェクトの拡張に今回はundescore.jsを利用するので、underscore.jsをnodeモジュールとしてインストールしておきます。
npm install underscore --save-dev
また、どのGruntfile.jsを共有のGruntfile.jsから読み込むのかを設定ファイルに記載する仕組みにしました。 setting.jsonという名前で以下の内容をファイルに記載しました。
{
  "whoYouAre": "munesada"
}
これで下準備は完了です。



プロジェクト共有のGruntfile.jsを作成する

続いてプロジェクトメンバーで共有するGruntfile.jsを作成します。
このGruntfileには、プロジェクト全体のビルドタスクや、jsLintなどのプロジェクト全体で利用するタスクを記述します。
また、アニメーション作成用のGruntタスクなど、特定の仁か使わないけど、他の人にシェアしたい内容も記載しても良いかもしれません。
以下のように、通常のGruntfile.jsの記述と、setting.jsonの内容をもとに個人のGrunt設定を読み込む実装を行います。
module.exports = function(grunt) {
  
  // 共通で利用するnpm形式のgruntタスクを読み込む
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-compass');

  // プロジェクト全体で利用するconfigを設定
  grunt.initConfig({

    // 設定ファイルを読み込みます
    pkg: grunt.file.readJSON('setting.json'),

    // 例えばプロジェクト共有で利用するJSの結合タスク
    concat: {
      js : {
        src : ['page/js/common/**/*.js'],
        dest : './publish/js/common/main.js'
      },
      jsMypage : {
        src : ['page/js/orgn/mypage/*.js'],
        dest : './publish/js/orgn/mypage.js'
      },
      /* 他にも必要なConcatタスクを定義する */
    },

    // 例えばプロジェクト全体のScssファイルのコンパイルタスク
    compass: {
      options : {
        imagesDir: '../publish/images',
        time: true
      },
      cmn : {
        options: {
          sassDir: 'page/css/common/',
          cssDir: './publish/css/common/'
        }
      },
      orgn : {
        options: {
          sassDir: 'page/css/orgn/',
          cssDir: './publish/css/orgn/'
        }
      }
    }
  });

  // プロジェクト全体をビルドするためのタスクを定義します。
  grunt.registerTask('build', ['concat', 'compass']);
  grunt.registerTask('buildCommonJS', ['concat:js']);
  grunt.registerTask('buildCommonCSS', ['compass:cmn']);


  // ここからが拡張している部分。
  // gruntのconfigを拡張するための仕組みを追加する
  grunt.extendConfig = function(additionalConfig) {

    var config = grunt.config();

    for (prop in additionalConfig) {
      if (additionalConfig.hasOwnProperty(prop)) {

        // gruntのconfigに既に含まれる場合
        if (config[prop]) {
          config[prop] = _.extend(config[prop], additionalConfig[prop]);

        } else {
          // gruntのconfigに存在しない場合
          var tmpConfig = {};
          tmpConfig[prop] = additionalConfig[prop]
          config = _.extend(config, tmpConfig);
        }
      }
    }

    grunt.initConfig(config);
  };


  // 設定内容に合わせて、各メンバーのGruntfile.jsを追加で読み込み、
  // configの拡張やタスクの定義の追加を行う。
  var individual = grunt.config().pkg.whoYouAre;
  switch(individual) {

    case 'munesada':
      var individual = require('./individual/munesada_grunt.js');
      individual.extendGrantTask(grunt);
      break;

    case 'yamada':
      var individual = require('./individual/yamada_grunt.js');
      individual.extendGrantTask(grunt);
      break;

    case 'suzuki':
      var individual = require('./individual/suzuki_grunt.js');
      individual.extendGrantTask(grunt);
      break;

    default:
      break;
  }
}
これでプロジェクト共通のGruntfile.jsが完成です。個人のGruntfileの内容を分離することで、Gruntfile.jsの更新回数が減り、バージョン管理でのコンフリクト解消が減り、ストレス少なく快適に開発が出来るようになります。



各人のgruntfile.jsを作成する

続いて個人のGruntfileの書き方です。上記のプロジェクト共有のGruntfile.jsで読み込んでいる、例えば「munesada_grunt.js」は以下の内容を記載します。
「個人のGruntfile」という表現をしていますが、中身はgruntfileの書き方というよりは、node.jsの記述となります。
(function() {

  // underscoreのモジュールをインポートする。
  var _ = require('underscore');

  // 共有のGruntfile.jsから実行される関数を定義する。
  // 引数でgruntへの参照を受け取る。
  module.exports.extendGrantTask = function(grunt) {

    // 親で定義したgruntタスクを取得する
    var config = grunt.config();

    // 必要なnpmタスクは追加で読み込む
    grunt.loadNpmTasks('grunt-compass-multiple');

    // configを拡張する。
    grunt.extendConfig({

        // 親で定義されていないタスクを追加します
        compassMultiple: {
          options : {
                imagesDir: '../publish/images',
                time: true,
          },
            all: {
              options: {
                multiple: [
                  {
                    // common
                    sassDir: 'page/css/common',
                    cssDir: '../publish/css/common/'
                  },{
                    // orginal
                    sassDir: 'page/css/orgn/scss',
                    cssDir: './publish/css/orgn/'
                  }
                ]
              }
            },
        },

        // 親に既に定義されているタスクもここで拡張できる
        compass: {
          // 例えばmypage用を取り出して開発したい場合とか、
          // 個別に定義するとビルド時間も短くなり便利です。
          mypage : {
            options: {
              sassDir: 'page/css/orgn/mypage',
              cssDir: './publish/css/orgn/'
            }
          }
        }
    });

    // 独自にタスクを定義したい場合には、定義する。
    grunt.registerTask('mune1', ['compass:mypage']);
  };
})();
という感じです。gruntのconfigを拡張したり、registerTaskで自分用のタスクセットを定義することで、 個人により最適化されたビルド環境を準備することが出来ます。

こんな感じで各人固有のGruntfileを作成することで、プロジェクト共有のものと個人特有のものを別ファイルにすることが出来ます。



最後に

チームで開発をしていると、Gruntfile.jsにプロジェクト全体のコンパイルを書くことはもちろんのこと、 各個人ごとのスキルや仕事内容に合わせてカスタマイズしたいもの。
今のチームでも各人が最適化されたGruntfile.jsを持ち、 プロジェクト全体用のGruntfileはメンテナンスされ無いようになってしまい、プロジェクト全体をクリーンビルドするタスクを再度定義する必要があり面倒でした。
ノウハウ共有のためにも、Gruntfileの最新化を行うモチベーションのためにも、各人のGruntファイルをシェアできる環境が出来るといいなと思い、今回のブログに至りました。

最後までご覧頂きましてありがとうございました。





こんな記事もいかがですか?

[JS] Compass(Scss)のビルドが遅いので、マルチスレッドで高速でCompassビルドするgruntタスクを作ってみました
[grunt] gruntを使ってSFTPをファイル保存時に自動で行う
[grunt] ファイル保存したタイミングで、ブラウザを自動的にリロードして、確認作業をスピードアップさせよう
[grunt] Gruntfile.jsをチーム共有用と個人用にファイル分割して、gitやsvnのコンフリクトを減らす方法
[grunt] Compass(Sass)のマルチスレッドコンパイルができるgrunt-compass-multipleに、ファイルの個別指定オプションが追加
[gruntJS] gruntJSでタスクを実行する際に、引数で処理対象のファイルを変更する
[GruntJS] gruntからJenkinsのジョブを実行する
RSS画像

もしご興味をお持ち頂けましたら、ぜひRSSへの登録をお願い致します。