grunt
pngquant

更新時刻を保持するpngquantタスク

More than 3 years have passed since last update.

pngquantに限った事ではないですが、grunt等で公開用のデータをビルドする場合、基本的にファイルの更新時刻はビルドした時点の物となります。ファイルを更新しているので当たり前ですね。

デプロイの方法によってはこれで全然問題はないのですが、レガシーな手法、例えばsshやftpなどでプッシュする場合に更新時刻をソースの物に合わせたい場合があります。もとい、あるとします。(差分で同期を取る際などに、時刻を基準とするケース)

そのようなケースに合わせるために、オリジナルの更新時刻を保持するpngquantタスクを書いてみます。



  • fs.stat/fs.statSync を使って更新時刻を取得


  • pngquantコマンドで最適化した後、touchコマンド実行で更新時刻を変更

  • 一度に多くの処理を走らせるとエラーを吐くので、決まった数ずつ順次処理していきます

尚、touchコマンド/pngquantコマンドを使用出来る環境を前提としているので、事前にpngquantコマンドをインストールしておきます。(Windowsの場合はGit CUIなどでパスを通します)

更新時刻を保持するpngquantタスク @ Gist

/**

* Grunt task for pngquant
* -----------------------
*/

// @example:
//
// pngquant: {
// options: {
// preserve_mtime: true
// },
// dist: {
// src: [
// "the/path/to/*.png",
// "the/path/to/**/*.png"
// ]
// }
// }

module.exports = function(grunt){

grunt.registerMultiTask("pngquant", "", function(){

var my = {}, fs, cp, done;

fs = require("fs"),
cp = require("child_process");
done = this.async();

/**
* Options:
* - preserve_mtime:Boolean ... Preserve modified time or not
* - pngquant:String ... Template string for pngquant command
* - touch:String ... Tempalte string for touch command
* - verbose:Boolean ... Verbose mode
* - separate:Number ... Separate the process by count
*/

my.options = this.options({
preserve_mtime: true,
pngquant: 'pngquant <%=file %> --iebug --ext .png --force',
touch: 'touch -d "<%=date %>" <%=file %>',
verbose: true,
separate: 8
});

/**
* Get file list
*/

my.count = 0;
my.files = (function(files){
var list = [], res = [], i = 0;
files.forEach(function(o){
list = list.concat(o.src);
});
my.count = list.length;
while(i<=list.length){
res.push(list.slice(i, i+my.options.separate));
i+=my.options.separate;
}
return res;
}(this.files));
my.length = my.files.length;

/**
* Get modified time of file
* - If `preserve_mtime` is false, return null
* @param String file
*/

my.getMTime= function(file){
if(! this.options.preserve_mtime){ return null; }
var date = new Date(fs.statSync(file).mtime);
return grunt.template.date(date, "yyyy/mm/dd HH:MM:ss");
};

/**
* Optimize image file by pngquant
* @param {String} file
* @param {Function} callback
*/

my.optimize = function(file, callback){
var mtime, pngquant, touch;
mtime = my.getMTime(file);
pngquant = grunt.template.process(my.options.pngquant, {
data: { file: file }
});
touch = mtime ? grunt.template.process(my.options.touch, {
data: {
file: file,
date: mtime
}
}) : "";
cp.exec(pngquant, function(e, out, error){
if(e){
return grunt.log.error(error);
}
mtime && cp.exec(touch);
callback();
});
};

/**
* Run process
*/

my.process = function(){
var files, count;

if(! my.files.length){
grunt.log.writeln("All images optimized.");
return done();
}

files = my.files.pop();
count = files.length;
files.forEach(function(file){
my.optimize(file, function(){
count -= 1;
! count && my.process();
});
});

if(my.options.verbose){
var percent = 100 - parseInt(my.files.length / my.length * 100, 10);
grunt.log.writeln(
grunt.template.process(
"<%=percent %>% of <%=total %> files",
{data: {percent: percent, total: my.count}}
)
);
}
};

my.process();

});

};