UNPKG

ionic

Version:

A tool for creating and developing Ionic Framework mobile apps.

1,076 lines (905 loc) • 33.1 kB
var fs = require('fs'); var os = require('os'); var path = require('path'); var Q = require('q'); var request = require('request'); var xml2js = require('xml2js'); var _ = require('underscore'); var crc = require('crc'); var shelljs = require('shelljs'); var Utils = require('./utils'); var log = require('./logging').logger; var appDirectory; var tmpDir = os.tmpdir(); var configData; var buildPlatforms; var images; var generateQueue; var sourceFiles; var generatingImages; var generateLandscape; var generatePortrait; var orientation; var Settings = { apiUrl: 'http://res.ionic.io', apiUploadPath: '/api/v1/upload', apiTransformPath: '/api/v1/transform', resourceDir: 'resources', iconDir: 'icon', splashDir: 'splash', iconSourceFile: 'icon', splashSourceFile: 'splash', sourceExtensions: ['psd', 'ai', 'png'], supportedPlatforms: ['android', 'ios', 'wp8'], configFile: 'config.xml', generateThrottle: 4, defaultMaxIconSize: 96, cacheImages: false }; var Platforms = { android: { icon: { images: [ { name: 'drawable-ldpi-icon.png', width: 36, height: 36, density: 'ldpi' }, { name: 'drawable-mdpi-icon.png', width: 48, height: 48, density: 'mdpi' }, { name: 'drawable-hdpi-icon.png', width: 72, height: 72, density: 'hdpi' }, { name: 'drawable-xhdpi-icon.png', width: 96, height: 96, density: 'xhdpi' }, { name: 'drawable-xxhdpi-icon.png', width: 144, height: 144, density: 'xxhdpi' }, { name: 'drawable-xxxhdpi-icon.png', width: 192, height: 192, density: 'xxxhdpi' } ], nodeName: 'icon', nodeAttributes: ['src', 'density'] }, splash: { images: [ { name: 'drawable-land-ldpi-screen.png', width: 320, height: 240, density: 'land-ldpi' }, { name: 'drawable-land-mdpi-screen.png', width: 480, height: 320, density: 'land-mdpi' }, { name: 'drawable-land-hdpi-screen.png', width: 800, height: 480, density: 'land-hdpi' }, { name: 'drawable-land-xhdpi-screen.png', width: 1280, height: 720, density: 'land-xhdpi' }, { name: 'drawable-land-xxhdpi-screen.png', width: 1600, height: 960, density: 'land-xxhdpi' }, { name: 'drawable-land-xxxhdpi-screen.png', width: 1920, height: 1280, density: 'land-xxxhdpi' }, { name: 'drawable-port-ldpi-screen.png', width: 240, height: 320, density: 'port-ldpi' }, { name: 'drawable-port-mdpi-screen.png', width: 320, height: 480, density: 'port-mdpi' }, { name: 'drawable-port-hdpi-screen.png', width: 480, height: 800, density: 'port-hdpi' }, { name: 'drawable-port-xhdpi-screen.png', width: 720, height: 1280, density: 'port-xhdpi' }, { name: 'drawable-port-xxhdpi-screen.png', width: 960, height: 1600, density: 'port-xxhdpi' }, { name: 'drawable-port-xxxhdpi-screen.png', width: 1280, height: 1920, density: 'port-xxxhdpi' } ], nodeName: 'splash', nodeAttributes: ['src', 'density'] } }, ios: { icon: { images: [ { name: 'icon.png', width: 57, height: 57 }, { name: 'icon@2x.png', width: 114, height: 114 }, { name: 'icon-40.png', width: 40, height: 40 }, { name: 'icon-40@2x.png', width: 80, height: 80 }, { name: 'icon-40@3x.png', width: 120, height: 120 }, { name: 'icon-50.png', width: 50, height: 50 }, { name: 'icon-50@2x.png', width: 100, height: 100 }, { name: 'icon-60.png', width: 60, height: 60 }, { name: 'icon-60@2x.png', width: 120, height: 120 }, { name: 'icon-60@3x.png', width: 180, height: 180 }, { name: 'icon-72.png', width: 72, height: 72 }, { name: 'icon-72@2x.png', width: 144, height: 144 }, { name: 'icon-76.png', width: 76, height: 76 }, { name: 'icon-76@2x.png', width: 152, height: 152 }, { name: 'icon-83.5@2x.png', width: 167, height: 167 }, { name: 'icon-small.png', width: 29, height: 29 }, { name: 'icon-small@2x.png', width: 58, height: 58 }, { name: 'icon-small@3x.png', width: 87, height: 87 } ], nodeName: 'icon', nodeAttributes: ['src', 'width', 'height'] }, splash: { images: [ { name: 'Default-568h@2x~iphone.png', width: 640, height: 1136 }, { name: 'Default-667h.png', width: 750, height: 1334 }, { name: 'Default-736h.png', width: 1242, height: 2208 }, { name: 'Default-Landscape-736h.png', width: 2208, height: 1242 }, { name: 'Default-Landscape@2x~ipad.png', width: 2048, height: 1536 }, { name: 'Default-Landscape@~ipadpro.png', width: 2732, height: 2048 }, { name: 'Default-Landscape~ipad.png', width: 1024, height: 768 }, { name: 'Default-Portrait@2x~ipad.png', width: 1536, height: 2048 }, { name: 'Default-Portrait@~ipadpro.png', width: 2048, height: 2732 }, { name: 'Default-Portrait~ipad.png', width: 768, height: 1024 }, { name: 'Default@2x~iphone.png', width: 640, height: 960 }, { name: 'Default~iphone.png', width: 320, height: 480 } ], nodeName: 'splash', nodeAttributes: ['src', 'width', 'height'] } }, wp8: { icon: { images: [ { name: 'ApplicationIcon.png', width: 99, height: 99 }, { name: 'Background.png', width: 159, height: 159 } ], nodeName: 'icon', nodeAttributes: ['src', 'width', 'height'] }, splash: { images: [ { name: 'SplashScreenImage.png', width: 768, height: 1280 } ], nodeName: 'splash', nodeAttributes: ['src', 'width', 'height'] } } }; function generate(dir, options) { appDirectory = dir; buildPlatforms = []; images = []; generateQueue = []; sourceFiles = {}; generatingImages = {}; generateLandscape = false; generatePortrait = false; orientation = 'default'; if (!fs.existsSync(path.join(appDirectory, Settings.configFile))) { log.error('Invalid ' + Settings.configFile + ' file. Make sure the working directory is a Cordova project.'); return Q.reject('INVALID_CONFIG_FILE'); } if (options.default) { if (hasExistingResources(appDirectory) && !options.force) { log.error('The resources folder already exists. We will not overwrite ' + 'your files unless you pass the --force argument.'); log.error('Running with the force flag will overwrite your resources ' + 'directory and modify your config.xml file'); return Q.reject('RESOURCES_EXISTS'); } return copyIconFilesIntoResources(appDirectory, options.force) .then(function() { return addIonicIcons(appDirectory, 'ios'); }) .then(function() { return addIonicIcons(appDirectory, 'android'); }); } if (!fs.existsSync(path.join(appDirectory, Settings.resourceDir))) { fs.mkdirSync(path.join(appDirectory, Settings.resourceDir)); } var hasPlatforms = true; if (_.contains(options.platforms, 'all')) { buildPlatforms = Settings.supportedPlatforms; } else if (_.contains(options.platforms, 'ios')) { buildPlatforms.push('ios'); } else if (_.contains(options.platforms, 'android')) { buildPlatforms.push('android'); } else if (_.contains(options.platforms, 'wp8')) { buildPlatforms.push('wp8'); } else if (!fs.existsSync(path.join(appDirectory, 'platforms'))) { hasPlatforms = false; } else { var listedPlatforms = fs.readdirSync(path.join(appDirectory, 'platforms')); buildPlatforms = _.intersection(Settings.supportedPlatforms, listedPlatforms); hasPlatforms = buildPlatforms.length; } if (!hasPlatforms) { log.error('No platforms have been added.'); log.error('Please add a platform, for example: ionic platform add ios'); log.error('Or provide a platform name, for example: ionic resources android'); return Q.reject('NO_PLATFORMS'); } return getConfigData(appDirectory).then(function() { if (options.landscape || options.l) { generateLandscape = true; } if (options.portrait || options.p) { generatePortrait = true; } if (!options.landscape && !options.l && !options.portrait && !options.p) { orientation = getOrientationConfigData(); if (orientation === 'landscape') { generateLandscape = true; } else if (orientation === 'portrait') { generatePortrait = true; } else { generateLandscape = true; generatePortrait = true; } } if (options['api']) { Settings.apiUrl = options['api']; } var promises = []; if (options.icon || options.i) { log.info('Ionic icon resources generator'); promises.push(queueResTypeImages('icon')); } else if (options.splash || options.s) { log.info('Ionic splash screen resources generator'); promises.push(queueResTypeImages('splash')); } else { log.info('Ionic icon and splash screen resources generator'); promises.push(queueResTypeImages('icon')); promises.push(queueResTypeImages('splash')); } return Q.all(promises) .then(loadSourceImages) .then(generateResourceImages) .then(loadResourceImages) .then(updateConfigData) .catch(function(err) { console.error('Unable to generate images due to an error', err); }); }); } function queueResTypeImages(resType) { var resTypePlatforms = {}; return buildImagesData() .then(validateSourceImages) .then(queueResourceImages); function buildImagesData() { var deferred = Q.defer(); buildPlatforms.forEach(function(platform) { if (!Platforms[platform]) return; var platformResourceDir = path.join(Settings.resourceDir, platform); var resTypeDir = path.join(platformResourceDir, Settings[resType + 'Dir']); if (!fs.existsSync(platformResourceDir)) { fs.mkdirSync(platformResourceDir); } if (!fs.existsSync(resTypeDir)) { fs.mkdirSync(resTypeDir); } _.forEach(Platforms[platform][resType].images, function(image) { var data = _.clone(image); _.extend(data, { platform: platform, src: path.join(resTypeDir, image.name), nodeName: Platforms[platform][resType].nodeName, nodeAttributes: Platforms[platform][resType].nodeAttributes, resType: resType }); images.push(data); }); }); deferred.resolve(); return deferred.promise; } function validateSourceImages() { var deferred = Q.defer(); var validSourceFiles = _.map(Settings.sourceExtensions, function(ext) { return Settings[resType + 'SourceFile'] + '.' + ext; }); images.forEach(function(image) { if (resTypePlatforms[image.platform]) return; resTypePlatforms[image.platform] = { platform: image.platform }; }); _.each(resTypePlatforms, function(resTypePlatform) { for (var x = 0; x < validSourceFiles.length; x += 1) { var globalSourceFile = path.join(appDirectory, Settings.resourceDir, validSourceFiles[x]); var platformSourceFile = path.join(appDirectory, Settings.resourceDir, resTypePlatform.platform, validSourceFiles[x]); if (fs.existsSync(platformSourceFile)) { resTypePlatform.sourceFilePath = platformSourceFile; resTypePlatform.sourceFilename = resTypePlatform.platform + '/' + validSourceFiles[x]; break; } else if (fs.existsSync(globalSourceFile)) { resTypePlatform.sourceFilePath = globalSourceFile; resTypePlatform.sourceFilename = validSourceFiles[x]; break; } } if (!resTypePlatform.sourceFilePath || sourceFiles[resTypePlatform.sourceFilePath]) return; sourceFiles[resTypePlatform.sourceFilePath] = { filePath: resTypePlatform.sourceFilePath, filename: resTypePlatform.sourceFilename }; }); var missingPlatformSources = _.filter(resTypePlatforms, function(resTypePlatform) { return !resTypePlatform.sourceFilePath; }); if (missingPlatformSources.length) { var notFoundDirs = ['resources']; missingPlatformSources.forEach(function(missingPlatformSource) { notFoundDirs.push('resources/' + missingPlatformSource.platform); }); var msg = resType + ' source file not found in '; if (notFoundDirs.length > 1) { msg += 'any of these directories: ' + notFoundDirs.join(', '); } else { msg += 'the resources directory'; } log.error(msg); log.error('valid ' + resType + ' source files: ' + validSourceFiles.join(', ')); deferred.reject('MISSING_SOURCE_FILE'); } deferred.resolve(); return deferred.promise; } function queueResourceImages() { var promises = []; _.each(resTypePlatforms, function(resTypePlatform) { if (!resTypePlatform.sourceFilePath) return; var deferred = Q.defer(); promises.push(deferred.promise); fs.readFile(resTypePlatform.sourceFilePath, function(err, buf) { if (err) { deferred.reject('Error reading ' + resTypePlatform.sourceFilePath); } else { try { sourceFiles[resTypePlatform.sourceFilePath].imageId = crc.crc32(buf).toString(16); var resImages = _.filter(images, function(image) { return image.resType === resType; }); resImages.forEach(function(image) { if (image.platform === resTypePlatform.platform) { image.sourceFilePath = resTypePlatform.sourceFilePath; var sourceFile = sourceFiles[image.sourceFilePath]; var tmpFilename = sourceFile.imageId + '-' + image.width + 'x' + image.height + '.png'; image.imageId = sourceFile.imageId; image.tmpPath = path.join(tmpDir, tmpFilename); if (resType === 'splash') { if (!((image.width >= image.height && generateLandscape) || (image.height >= image.width && generatePortrait))) { image.skip = true; return; } } if (Settings.cacheImages && fs.existsSync(image.tmpPath)) { log.info(image.resType + ' ' + image.platform + ' ' + image.name + ' (' + image.width + 'x' + image.height + ') from cache'); } else { loadCachedSourceImageData(sourceFile); /* if (sourceFile.cachedData && !sourceFile.cachedData.vector && (sourceFile.cachedData.width < image.width || sourceFile.cachedData.height < image.height)) { image.skip = true; log.error(image.resType + ' ' + image.platform + ' ' + image.name + ' (' + image.width + 'x' + image.height + ') skipped, source image ' + sourceFile.filename + ' (' + sourceFile.cachedData.width + 'x' + sourceFile.cachedData.height + ') too small'); } else { */ sourceFile.upload = true; generateQueue.push(image); //} } } }); deferred.resolve(); } catch (e) { deferred.reject('Error loading ' + resTypePlatform.sourceFilePath + ' md5: ' + e); } } }); }); return Q.all(promises); } } function loadSourceImages() { var promises = []; _.each(sourceFiles, function(sourceFile) { if (!sourceFile.upload) return; var deferred = Q.defer(); log.info(' uploading ' + sourceFile.filename + '...'); var postData = { url: Settings.apiUrl + Settings.apiUploadPath, formData: { image_id: sourceFile.imageId, // eslint-disable-line camelcase src: fs.createReadStream(sourceFile.filePath) }, proxy: Utils.getProxy() }; request.post(postData, function(err, httpResponse, body) { function reject(msg) { try { msg += JSON.parse(body).Error; } catch (e) { msg += body || ''; } deferred.reject(msg); } if (err) { var msg = 'Failed to upload source image: '; if (err.code === 'ENOTFOUND') { msg += 'requires network connection'; } else { msg += err; } deferred.reject(msg); } else if (!httpResponse) { reject('Invalid http response'); } else if (parseInt(httpResponse.statusCode, 10) >= 500) { reject('Image server temporarily unavailable: '); } else if (parseInt(httpResponse.statusCode, 10) === 404) { reject('Image server unavailable: '); } else if (parseInt(httpResponse.statusCode, 10) > 200) { reject('Invalid upload: '); } else { try { var d = JSON.parse(body); sourceFile.width = d.Width; sourceFile.height = d.Height; sourceFile.vector = d.Vector; if (sourceFile.vector) { log.info(sourceFile.filename + ' (vector image) upload complete'); } else { log.info(sourceFile.filename + ' (' + d.Width + 'x' + d.Height + ') upload complete'); } cacheSourceImageData(sourceFile); deferred.resolve(); } catch (e) { reject('Error parsing upload response: '); } } }); promises.push(deferred.promise); }); return Q.all(promises); } function cacheSourceImageData(sourceFile) { try { var data = JSON.stringify({ width: sourceFile.width, height: sourceFile.height, vector: sourceFile.vector, version: 1 }); var cachedImagePath = path.join(tmpDir, sourceFile.imageId + '.json'); fs.writeFile(cachedImagePath, data, function(err) { if (err) log.error('Error writing cacheSourceImageData: ' + err); }); } catch (e) { log.error('Error cacheSourceImageData: ' + e); } } function loadCachedSourceImageData(sourceFile) { if (Settings.cacheImages && !sourceFile.cachedData && sourceFile.cachedData !== false) { try { var cachedImagePath = path.join(tmpDir, sourceFile.imageId + '.json'); sourceFile.cachedData = JSON.parse(fs.readFileSync(cachedImagePath)); } catch (e) { sourceFile.cachedData = false; } } } function generateResourceImages() { var deferred = Q.defer(); // https://github.com/ferentchak/QThrottle var max = Settings.generateThrottle - 1; var outstanding = 0; function catchingFunction(value) { deferred.notify(value); outstanding -= 1; if (generateQueue.length) { outstanding += 1; generateResourceImage(generateQueue.pop()) .then(catchingFunction) .fail(deferred.reject); } else if (outstanding === 0) { deferred.resolve(); } } if (generateQueue.length) { while (max-- && generateQueue.length) { // eslint-disable-line no-plusplus generateResourceImage(generateQueue.pop()) .then(catchingFunction) .fail(deferred.reject); outstanding += 1; } } else { deferred.resolve(); } return deferred.promise; } function generateResourceImage(image) { var deferred = Q.defer(); var sourceFile = sourceFiles[image.sourceFilePath]; if (!sourceFile.vector && (sourceFile.width < image.width || sourceFile.height < image.height)) { image.skip = true; log.error(image.resType + ' ' + image.platform + ' ' + image.name + ' (' + image.width + 'x' + image.height + ') skipped, source image ' + sourceFile.filename + ' (' + sourceFile.width + 'x' + sourceFile.height + ') too small'); deferred.resolve(); } else if (generatingImages[image.tmpPath]) { log.info(image.resType + ' ' + image.platform + ' ' + image.name + ' (' + image.width + 'x' + image.height + ') generated'); deferred.resolve(); } else { log.info(' generating ' + image.resType + ' ' + image.platform + ' ' + image.name + ' (' + image.width + 'x' + image.height + ')...'); generatingImages[image.tmpPath] = true; var postData = { url: Settings.apiUrl + Settings.apiTransformPath, formData: { image_id: image.imageId, // eslint-disable-line camelcase name: image.name, platform: image.platform, width: image.width, height: image.height, res_type: image.resType, // eslint-disable-line camelcase crop: 'center', encoding: 'png' }, proxy: Utils.getProxy() }; var wr = fs.createWriteStream(image.tmpPath, { flags: 'w' }); wr.on('error', function(err) { log.error('Error copying to ' + image.tmpPath + ': ' + err); deferred.resolve(); }); wr.on('finish', function() { if (!image.skip) { log.info(image.resType + ' ' + image.platform + ' ' + image.name + ' (' + image.width + 'x' + image.height + ') generated'); deferred.resolve(); } }); request.post(postData, function(err, httpResponse, body) { function reject(msg) { image.skip = true; try { delete generatingImages[image.tmpPath]; wr.close(); fs.unlink(image.tmpPath); } catch (err) {} // eslint-disable-line no-empty try { msg += JSON.parse(body).Error; } catch (e) { msg += body || ''; } deferred.reject(msg); } if (err || !httpResponse) { reject('Failed to generate image: ' + err); } else if (parseInt(httpResponse.statusCode, 10) >= 500) { reject('Image transformation server temporarily unavailable: '); } else if (parseInt(httpResponse.statusCode, 10) > 200) { reject('Invalid transformation: '); } }) .pipe(wr); } return deferred.promise; } function loadResourceImages() { var promises = []; images.forEach(function(image) { if (!image.tmpPath || !fs.existsSync(image.tmpPath)) return; var deferred = Q.defer(); promises.push(deferred.promise); var rd = fs.createReadStream(image.tmpPath); rd.on('error', function(err) { deferred.reject('Unable to read generated image: ' + err); }); var wr = fs.createWriteStream(image.src, { flags: 'w' }); wr.on('error', function(err) { deferred.reject('Unable to copy to ' + image.src + ': ' + err); }); wr.on('finish', function() { image.isValid = true; deferred.resolve(); }); rd.pipe(wr); }); return Q.all(promises); } function getConfigData(appDirectory) { var deferred = Q.defer(); fs.readFile(path.join(appDirectory, Settings.configFile), function(err, data) { if (err) { log.error('Error reading config file: ' + err); deferred.reject(); return; } try { var parser = new xml2js.Parser(); parser.parseString(data, onXmlParse); } catch (e) { log.error('Error xml2js parseString: ' + e); deferred.reject(); } }); function onXmlParse(err, parsedData) { if (err) { log.error('Error parsing config file: ' + err); deferred.reject(); return; } configData = parsedData; deferred.resolve(configData); } return deferred.promise; } function updateConfigData() { images = _.filter(images, function(image) { return image.isValid && !image.skip; }); if (!images.length || !configData) return; clearResourcesNodes(); images.forEach(updateImageNode); buildDefaultIconNode(); buildPreferenceSplashNodes(); function clearResourcesNodes() { images.forEach(function(image) { if (!image) return; var platformConfigData = getPlatformConfigData(image.platform); if (platformConfigData && platformConfigData[image.resType]) { delete platformConfigData[image.resType]; } }); } function updateImageNode(image) { if (!image) return; if (!configData.widget.platform) { configData.widget.platform = []; } var platformConfigData = getPlatformConfigData(image.platform); if (!platformConfigData) { configData.widget.platform.push({ $: { name: image.platform } }); platformConfigData = getPlatformConfigData(image.platform); } if (!platformConfigData[image.nodeName]) { platformConfigData[image.nodeName] = []; } var node = getResourceConfigNode(platformConfigData, image.nodeName, image.src); if (!node) { node = { $: {} }; platformConfigData[image.nodeName].push(node); } image.nodeAttributes.forEach(function(nodeAttribute) { node.$[nodeAttribute] = image[nodeAttribute]; }); } function buildDefaultIconNode() { var currentSize = 0; var defaultIcon; images.forEach(function(image) { if (image && image.resType === 'icon' && image.width > currentSize && image.width <= Settings.defaultMaxIconSize) { currentSize = image.width; defaultIcon = image; } }); if (defaultIcon) { configData.widget.icon = [ { $: { src: defaultIcon.src } } ]; } } function buildPreferenceSplashNodes() { var hasAndroidSplash = _.find(images, function(image) { return image.platform === 'android' && image.resType === 'splash'; }); if (hasAndroidSplash) { if (!configData.widget.preference) { configData.widget.preference = []; } var hasScreenPref = _.find(configData.widget.preference, function(d) { return d && d.$ && d.$.name === 'SplashScreen'; }); if (!hasScreenPref) { configData.widget.preference.push({ $: { name: 'SplashScreen', value: 'screen' } }); } var hasDelayPref = _.find(configData.widget.preference, function(d) { return d && d.$ && d.$.name === 'SplashScreenDelay'; }); if (!hasDelayPref) { configData.widget.preference.push({ $: { name: 'SplashScreenDelay', value: '3000' } }); } } } return writeConfigData(appDirectory, configData); } function getOrientationConfigData() { if (configData.widget && configData.widget.preference) { var n = _.find(configData.widget.preference, function(d) { return d && d.$ && d.$.name && d.$.name.toLowerCase() === 'orientation'; }); if (n && n.$ && n.$.value) { return n.$.value.toLowerCase(); } } return 'default'; } function getPlatformConfigData(platform) { if (configData.widget && configData.widget.platform) { return _.find(configData.widget.platform, function(d) { return d && d.$ && d.$.name === platform; }); } } function getResourceConfigNode(platformData, nodeName, src) { if (platformData[nodeName]) { return _.find(platformData[nodeName], function(d) { return d && d.$ && d.$.src === src; }); } } function writeConfigData(appDirectory, configData) { var deferred = Q.defer(); var builder = new xml2js.Builder(); var xmlString = builder.buildObject(configData); fs.writeFile(path.join(appDirectory, Settings.configFile), xmlString, function(err) { if (err) { var msg = 'Error writing config data: ' + err; log.error(msg); return deferred.reject(msg); } deferred.resolve(); }); return deferred.promise; } function addIonicIcons(appDirectory, platform, forceAddResources) { switch (platform) { case 'android': case 'ios': break; default: platform = 'android'; // most likely crosswalk `cordova platform add ./engine/cordova-android` } var promise = null; if (forceAddResources) { log.info('forceAddResources'); promise = copyIconFilesIntoResources(appDirectory, forceAddResources); } else { promise = Q(); } return promise .then(function() { return getConfigData(appDirectory); }) .then(function(data) { if (!data.widget.platform || (data.widget.platform.length === 1 && data.widget.platform[0]['$'].name !== platform)) { log.debug('Adding icons for platform:'.blue.bold, platform.green); addPlatformIcons(platform, data); addSplashScreenPreferences(data); return writeConfigData(appDirectory, data); } }); } function hasExistingResources(appDirectory) { // Check resources // resources/icon.png // resources/splash.png // resources/ios/ // resources/android var hasResources = fs.existsSync(path.join(appDirectory, 'resources')); var hasAndroidResources = fs.existsSync(path.join(appDirectory, 'resources', 'android')); var hasIosResources = fs.existsSync(path.join(appDirectory, 'resources', 'ios')); var hasIconResource = fs.existsSync(path.join(appDirectory, 'resources', 'icon.png')); var hasSplashResource = fs.existsSync(path.join(appDirectory, 'resources', 'splash.png')); var hasAnyResources = hasResources || hasAndroidResources || hasIosResources || hasIconResource || hasSplashResource; return hasAnyResources; } function copyIconFilesIntoResources(appDirectory, forceAddResources) { var unzipPath = path.join(appDirectory, 'resources'); if (hasExistingResources(appDirectory) && !forceAddResources) { // log.info('Not copying over default resources, the resources directory already exist. // Please pass the --force argument to overwrite that folder'.red.bold); return Q(); } var ionicResourcesUrl = 'https://github.com/driftyco/ionic-default-resources/archive/master.zip'; log.debug('uzip to: ', unzipPath); // log.info('Downloading Default Ionic Resources'.yellow); return Utils.fetchArchive(unzipPath, ionicResourcesUrl) .then(function() { shelljs.config.silent = true; shelljs.mv('resources/ionic-default-resources-master/*', 'resources'); shelljs.rm('-rf', 'resources/ionic-default-resources-master'); // log.info('Done adding default Ionic resources'.yellow); }); } function addPlatformIcons(platform, configData) { var platElem = { $: { name: platform }, icon: [], splash: [] }; if (!configData.widget.platform) { configData.widget.platform = []; } configData.widget.platform.push(platElem); Platforms[platform].icon.images.forEach(function(image) { var iconDir = ['resources/', platform, '/icon/', image.name].join(''); if (platform === 'android') { platElem.icon.push({ $: { src: iconDir, density: image.density } }); } else { platElem.icon.push({ $: { src: iconDir, width: image.width, height: image.height } }); } }); Platforms[platform].splash.images.forEach(function(image) { // resources/ios/splash/ var splashDir = ['resources/', platform, '/splash/', image.name].join(''); if (platform === 'android') { platElem.splash.push({ $: { src: splashDir, density: image.density } }); } else { platElem.splash.push({ $: { src: splashDir, height: image.height, width: image.width } }); } }); } function addSplashScreenPreferences(configData) { var hasSplashScreen = false; var hasSplashScreenDelay = false; // Check for splash screen stuff // <preference name="SplashScreen" value="screen"/> // <preference name="SplashScreenDelay" value="3000"/> if (!configData.widget.preference) { configData.widget.preference = []; } configData.widget.preference.forEach(function(pref) { if (pref.$.name === 'SplashScreen') { hasSplashScreen = true; } if (pref.$.name === 'SplashScreenDelay') { hasSplashScreenDelay = true; } }); if (!hasSplashScreen) { configData.widget.preference.push({ $: { name: 'SplashScreen', value: 'screen' } }); } if (!hasSplashScreenDelay) { configData.widget.preference.push({ $: { name: 'SplashScreenDelay', value: '3000' } }); } } module.exports = { Settings: Settings, Platforms: Platforms, generate: generate, addIonicIcons: addIonicIcons, copyIconFilesIntoResources: copyIconFilesIntoResources, getConfigData: getConfigData, hasExistingResources: hasExistingResources, writeConfigData: writeConfigData };