Black Lives Matter

サンプル Gruntfile

このページでは、シンプルなプロジェクトでよくあるニーズに対応するGruntfileの作成手順を説明します。すでにGruntfileの設定方法をご存知で、簡単な例をお探しの方は、こちらをご覧ください。

module.exports = function(grunt) {

  grunt.initConfig({
    jshint: {
      files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
      options: {
        globals: {
          jQuery: true
        }
      }
    },
    watch: {
      files: ['<%= jshint.files %>'],
      tasks: ['jshint']
    }
  });

  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-watch');

  grunt.registerTask('default', ['jshint']);

};

要件

どのプロジェクトにも独自のニーズがありますが、ほとんどのプロジェクトには共通点があります。このガイドでは、基本的な要件を自動化するためのいくつかのGruntプラグインを紹介します。最終的な目標は、これらのGruntプラグインを設定して、プロジェクトで使用できるようにする方法を教えることです。

例として、JavaScriptライブラリを作成しているとしましょう。一般的なフォルダ構成は、srcdist、およびtestというフォルダで構成されています。srcフォルダ(appと呼ばれることもあります)には、ライブラリのソースコードが作成者のように含まれています。distフォルダ(buildと呼ばれることもあります)には、配布物であるソースコードの最小化バージョンが含まれています。最小化されたファイルとは、ソースコードの機能を損なうことなく、スペース、改行、コメントなどの不要な文字がすべて削除されたものです。最小化されたソースコードは、転送する必要があるデータ量を削減するため、プロジェクトのユーザーにとって特に役立ちます。最後に、testフォルダには、プロジェクトをテストするためのコードが含まれています。この設定は、Gruntfile構成を作成する次のセクションで使用されます。

ライブラリを開発し、新しいバージョンをリリースする際には、定期的に実行する必要があるタスクがいくつかあります。たとえば、記述したコードがベストプラクティスに準拠していること、または記述したコードが予期しない動作を引き起こさないことを確認したい場合があります。そのためには、JSHintと呼ばれるツールを使用できます。Gruntには、grunt-contrib-jshintという公式プラグインがあり、この例ではそれを使用します。特に、コードを変更する際に、ルールやベストプラクティスを破らないようにする必要があります。したがって、変更を行うたびにコードをチェックするのが良い戦略です。そのため、grunt-contrib-watchというGruntプラグインを紹介します。後者は、ファイルの追加、変更、削除が行われるたびに、grunt-contrib-jshintなどの定義済みのタスクを実行します。

ソースコードがベストプラクティスに従っていることを確認するだけでは、安定しておりバグが含まれていないことを保証するには不十分です。堅牢なプロジェクトを作成するには、テストする必要があります。QUnitJasmineなど、採用できるライブラリがいくつかあります。このガイドでは、QUnit、特にgrunt-contrib-qunitを構成してコードをテストする方法を説明します。

作品を配布する場合、できるだけ小さいサイズのバージョンを提供したいものです。最小化されたバージョンを作成するには、grunt-contrib-uglifyのようなGruntプラグインが必要です。さらに、開発しているプロジェクトが非常に小さいものでない限り、コードを複数のファイルに分割している可能性が高くなります。これは開発者にとっては良い習慣ですが、ユーザーには1つのファイルのみを含めることをお勧めします。したがって、コードを最小化する前に、ソースファイルを連結して1つのファイルを作成する必要があります。この目標を達成するには、grunt-contrib-concatのようなGruntプラグインが必要です。

要約すると、このガイドでは次の5つのGruntプラグインを使用します。

最終結果がどのように見えるか気になる場合は、ページの下部に完全なGruntfileがあります。

Gruntfileの設定

最初の部分は、Grunt構成をカプセル化する「ラッパー」関数です。

module.exports = function(grunt) {
};

その関数内で、構成オブジェクトを初期化できます。

grunt.initConfig({
});

次に、package.jsonファイルからのプロジェクト設定をpkgプロパティに保存できます。これにより、後で説明するように、package.jsonファイル内のプロパティの値を参照できます。

pkg: grunt.file.readJSON('package.json')

ここまでで、次のようになります。

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json')
  });
};

これで、前述した各タスクの構成を定義できます。プラグインの構成オブジェクトは、構成オブジェクトのプロパティとして存在し、多くの場合、プラグインと同じ名前を共有します。grunt-contrib-concatの構成は、以下に示すように、構成オブジェクトのconcatキーの下に配置されます。

concat: {
  options: {
    // define a string to put between each file in the concatenated output
    separator: ';'
  },
  dist: {
    // the files to concatenate
    src: ['src/**/*.js'],
    // the location of the resulting JS file
    dest: 'dist/<%= pkg.name %>.js'
  }
}

上記のコードスニペットで、JSONファイルにあるnameプロパティを参照していることに注目してください。前にpkgプロパティをpackage.jsonファイルのロード結果として定義し、それがJavaScriptオブジェクトに解析されたため、pkg.nameを使用してアクセスします。Gruntには、構成オブジェクト内のプロパティの値を出力するための簡単なテンプレートエンジンがあります。ここでは、concatタスクに、src/内にあり、.jsで終わるすべてのファイルを連結するように指示します。

次に、JavaScriptコードを最小化するgrunt-contrib-uglifyプラグインを構成しましょう。

uglify: {
  options: {
    // the banner is inserted at the top of the output
    banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
  },
  dist: {
    files: {
      'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
    }
  }
}

このスニペットは、grunt-contrib-uglifyにJavaScriptファイルを最小化した結果を含むファイルをdist/内に作成するように指示します。ここでは、<%= concat.dist.dest %>を使用しているため、uglifyはconcatタスクが生成するファイルを最小化します。

ここまでで、ライブラリの配布バージョンを作成するためのプラグインを構成しました。次に、grunt-contrib-qunitを使用して、コードのテストを自動化します。そのためには、テストランナーファイル(QUnitが実行するHTMLファイル)の場所を指定する必要があります。結果として得られるコードを以下に示します。

qunit: {
  files: ['test/**/*.html']
},

完了したら、プロジェクトのコードがベストプラクティスに準拠していることを確認するための構成を設定します。JSHintは、高い循環的複雑さ、厳密な等価演算子の代わりに等価演算子の使用、未使用の変数と関数の定義など、問題または潜在的な問題を検出できるツールです。

Gruntfileやテストファイルを含む、プロジェクトのすべてのJavaScriptファイルをgrunt-contrib-jshintで分析することをお勧めします。grunt-contrib-jshintの構成例は次のとおりです。

jshint: {
  // define the files to lint
  files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
  // configure JSHint (documented at http://www.jshint.com/docs/)
  options: {
    // more options here if you want to override JSHint defaults
    globals: {
      jQuery: true,
      console: true,
      module: true
    }
  }
}

このプラグインは、ファイルの配列とオプションのオブジェクトを受け取ります。これらはすべてJSHintサイトで文書化されています。プラグインのデフォルトに満足している場合は、Gruntfileで再定義する必要はありません。

構成する必要がある最後のプラグインはgrunt-contrib-watchです。JavaScriptファイルが追加、削除、または変更されるとすぐに、jshintタスクとqunitタスクを実行するために使用します。指定されたファイルのいずれかが変更されたことを検出すると(ここでは、JSHintにチェックするよう指示したものと同じファイルを使用します)、指定したタスクを、表示される順序で実行します。これは、コマンドラインでgrunt watchを使用して実行できます。

上記の記述をgrunt-contrib-watchの構成に変換すると、次のスニペットになります。

watch: {
  files: ['<%= jshint.files %>'],
  tasks: ['jshint', 'qunit']
}

このスニペットで、概要で説明したすべてのプラグインの構成を設定しました。実行する必要がある最後のステップは、必要なGruntプラグインをロードすることです。これらはすべて、npmを使用して事前にインストールしておく必要があります。

grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');

最後に、いくつかのタスクを設定します。これらのタスクの中で最も重要なのはデフォルトタスクです。

// this would be run by typing "grunt test" on the command line
grunt.registerTask('test', ['jshint', 'qunit']);

// the default task can be run just by typing "grunt" on the command line
grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);

デフォルトタスクは、実行するタスクを指定せずにGruntを呼び出した場合(grunt)に実行されます。

結果のGruntfile

このガイドに正しく従った場合は、次のGruntfileが必要になります。

module.exports = function(grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    concat: {
      options: {
        separator: ';'
      },
      dist: {
        src: ['src/**/*.js'],
        dest: 'dist/<%= pkg.name %>.js'
      }
    },
    uglify: {
      options: {
        banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
      },
      dist: {
        files: {
          'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
        }
      }
    },
    qunit: {
      files: ['test/**/*.html']
    },
    jshint: {
      files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
      options: {
        // options here to override JSHint defaults
        globals: {
          jQuery: true,
          console: true,
          module: true,
          document: true
        }
      }
    },
    watch: {
      files: ['<%= jshint.files %>'],
      tasks: ['jshint', 'qunit']
    }
  });

  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-qunit');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-concat');

  grunt.registerTask('test', ['jshint', 'qunit']);

  grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);

};