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
uninstallはunと省略表記できる。ヘルプで見てみるとremove, rm, r, un, unlinkは全て同じエイリアスらしい。- 削除するパッケージを複数並べて記述できる。
-Dは--save-devの略。package.jsonからこのパッケージを使用している記述を消してくれる。- 参考 : npmのuninstallコマンドを忘れがちなのでメモ - Qiita
次に、今回使うパッケージ群をインストールする。
$ npm install gulp gulp-load-plugins gulp-plumber gulp-clean-css gulp-uglify -D
installはiと省略表記できる。-Dは前述のとおり--save-devの省略表記。package.jsonにこのパッケージを使用しているという記述を追記してくれる。
導入しているプラグインは以下のとおり。
gulp: Gulp 本体。gulp-load-plugins: Gulp プラグインを一括読み込みできるプラグイン。便利なのでとりあえず入れておく。- Babel にリベンジ!ES2015 ってやつを勉強する環境を作るぜ!
- 参考 : gulpパッケージの一括ロード - Qiita
-
デフォルト設定では、パッケージの名前から
gulp-が削除され、残りの部分をキャメルケースにした名前で$に割り当てられます
gulp-plumber: JavaScript のビルドに失敗した時にプロセスが落ちないようにしてくれるプラグイン。今回はなくても良い気がしたけど入れておいて悪いことないので入れておく。gulp-clean-css: 先程アンインストールしたclean-css-cliを Gulp で使えるようにしているラッパーのプラグイン。CSS を圧縮する。gulp-uglify: JavaScript を圧縮するプラグイン。
ググっていた中で gulp-minify-css とか gulp-uglifyjs といったプラグイン名も見かけたが、npm で確認するとどちらも Deprecated (廃止) になっていた。使うプラグインはちゃんと公式の API を確認しておこう。
- 参考 : gulp-minify-css - npm
- 参考 : gulp-uglifyjs - npm
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/ 配下に出力する
});
gulp-load-pluginsの使い方は見ていただければ分かるかと。ハイフンケースのパッケージ名をキャメルケースにする。gulp-plumberはgulp.src()の後に挟んでおく。gulp.dest()は対象のディレクトリがなくても勝手に作ってくれる。また、ファイルが存在する場合は上書きする。
Clean-CSS について
gulp-clean-css は clean-css-cli で使えたオプションがそのまま使えるので、clean-css パッケージの説明を参考に、連想配列の形式で書き直しておく。
- 参考 : clean-css - npm
実行結果をログ出力するコールバック関数を仕込めたので、圧縮前後のファイルサイズを出力するようにしてみた。
- 参考 : gulp-clean-css - npm
Uglify について
オプションの類は、コマンドラインで使う場合と基本的に同じ。
-c=-compress… 圧縮する。-m=-mangle… 変数を短くする。AngularJS なんかでコレのせいでストレートな DI ができなくなる、アレ。
preserveComments オプションの some は廃止されているようなのだが、license で /*! 始まりのコメントが出力されなかったので使っている。
- 参考 : gulp-uglify - npm
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.js と package.json を見ていただくと分かるかと。