imageminのクロスプラットフォームなインストール
npmからimageminをインストールするとimagemin-optipngやimagemin-jpegtranも一緒にインストールされると思うのだけど、 インストールされるoptipngやjpegtranのバイナリは当然ながらコマンドを実行したOSのものがインストールされる。
これを別のOSのバイナリをインストールさせるようにしたい。というのも、Electronの各OS向けアーカイブを一つのOSで作りたいので。
optipng-binやjpegtran-binを調べてみると、内部で使用しているbin-wrapperというモジュールでバイナリをダウンロードしているみたいだった。
OSやCPUのアーキテクチャを判定してダウンロードするモジュールを変えているのはbin-wrapperが内部で使用しているos-filter-objというモジュールだった。
os-filter-objは直接process.platform
やprocess.arch
を見て判定しているので、これらを偽装する必要がある。
http://stackoverflow.com/questions/30405397/how-do-you-mock-process-platform-in-jasmine-tests
を見ると、Object.defineProperty
で既存のプロパティを上書きしてしまえばいいみたいなので、以下のようなものを書いた。
function ProcessFaker() {
this._originalProcess = process;
this._originalDescripters = {};
}
ProcessFaker.prototype.fake = function fake(propertyName, fakeValue) {
var originalDescripter =
Object.getOwnPropertyDescriptor(this._originalProcess, propertyName);
this._originalDescripters[propertyName] = originalDescripter;
Object.defineProperty(process, propertyName, fakeValue);
};
ProcessFaker.prototype.unfake = function unfake(propertyName) {
var originalDescripter =
this._originalDescripters[propertyName];
Object.defineProperty(process, propertyName, originalDescripter);
this._originalDescripters[propertyName] = null;
};
module.exports = ProcessFaker;
うーん。で、パッケージ化するコードの簡易なやつは以下のような感じ。 実際に使ったコードはもっと長いので省略した感じで書いてるけど、代わりに動かないかもしれない。
'use strict';
var co = require('co'),
electronPackager = require('electron-packager'),
glob = require('glob'),
rimraf = require('rimraf'),
thunkify = require('thunkify'),
ProcessFaker = require('./process-faker');
co(function*() {
var processFaker = new ProcessFaker();
// electron-packagerでパッケージ化する
console.log(yield thunkify(electronPackager).call(electronPackager, {
// options...
}));
try {
let vendorDirs;
// process.archとprocess.platformを偽装する
processFaker.fake('arch', { value: 'x64' });
processFaker.fake('platform', { value: 'win32' });
// バイナリがインストールされているディレクトリを
// electron-packagerが出力したディレクトリから探す
vendorDirs = yield thunkify(glob).call(glob, `/path/to/**/vendor`);
// バイナリがインストールされているディレクトリを削除する
yield vendorDirs.map(
(vendorDir) => thunkify(rimraf).call(rimraf, vendorDir)
);
// optipng-binやjpegtran-binを直接requireしてdownload()を実行する
yield vendorDirs.map(function(vendorDir) {
var pluginModule = require(`${vendorDirPath}/../lib`);
return co(function*() {
return yield thunkify(pluginModule.download.bind(pluginModule));
});
});
} finally {
// 一応戻しておく
processFaker.unfake('arch');
processFaker.unfake('platform');
}
}).then(function() {
console.log('done');
}).catch(function(err) {
console.error(err);
});
とても無理矢理な感じがする( ˘ω˘)
もっと良い方法があったりするのかなー?