CSS と JavaScript を圧縮する Gulp タスク ~ npm-scripts から Gulp に戻ってしまった ~

このはてなブログの CSS・JS ビルドに関して。

これまで、CSS の圧縮は clean-css-cli を使っていて、package.json に npm-scripts を書き、npm-run-all で一括実行できるようにしていたが、結局 Gulp に戻ってしまった。

Uglify を導入して、JavaScript の圧縮をしようとしたところで、なんとなく package.json にワンライナーを列挙するのがゴチャつく気がして、npm-scripts の運用を止めてしまった。better-npm-run というパッケージを使うともう少し package.json 内での可読性も上がるのかもしれないが、なんだか結局 gulpfile.js に書いていたモノを package.json に移し替えているだけな気がしてきて、なんだかなーと。

直接 npm パッケージを叩くよりも、Gulp のラッパープラグインを間に挟むことで、「ラッパープラグインだけ保守されなくなったらどうしよう」とかいう心配はあるものの、1度構築した環境はバージョンを上げずに残しておけば最悪良いワケだし、フロントエンド界隈で今のところ「この技術でもうオッケー、これからは枯れていくだけ!」という状態のモノは何もないので、色々と諦めることにした。気持ち的には JSON ファイルに「処理」が書かれるのは違和感があり、JSON ファイルは「定義」だけにしたい感じがあるので、Gulp に戻った。

つーわけで Gulp の用意

というワケで、npm-scripts を使っていたリポジトリで Gulp を使うための準備をしていく。

まずはコマンドラインで使っていたパッケージ群が不要になるので消してしまう。

$ npm uninstall clean-css-cli npm-run-all rimraf -D

次に、今回使うパッケージ群をインストールする。

$ npm install gulp gulp-load-plugins gulp-plumber gulp-clean-css gulp-uglify -D

導入しているプラグインは以下のとおり。

ググっていた中で gulp-minify-css とか gulp-uglifyjs といったプラグイン名も見かけたが、npm で確認するとどちらも Deprecated (廃止) になっていた。使うプラグインはちゃんと公式の API を確認しておこう。

gulpfile.js を書く

次にリポジトリ配下に gulpfile.js を作り、以下のように書く。今回は gulp-babel などを入れていないので、普通の JavaScript (ES5) の書式で書く。

'use strict';

// 必要なプラグインの読み込み
var gulp = require('gulp');
var gulpLoadPlugins = require('gulp-load-plugins');
var $ = gulpLoadPlugins();  // Gulp プラグインを一括で読み込む

/**
 * CSS を圧縮する
 * 
 * @return {Stream}
 */
gulp.task('min-css', function() {
  return gulp
    .src("src/css/" + fileName + ".css")  // src/css/ 配下の指定ファイルを対象に圧縮する
    .pipe($.cleanCss(
      {
        compatibility: 'ie7',   // 互換性の設定
        format: {
          breaks: {
            afterComment: true  // コメントの後ろに改行を入れる
          }
        }
      }, function(details) {
        // 圧縮結果をログ出力する
        console.log(details.name + ': ' + details.stats.originalSize + ' -> ' + details.stats.minifiedSize);
      }
    ))
    .pipe(gulp.dest('dist/css/'));  // dist/css/ 配下に出力する
});

/**
 * JavaScript を圧縮する
 * 
 * @return {Stream}
 */
gulp.task('min-js', function() {
  return gulp
    .src("src/js/*.js")  // src/js/ 配下の全ファイルを対象に圧縮する
    .pipe($.plumber())  // エラー時にプロセスが落ちないようにする
    .pipe($.uglify({
      compress: true,  // 圧縮する
      mangle: true,    // 変数の難読化を行う
      preserveComments: 'some'  // 「*!」で始まるブロックコメントを残す
    }))
    .pipe(gulp.dest('dist/js/'));  // dist/js/ 配下に出力する
});

Clean-CSS について

gulp-clean-cssclean-css-cli で使えたオプションがそのまま使えるので、clean-css パッケージの説明を参考に、連想配列の形式で書き直しておく。

実行結果をログ出力するコールバック関数を仕込めたので、圧縮前後のファイルサイズを出力するようにしてみた。

Uglify について

オプションの類は、コマンドラインで使う場合と基本的に同じ。

preserveComments オプションの some は廃止されているようなのだが、license/*! 始まりのコメントが出力されなかったので使っている。

npm-scripts は Gulp 呼び出しで書いておく

今回、グローバルに Gulp をインストールしていないので、$ gulp min-js といった呼び出し方をしても、command not found となってしまうか、gulp のバージョンが合わないと正常に動作しない。

ローカルにインストールされた Gulp でも、node_modules 配下の実行ファイルを指定すれば呼べるのだが、これはめんどくさい。

# これでも一応叩ける。
$ ./node_modules/gulp/bin/gulp.js min-js

そこで、ローカルインストールされているコマンドでも叩ける npm-scripts の仕様を利用し、以下のようなタスクを package.json に定義してやる。

// 前略
"scripts": {
  "min-css": "gulp min-css",
  "min-js": "gulp min-js"
},
// 後略

こうしておけば、以下のように npm run で Gulp タスクが呼べるようになる。

$ npm run min-css
$ npm run min-js

npm-scripts を全く使わなくなるワケではなく、「定義」だけ書くスタイルで残してみた。

個別のファイルのみビルドするようタスクを分割する

今回の「はてなブログで利用する CSS・JS をビルドしたい」という用途からすると、*.css というように全ファイルを毎度ビルドする必要がそこまでないので、個別のファイルをビルドできるタスクを作る。この時、基本的な処理は同じなので、処理部分は共通化しておく。

/**
 * CSS を圧縮する Function
 * 
 * @param {string} 圧縮するファイル名。"*" を渡せば src/css/ 配下の全ファイルが対象となる
 * @return {Stream}
 */
function minCss(fileName) {
  return gulp
    .src("src/css/" + fileName + ".css")  // src/css/ 配下の指定ファイルを対象に圧縮する
    .pipe($.cleanCss(
      {
        compatibility: 'ie7',   // 互換性の設定
        format: {
          breaks: {
            afterComment: true  // コメントの後ろに改行を入れる
          }
        }
      }, function(details) {
        // 圧縮結果をログ出力する
        console.log(details.name + ': ' + details.stats.originalSize + ' -> ' + details.stats.minifiedSize);
      }
    ))
    .pipe(gulp.dest('dist/css/'));  // dist/css/ 配下に出力する (対象ディレクトリがなくても OK・ファイルは上書き)
}

/**
 * CSS 圧縮 … 全ファイル
 * 
 * @return {Stream}
 */
gulp.task('min-css', function() {
  return minCss("*");
});

/**
 * CSS 圧縮 … Corredor
 * 
 * @return {Stream}
 */
gulp.task('min-css-corredor', function() {
  return minCss("Corredor");
});

/**
 * CSS 圧縮 … Murga
 * 
 * @return {Stream}
 */
gulp.task('min-css-murga', function() {
  return minCss("Murga");
});

/**
 * CSS 圧縮 … El Mylar
 * 
 * @return {Stream}
 */
gulp.task('min-css-elmylar', function() {
  return minCss("ElMylar");
});

minCss() という独自の Function を作り、引数のファイルを圧縮するようにしておく。gulp.task() は、この関数にファイル名を渡すだけのタスクにして、ファイル数の数だけ作っておいてやる。

コレに合わせて、package.json にも各タスクを呼び出す npm-scripts を定義しておくと良い。

JavaScript の圧縮タスクも同様に作っておくことで、個別に呼び出しやすい。

こうしてできた GitHub リポジトリは以下。gulpfile.jspackage.json を見ていただくと分かるかと。