index.js
var Decompress, File, PLUGIN_NAME, PluginError, ProgressBar, Promise, asar, asarPackaging, async, chalk, childProcess, distributeApp, distributeBase, distributeMacIcon, distributePlist, distributeWinIcon, download, electron, fs, getApmPath, grs, isDir, isExists, isFile, mv, mvAsync, packaging, path, plist, rcedit, rebuild, rm, rmAsync, signDarwin, spawn, through, unzip, util;
fs = require('fs-extra');
grs = require('grs');
path = require('path');
async = require('async');
Promise = require('bluebird');
mv = require('mv');
mvAsync = Promise.promisify(mv);
rm = require('rimraf');
rmAsync = Promise.promisify(rm);
util = require('gulp-util');
asar = require('asar');
chalk = require('chalk');
Decompress = require('decompress-zip');
PluginError = util.PluginError;
through = require('through2');
childProcess = require('child_process');
ProgressBar = require('progress');
File = require('vinyl');
plist = require('plist');
rcedit = require('rcedit');
PLUGIN_NAME = 'gulp-electron';
module.exports = electron = function(options) {
var bufferContents, endStream, packageJson;
PLUGIN_NAME = 'gulp-electron';
options = options || {};
if (!options.release || !options.version || !options.src || !options.cache) {
throw new PluginError(PLUGIN_NAME, 'Miss version or release path.');
}
if (path.resolve(options.src) === path.resolve(".")) {
throw new PluginError(PLUGIN_NAME, 'src path can not root path.');
}
packageJson = options.packageJson;
if (typeof options.packageJson === 'string') {
packageJson = require(packageJson);
}
if (options.platforms == null) {
options.platforms = ['darwin'];
}
if (options.apm == null) {
options.apm = getApmPath();
}
if (options.symbols == null) {
options.symbols = false;
}
if (options.rebuild == null) {
options.rebuild = false;
}
if (options.asar == null) {
options.asar = false;
}
if (options.asarUnpack == null) {
options.asarUnpack = false;
}
if (options.asarUnpackDir == null) {
options.asarUnpackDir = false;
}
if (options.packaging == null) {
options.packaging = true;
}
if (options.ext == null) {
options.ext = 'zip';
}
if (typeof options.platforms === 'string') {
options.platforms = [options.platforms];
}
bufferContents = function(file, enc, cb) {
var src;
src = file;
return cb();
};
endStream = function(callback) {
var platforms, push;
push = this.push;
platforms = ['darwin', 'win32', 'linux', 'darwin-x64', 'linux-ia32', 'linux-x64', 'win32-ia32', 'win32-x64', 'linux-arm'];
return Promise.map(options.platforms, function(platform) {
var _src, binName, cache, cacheFile, cachePath, cacheZip, cacheedPath, contentsPlistDir, defaultAppName, electronFile, electronFileDir, electronFilePath, getUserHome, identity, packagingCmd, pkg, pkgZip, pkgZipDir, pkgZipFilePath, pkgZipPath, platformDir, platformPath, ref, ref1, suffix, targetApp, targetAppDir, targetAppPath, targetAsarPath, targetDir, targetDirPath, targetZip, unpackagingCmd;
if (platform === 'osx') {
platform = 'darwin';
}
if (platform === 'win') {
platform = 'win32';
}
if (platforms.indexOf(platform) < 0) {
throw new PluginError(PLUGIN_NAME, "Not support platform " + platform);
}
if (options.ext == null) {
options.ext = "zip";
}
pkgZip = pkg = packageJson.name + "-" + packageJson.version + "-" + platform;
if (options.symbols) {
pkgZip += '-symbols';
}
pkgZip += "." + options.ext;
cacheZip = cache = "electron-" + options.version + "-" + platform;
if (options.symbols) {
cacheZip += '-symbols';
}
cacheZip += "." + options.ext;
getUserHome = function() {
return process.env.HOME || process.env.USERPROFILE;
};
if (!path.isAbsolute(options.cache)) {
if (options.cache.match(/^\~/)) {
options.cache = path.join(getUserHome(), options.cache.replace(/^\~\//, ""));
} else {
options.cache = path.resolve(options.cache);
}
}
cachePath = path.resolve(options.cache, options.version);
cacheFile = path.resolve(cachePath, cacheZip);
cacheedPath = path.resolve(cachePath, cache);
if (!path.isAbsolute(options.release)) {
if (options.release.match(/^\~/)) {
options.release = path.join(getUserHome(), options.release.replace(/^\~\//, ""));
} else {
options.release = path.resolve(options.release);
}
}
pkgZipDir = path.resolve(options.release, options.version);
pkgZipPath = path.resolve(pkgZipDir);
pkgZipFilePath = path.resolve(pkgZipDir, pkgZip);
platformDir = path.join(pkgZipDir, platform);
platformPath = path.resolve(platformDir);
targetApp = "";
defaultAppName = "Electron";
suffix = "";
_src = path.join('resources', 'app');
if (platform.indexOf('darwin') >= 0) {
suffix = ".app";
electronFile = "Electron" + suffix;
targetZip = packageJson.name + suffix;
_src = path.join(packageJson.name + suffix, 'Contents', 'Resources', 'app');
} else if (platform.indexOf('win') >= 0) {
suffix = ".exe";
electronFile = "electron" + suffix;
targetZip = ".";
} else {
electronFile = "electron";
targetZip = ".";
}
electronFileDir = path.join(platformDir, electronFile);
electronFilePath = path.resolve(electronFileDir);
binName = packageJson.name + suffix;
targetAppDir = path.join(platformDir, binName);
targetAppPath = path.join(targetAppDir);
_src = path.join('resources', 'app');
if (platform.indexOf('darwin') >= 0) {
_src = path.join(binName, 'Contents', 'Resources', 'app');
}
targetDir = path.join(packageJson.name, _src);
targetDirPath = path.resolve(platformDir, _src);
targetAsarPath = path.resolve(platformDir, _src + ".asar");
contentsPlistDir = path.join(targetAppPath, 'Contents', 'Info.plist');
identity = "";
if ((((ref = options.platformResources) != null ? (ref1 = ref.darwin) != null ? ref1.identity : void 0 : void 0) != null) && isFile(options.platformResources.darwin.identity)) {
identity = fs.readFileSync(options.platformResources.darwin.identity, 'utf8').trim();
/*
signingCmd =
* http://sevenzip.sourceforge.jp/chm/cmdline/commands/extract.htm
darwin: [
cmd: 'codesign'
args: ['--deep', '--force', '--verbose', '--sign', identity, path.join(targetAppDir ,'Contents', 'Frameworks', 'Electron\\ Framework.framework')]
,
cmd: 'codesign'
args: ['--deep', '--force', '--verbose', '--sign', identity, path.join(targetAppDir ,'Contents', 'Frameworks', 'Electron\\ Helper EH.app')]
,
cmd: 'codesign'
args: ['--deep', '--force', '--verbose', '--sign', identity, path.join(targetAppDir ,'Contents', 'Frameworks', 'Electron\\ Helper NP.app')]
,
cmd: 'codesign'
args: ['--deep', '--force', '--verbose', '--sign', identity, path.join(targetAppDir ,'Contents', 'Frameworks', 'Electron\\ Helper.app')]
,
cmd: 'codesign'
args: ['--deep', '--force', '--verbose', '--sign', identity, path.join(targetAppDir ,'Contents', 'Frameworks', 'ReactiveCocoa.framework')]
,
cmd: 'codesign'
args: ['--deep', '--force', '--verbose', '--sign', identity, path.join(targetAppDir ,'Contents', 'Frameworks', 'Squirrel.framework')]
,
cmd: 'codesign'
args: ['--deep', '--force', '--verbose', '--sign', identity, path.join(targetAppDir,'Contents', 'Frameworks', 'Mantle.framework')]
,
cmd: 'codesign'
args: ['--deep', '--force', '--verbose', '--sign', identity, targetAppDir]
]
*/
}
unpackagingCmd = {
win32: {
cmd: '7z',
args: ['x', cacheFile, '-o' + cacheedPath]
},
darwin: {
cmd: 'unzip',
args: ['-q', '-o', cacheFile, '-d', cacheedPath]
},
linux: {
cmd: 'unzip',
args: ['-o', cacheFile, '-d', cacheedPath]
}
};
packagingCmd = {
win32: {
cmd: '7z',
args: ['a', path.join('..', pkgZip), targetZip],
opts: {
cwd: platformPath
}
},
darwin: {
cmd: 'ditto',
args: ['-c', '-k', '--sequesterRsrc', '--keepParent', targetZip, path.join('..', pkgZip)],
opts: {
cwd: platformPath
}
},
linux: {
cmd: 'zip',
args: ['-9', '-y', '-r', path.join('..', pkgZip), targetZip],
opts: {
cwd: platformPath
}
}
};
return new Promise(function(resolve, reject) {
return Promise.resolve().then(function() {
return download(cacheFile, cachePath, options.version, cacheZip, options.token);
}).then(function() {
return unzip(cacheFile, cacheedPath, unpackagingCmd[process.platform]);
}).then(function() {
return distributeBase(platformPath, cacheedPath, electronFilePath, targetAppPath);
}).then(function() {
if (!options.rebuild) {
return Promise.resolve();
}
util.log(PLUGIN_NAME, "Rebuilding modules");
return rebuild({
cmd: options.apm,
args: ['rebuild']
});
}).then(function() {
util.log(PLUGIN_NAME, "distributeApp " + targetAppDir);
if (!path.isAbsolute(options.src)) {
if (options.src.match(/^\~/)) {
options.src = path.join(getUserHome(), options.src.replace(/^\~\//, ""));
} else {
options.src = path.resolve(options.src);
}
}
return distributeApp(options.src, targetDirPath);
}).then(function() {
var ref2;
if (platform.indexOf('darwin') === -1 || (((ref2 = options.platformResources) != null ? ref2.darwin : void 0) == null)) {
return Promise.resolve();
}
util.log(PLUGIN_NAME, "distributePlist " + targetAppPath);
return distributePlist(options.platformResources.darwin, packageJson.name, targetAppPath);
}).then(function() {
var ref2;
if (platform.indexOf('darwin') === -1 || (((ref2 = options.platformResources) != null ? ref2.darwin : void 0) == null)) {
return Promise.resolve();
}
util.log(PLUGIN_NAME, "distributeMacIcon " + targetAppDir);
return distributeMacIcon(options.platformResources.darwin.icon, targetAppPath);
}).then(function() {
var ref2;
if (platform.indexOf('win32') === -1 || (((ref2 = options.platformResources) != null ? ref2.win : void 0) == null)) {
return Promise.resolve();
}
util.log(PLUGIN_NAME, "distributeWinIcon " + targetAppDir);
return distributeWinIcon(options.platformResources.win, targetAppPath);
}).then(function() {
if (!options.asar) {
return Promise.resolve();
}
util.log(PLUGIN_NAME, "packaging app.asar");
return asarPackaging(targetDirPath, targetAsarPath, {
unpack: options.asarUnpack,
unpackDir: options.asarUnpackDir
});
}).then(function() {
if (!options.packaging) {
return Promise.resolve();
}
return Promise.resolve();
/*
if platform is "darwin-x64" and process.platform is "darwin"
if identity is ""
util.log PLUGIN_NAME, "not found identity file. skip signing"
return Promise.resolve()
signDarwin signingCmd.darwin
*/
}).then(function() {
if (!options.packaging) {
return Promise.resolve();
}
return packaging(pkgZipFilePath, packagingCmd[process.platform]);
}).then(function() {
return resolve();
});
});
})["finally"](function() {
util.log(PLUGIN_NAME, "all distribute done.");
return callback();
});
};
return through.obj(bufferContents, endStream);
};
isDir = function() {
var filepath;
filepath = path.join.apply(path, arguments);
return fs.existsSync(filepath) && !fs.statSync(filepath).isFile();
};
isFile = function() {
var filepath;
filepath = path.join.apply(path, arguments);
return fs.existsSync(filepath) && fs.statSync(filepath).isFile();
};
isExists = function() {
var filepath;
filepath = path.join.apply(path, arguments);
return fs.existsSync(filepath);
};
getApmPath = function() {
var apmPath;
apmPath = path.join('apm', 'node_modules', 'atom-package-manager', 'bin', 'apm');
if (!isFile(apmPath)) {
return apmPath = 'apm';
}
};
download = function(cacheFile, cachePath, version, cacheZip, token) {
if (isFile(cacheFile)) {
util.log(PLUGIN_NAME, "download skip: already exists");
return Promise.resolve();
}
return new Promise(function(resolve, reject) {
var bar;
util.log(PLUGIN_NAME, "download electron " + cacheZip + " cache filie.");
fs.mkdirsSync(cachePath);
bar = null;
return grs({
repo: 'atom/electron',
tag: version,
name: cacheZip,
token: token
}).on('error', function(error) {
throw new PluginError(PLUGIN_NAME, error);
}).on('size', function(size) {
return bar = new ProgressBar(cacheFile + " [:bar] :percent :etas", {
complete: '>',
incomplete: ' ',
width: 20,
total: size
});
}).pipe(through(function(chunk, enc, cb) {
bar.tick(chunk.length);
this.push(chunk);
return cb();
})).pipe(fs.createWriteStream(cacheFile)).on('close', resolve).on('error', reject);
});
};
unzip = function(src, target, unpackagingCmd) {
if (isExists(target)) {
return Promise.resolve();
}
return new Promise(function(resolve, reject) {
/*
decompress = new Decompress src
decompress.on 'error', reject
decompress.on 'extract', ->
util.log PLUGIN_NAME, "decompress done #{src}, #{target}"
resolve()
decompress.extract
path: target
follow: true
*/
return spawn(unpackagingCmd, function() {
return resolve();
});
});
};
distributeBase = function(platformPath, cacheedPath, electronFilePath, targetAppPath) {
if (isExists(platformPath) && isExists(targetAppPath)) {
util.log(PLUGIN_NAME, "distributeBase skip: already exists");
return Promise.resolve();
}
return new Promise(function(resolve) {
fs.mkdirsSync(platformPath);
fs.copySync(cacheedPath, platformPath);
return mvAsync(electronFilePath, targetAppPath, {
mkdirp: true
}).then(resolve);
});
};
distributeApp = function(src, targetDirPath) {
if (isExists(targetDirPath)) {
util.log(PLUGIN_NAME, "distributeApp skip: already exists");
return Promise.resolve();
}
return new Promise(function(resolve) {
return rmAsync(targetDirPath)["finally"](function() {
fs.mkdirsSync(targetDirPath);
fs.copySync(src, targetDirPath);
return resolve();
});
});
};
distributePlist = function(darwin, name, targetAppPath) {
return new Promise(function(resolve) {
var _binaryDest, _binarySrc, contentsPlist;
contentsPlist = plist.parse(fs.readFileSync(path.join(targetAppPath, 'Contents', 'Info.plist'), 'utf8'));
if (darwin.CFBundleDisplayName != null) {
contentsPlist.CFBundleDisplayName = darwin.CFBundleDisplayName;
}
if (darwin.CFBundleIdentifier != null) {
contentsPlist.CFBundleIdentifier = darwin.CFBundleIdentifier;
}
if (darwin.CFBundleName != null) {
contentsPlist.CFBundleName = darwin.CFBundleName;
}
if (darwin.CFBundleVersion != null) {
contentsPlist.CFBundleVersion = darwin.CFBundleVersion;
}
if (darwin.CFBundleExecutable != null) {
contentsPlist.CFBundleExecutable = darwin.CFBundleExecutable;
}
if (darwin.CFBundleURLTypes != null) {
contentsPlist.CFBundleURLTypes = darwin.CFBundleURLTypes;
}
fs.writeFileSync(path.join(targetAppPath, 'Contents', 'Info.plist'), plist.build(contentsPlist));
if (darwin.CFBundleExecutable != null) {
_binarySrc = path.join(targetAppPath, 'Contents', 'MacOS', 'Electron');
_binaryDest = path.join(targetAppPath, 'Contents', 'MacOS', darwin.CFBundleExecutable);
return mvAsync(_binarySrc, _binaryDest, {
mkdirp: true
}).then(resolve);
} else {
return resolve();
}
});
};
distributeMacIcon = function(src, targetAppPath) {
return new Promise(function(resolve) {
var iconDir;
iconDir = path.join(targetAppPath, 'Contents', 'Resources', 'electron.icns');
fs.createReadStream(src).pipe(fs.createWriteStream(iconDir));
return resolve();
});
};
distributeWinIcon = function(src, targetAppPath) {
return new Promise(function(resolve) {
rcedit(targetAppPath, src, resolve);
return resolve();
});
};
rebuild = function(cmd) {
return new Promise(function(resolve) {
return spawn(cmd, resolve);
});
};
asarPackaging = function(src, target, opts) {
var escSrc, escTarget;
escSrc = src.replace(/(\\\s)/, "\\ ");
escTarget = target.replace(/(\\\s)/, "\\ ");
return new Promise(function(resolve) {
util.log(PLUGIN_NAME, "packaging app.asar " + escSrc + ", " + escTarget);
return asar.createPackageWithOptions(escSrc, escTarget, opts, function() {
return resolve();
});
});
};
signDarwin = function(signingCmd) {
var promiseList;
promiseList = [];
signingCmd.forEach(function(cmd) {
var p;
p = Promise.defer();
promiseList.push(p);
return spawn(cmd, function() {
return p.resolve();
});
});
return Promise.when(promiseList);
};
packaging = function(pkgZipFilePath, packagingCmd) {
if (!isFile(pkgZipFilePath)) {
return new Promise(function(resolve) {
var cmd;
cmd = packagingCmd;
return spawn(cmd, function() {
return resolve();
});
});
}
return new Promise(function(resolve) {
return rmAsync(pkgZipFilePath)["finally"](function() {
var cmd;
cmd = packagingCmd;
return spawn(cmd, function() {
return resolve();
});
});
});
};
spawn = function(options, cb) {
var error, proc, stderr, stdout;
stdout = [];
stderr = [];
error = null;
options.args.forEach(function(arg) {
return arg = arg.replace(' ', '\\ ');
});
util.log("> " + options.cmd + " " + (options.args.join(' ')));
proc = childProcess.spawn(options.cmd, options.args, options.opts);
proc.stdout.on('data', function(data) {
stdout.push(data.toString());
if (process.NODE_ENV === 'test') {
return util.log(data.toString());
}
});
proc.stderr.on('data', function(data) {
return stderr.push(data.toString());
});
return proc.on('exit', function(code, signal) {
var results;
if (code !== 0) {
error = new Error(signal);
}
results = {
stderr: stderr.join(''),
stdout: stdout.join(''),
code: code
};
if (code !== 0) {
throw new PluginError(PLUGIN_NAME, results.stderr || 'unknow error , maybe you can try delete the zip packages.');
}
return cb(error, results);
});
};