tarifa
Version:
Your friendly toolchain for mobile app development on top of Apache Cordova
281 lines (249 loc) • 11.1 kB
JavaScript
var Q = require('q'),
mkdirp = require('mkdirp'),
rimraf = require('rimraf'),
path = require('path'),
fs = require('fs'),
qfs = require('q-io/fs'),
format = require('util').format,
settings = require('../settings'),
log = require('../helper/log'),
exec = require('child_process').exec,
tarifaFile = require('../tarifa-file');
function copyDefaultTarifaAssets(mapping, platform, config, root, type) {
return function () {
if(!mapping[platform]) return Q.reject('platform not available!');
var sources = mapping[platform].map(function(tuple) {
return tuple.src;
}).reduce(function (acc, source) {
var defaultSource = source instanceof Array ? source[1] : source;
if(acc.indexOf(defaultSource) < 0) acc.push(defaultSource);
return acc;
}, []);
return sources.reduce(function (promise, source) {
var src = path.join(__dirname, '..', '..', 'template', 'assets', platform, source),
dest = path.join(root, settings.images, platform, config, type, source);
return promise.then(function () {
return qfs.copy(src, path.resolve(dest));
});
}, Q.resolve());
};
}
function copyDefaultAssets(mapping, root, platforms, type) {
return platforms.reduce(function (promise, platform) {
return promise.then(copyDefaultTarifaAssets(mapping, platform, 'default', root, type))
.then(function () {
log.send('success', 'default %s copied for platform %s', type, platform);
});
}, Q.resolve());
}
function destinationPath(appName, destTmpl, natPath, type, platform, version) {
var xcassets = platform === 'ios' && version.split('.')[0] >= 4,
innerPath;
if (xcassets) {
innerPath = type === 'icons' ? 'Images.xcassets/AppIcon.appiconset' : 'Images.xcassets/LaunchImage.launchimage';
} else {
innerPath = type === 'icons' ? 'Resources/icons' : 'Resources/splash';
}
return path.join(natPath, destTmpl.replace('{$app_name}', appName).replace('{$path}', innerPath));
}
function mkdirAndCopy(src, dest) {
var defer = Q.defer();
mkdirp(path.dirname(dest), function(err) {
if (err) return defer.reject(err);
qfs.copy(path.resolve(src), path.resolve(dest)).done(function () {
defer.resolve();
}, function (er) { defer.reject(er); });
});
return defer.promise;
}
function copyAssets(root, mapping, platform, configuration, type, version) {
if(!mapping[platform]) return Q.reject('platform not available!');
return tarifaFile.parse(root).then(function (localSettings) {
var confSettings = localSettings.configurations[platform][configuration],
// try path $TARIFA_ROOT/images/$platform/$config first
imgPath = path.join(root, settings.images, platform, configuration, type),
natPath = path.join(root, settings.cordovaAppPath, 'platforms', platform);
// if no assets folder for given configuration try the default configuration
if (!fs.existsSync(imgPath) || fs.readdirSync(imgPath).length === 0) imgPath = path.join(root, settings.images, platform, 'default', type);
// img path potentially overriden in tarifa.json configuration (relative to tarifa root project or absolute)
if(confSettings.assets_path) imgPath = path.resolve(confSettings.assets_path, type);
// reject if imgPath directory doesn't exist
if(!fs.existsSync(imgPath) || fs.readdirSync(imgPath).length === 0)
return Q.reject(format('%s directory does not exist or is empty', imgPath));
return mapping[platform].reduce(function (promise, tuple) {
return promise.then(function () {
if(tuple.src instanceof Array) {
tuple.dest.forEach(function (dest) {
rimraf.sync(destinationPath(localSettings.name, dest, natPath, type, platform, version));
});
for(var i = 0, l = tuple.src.length; i < l; i++) {
var dest = destinationPath(localSettings.name, tuple.dest[i], natPath, type, platform, version),
source = path.join(imgPath, tuple.src[i]);
if (fs.existsSync(source))
return mkdirAndCopy(source, dest);
if (i === tuple.src.length - 1) {
log.send(
'warning',
'Unable to copy any source from list: [%s] to: [%s]',
tuple.src.join(', '),
tuple.dest.join(', ')
);
return Q.resolve();
}
}
} else {
var src = path.join(imgPath, tuple.src);
if (fs.existsSync(src)) {
return mkdirAndCopy(
src,
destinationPath(localSettings.name, tuple.dest, natPath, type, platform, version)
);
}
else {
log.send('warning', 'Unable to copy source %s to %s', src, dest);
return Q.resolve();
}
}
});
}, Q.resolve());
});
}
function execConvert(cmd, defer) {
var options = {
timeout: 0,
maxBuffer: 1024 * 400
};
exec(cmd, options, function (err, stdout, stderr) {
if(err) {
log.send('error', cmd);
log.send('error', 'convert stderr %s', stderr);
defer.reject(err);
return;
}
defer.resolve();
});
}
function generateImage(color, size, destination) {
var defer = Q.defer(),
cmd = format('convert -size %s xc:"%s" %s', size, color, destination);
execConvert(cmd, defer);
return defer.promise;
}
function resizeImage(file, size, destination, platform, color) {
var defer = Q.defer(),
c = color || 'white',
opts = platform === 'ios' ? format('-background %s -alpha remove', c) : '',
cmd = format('convert %s -resize %s %s %s', file, size.join('x'), opts, destination);
execConvert(cmd, defer);
return defer.promise;
}
function checkImageSize(file) {
var options = {
timeout: 0,
maxBuffer: 1024 * 400
},
defer = Q.defer(),
cmd = 'identify ' + file;
exec(cmd, options, function (err, stdout, stderr) {
if(err) {
log.send('error', cmd);
log.send('error', 'convert stderr %s', stderr);
defer.reject('convert ' + err);
return;
}
var res = stdout.split(' ');
defer.resolve(res[2]);
});
return defer.promise;
}
function generate(mapping, color, type, root, platforms, config) {
return platforms.reduce(function (platformPromise, platform) {
return platformPromise.then(function () {
return mapping[platform].reduce(function (promise, tuple) {
var src = tuple.src instanceof Array ? tuple.src[1] : tuple.src,
dest = path.join(root, settings.images, platform, config, type, src),
dim = src.replace('icon-', '').replace('.png', ''),
size = type === 'icons' ? (dim + 'x' + dim)
: src.replace('screen-', '').replace('.png', '').replace('.jpg', '');
return promise.then(function () {
return generateImage(color, size, dest);
});
}, Q.resolve());
});
}, Q.resolve());
}
function generateFromFile(mapping, file, root, platforms, config, color) {
return checkImageSize(file).then(function(size) {
var side = parseInt(size.split('x')[0], 10);
if (side < 192) {
return Q.reject('The provided image is too small. It must be a square' +
'of at least 192px side.');
}
return size;
}).then(function () {
return platforms.reduce(function (platformPromise, platform) {
return platformPromise.then(function () {
return mapping[platform].reduce(function (promise, tuple) {
var src = tuple.src instanceof Array ? tuple.src[1] : tuple.src,
dest = path.join(root, settings.images, platform, config, 'icons', src),
dim = parseInt(src.replace('icon-', '').replace('.png', ''), 10);
return promise.then(function () {
return resizeImage(file, [dim, dim], dest, platform, color);
});
}, Q());
});
}, Q());
});
}
function canGenerate() {
var defer = Q.defer(),
cmd = settings.external.convert.print_version,
options = {
timeout: 1000 * 10,
maxBuffer: 1024 * 400
};
exec(cmd, options, function (err) { defer.resolve(!err); });
return defer.promise;
}
function createFolder(root, platform, configuration, type) {
var defer = Q.defer(),
dirPath = path.join(root, settings.images, platform, configuration, type);
mkdirp(dirPath, function (err) {
if(err) return defer.reject('unable to create folder ' + err);
defer.resolve();
});
return defer.promise;
}
function createFolders(root, platforms, configuration) {
var foldersPromises = [];
platforms.forEach(function (platform) {
foldersPromises.push(createFolder(root, platform, configuration, 'icons'));
foldersPromises.push(createFolder(root, platform, configuration, 'splashscreens'));
});
return foldersPromises;
}
function copyDefaultAndroidNotifAssets (root) {
var defer = Q.defer(),
tmplFolder = path.resolve(__dirname, '..', '..', 'template', 'assets', 'android'),
folder = path.resolve(root, settings.images, 'android', 'default', 'images');
mkdirp(folder, function (err) {
if(err) return defer.reject('unable to create folder ' + folder + ' ' + err);
fs.readdirSync(tmplFolder).filter(function (fileName) {
return /.*notif.*\.png/.test(fileName);
}).reduce(function (p, fn) {
return p.then(function () {
return qfs.copy(path.resolve(tmplFolder, fn), path.resolve(folder, fn));
});
}, Q()).then(function () {
defer.resolve();
});
});
return defer.promise;
}
module.exports.generate = generate;
module.exports.generateFromFile = generateFromFile;
module.exports.canGenerate = canGenerate;
module.exports.copyDefaultAssets = copyDefaultAssets;
module.exports.copyAssets = copyAssets;
module.exports.createFolders = createFolders;
module.exports.copyDefaultAndroidNotifAssets = copyDefaultAndroidNotifAssets;