UNPKG

ionic

Version:

A tool for creating and developing Ionic Framework mobile apps.

376 lines (313 loc) • 10.8 kB
require('colors'); var fs = require('fs'); var archiver = require('archiver'); var ConfigXml = require('./config-xml'); var Multibar = require('./multibar'); var path = require('path'); var Q = require('q'); var shelljs = require('shelljs'); var log = require('./logging').logger; var Utils = module.exports; Utils.errorHandler = null; Utils.transformCookies = function transformCookies(jar) { if (!jar) { throw new Error('You parse out cookies if they are null'); } return jar.map(function(c) { return c.key + '=' + encodeURIComponent(c.value); }).join('; '); }; Utils.retrieveCsrfToken = function retrieveCsrfToken(jar) { var csrftoken = ''; if (!jar || typeof jar == 'undefined' || jar.length === 0) { return ''; } for (var i = 0; i < jar.length; i += 1) { if (jar[i].key === 'csrftoken') { csrftoken = jar[i].value; break; } } return csrftoken; }; /** * Utils.getProxy will return a string that represents a HTTP proxy server * setup by the user in their environment variables. * * No parameters are required * @return {String} or null if no proxy has been setup */ Utils.getProxy = function() { return process.env.PROXY || process.env.HTTP_PROXY || process.env.http_proxy || process.env.proxy || null; } /** * Utils.createArchive will zip up a subdirectory in the app directory * * Utils.createArchive(appDirectory, 'www') makes a zip file at * {appDirectory}/www.zip whose file structure is like www.zip/www/{assets} * * @param {string} appDirectory The app's absolute directory * @param {string} documentRoot Denotation of the subdirectory, e.g. 'www' * * @return {Promise} */ Utils.createArchive = function(appDirectory, documentRoot) { var q = Q.defer(); var zipPath = path.join(appDirectory, documentRoot); log.debug('Now zipping contents of ' + zipPath); if (!fs.existsSync(zipPath)) { q.reject(documentRoot + ' directory cannot be found. Make sure the working directory ' + 'is at the top level of an Ionic project.', 'upload'); } var zipDestination = zipPath + '.zip'; var zip = fs.createWriteStream(zipDestination); var archive = archiver('zip'); archive.pipe(zip); archive.bulk([ { expand: true, cwd: zipPath, src: ['**'] } ]); archive.finalize(function(err) { if (err) { q.reject(['Error uploading: ', err].join('')); } }); zip.on('close', function() { q.resolve(zipDestination); }); return q.promise; }; Utils.fetchArchive = function fetchArchive(targetPath, archiveUrl, isGui) { var os = require('os'); var fs = require('fs'); var path = require('path'); var AdmZip = require('adm-zip'); var q = Q.defer(); // The folder name the project will be downloaded and extracted to var message = ['Downloading:'.bold, archiveUrl].join(' '); log.info(message); var tmpFolder = os.tmpdir(); var tempZipFilePath = path.join(tmpFolder, 'ionic-starter-' + new Date().getTime() + '.zip'); var proxy = Utils.getProxy(); var request = require('request'); request({ url: archiveUrl, rejectUnauthorized: false, encoding: null, proxy: proxy }, function(err, res, body) { if (err) { return q.reject(err); } if (!res) { log.error('Invalid response:'.red.bold, archiveUrl); return q.reject('Unable to fetch response: ' + archiveUrl); } if (parseInt(res.statusCode, 10) !== 200) { if (parseInt(res.statusCode, 10) === 404 || parseInt(res.statusCode, 10) === 406) { log.error('Not found:'.red.bold, archiveUrl, '(' + res.statusCode + ')'); log.error('Please verify the url and try again.'.red.bold); } else { log.error('Invalid response status:'.red.bold, archiveUrl, '(' + res.statusCode + ')'); } q.reject(res); return; } try { fs.writeFileSync(tempZipFilePath, body); var zip = new AdmZip(tempZipFilePath); zip.extractAllTo(targetPath); q.resolve(); } catch (e) { log.debug('fetchArchive request write: ', e); q.reject(e); } }).on('response', function(res) { // Add default flag for CLI - have it attach multibar and upgrade its progress for // simultaneous downloading bars (crosswalk). if (!isGui) { var bar = Multibar.newBar('[:bar] :percent :etas', { complete: '=', incomplete: ' ', width: 30, total: parseInt(res.headers['content-length'], 10) }); res.on('data', function(chunk) { try { bar.tick(chunk.length); } catch (e) {} // eslint-disable-line no-empty }); } }); return q.promise; }; Utils.preprocessOptions = function preprocessOptions(options) { var result = {}; result.appDirectory = options.appDirectory; result.targetPath = options.targetPath || null; result.template = options.template || 'blank'; result.packageName = options.packageName || null; if (!options.appName) { var appNameSplit = options.appDirectory.split('/'); appNameSplit = appNameSplit[appNameSplit.length - 1].split('\\'); options.appName = appNameSplit[appNameSplit.length - 1]; } else { result.appName = options.appName; } result.isCordovaProject = options.isCordovaProject || true; result.setupSass = options.setupSass || true; return result; }; Utils.preprocessCliOptions = function preprocessCliOptions(argv) { log.debug('Utils.preprocessCliOptions', argv); try { var options = {}; // 0 1 // ionic start facebooker // Grab the app's relative directory name options.appDirectory = argv._[1]; // Grab the name of the app from -a or --app. Defaults to appDirectory if none provided options.appName = argv.appname || argv['app-name'] || argv.a; if (!options.appName) { var appNameSplit = options.appDirectory.split('/'); appNameSplit = appNameSplit[appNameSplit.length - 1].split('\\'); options.appName = appNameSplit[appNameSplit.length - 1]; } // get a packge name, like com.ionic.myapp options.packageName = argv.id || argv.i; options.ionicAppId = argv['io-app-id']; options.isCordovaProject = (argv.cordova !== false && !(argv.w || argv['no-cordova'])); // start project template can come from cmd line args -t, --template, or the 3rd arg, and defaults to tabs options.template = (argv.template || argv.t || argv._[2] || 'tabs'); // figure out the full path options.targetPath = Utils.getProjectDirectory(options); options.typescript = argv.typescript || argv.ts; options.v2 = argv.v2 || argv.v; options.skipNpm = argv['skip-npm']; // internal commands for changing which branches the starter should use options.wrapperBranchName = argv.wrapperBranchName; options.starterBranchName = argv.starterBranchName; return options; } catch (ex) { log.debug('An error occrured processing the CLI arguments', ex); Utils.fail('There was an error parsing out options from the Command Line'); } }; Utils.getProjectDirectory = function getProjectDirectory(options) { return path.resolve(options.appDirectory); }; Utils.getContentSrc = function getContentSrc(appDirectory) { log.debug('Utils.getContentSrc', appDirectory); var contentSrc; try { var fs = require('fs'); var path = require('path'); var configXmlPath = path.join(appDirectory, 'config.xml'); if (!fs.existsSync(configXmlPath)) { return 'index.html'; } ConfigXml.setConfigXml(appDirectory, { resetContent: true, errorWhenNotFound: false }); var configString = fs.readFileSync(configXmlPath, { encoding: 'utf8' }); var xml2js = require('xml2js'); var parseString = xml2js.parseString; parseString(configString, function(err, jsonConfig) { if (err) { return Utils.fail('Error parsing config.xml: ' + err); } try { contentSrc = jsonConfig.widget.content[0].$.src; } catch (e) { return Utils.fail('Error parsing ' + configXmlPath + ': ' + e.stack); } }); } catch (e) { return Utils.fail('Error loading ' + configXmlPath + ': ' + e.stack); } return contentSrc; }; Utils.mergeOptions = function mergeOptions(obj1, obj2) { var obj3 = {}; for (var attrname in obj1) { if (obj1.hasOwnProperty(attrname)) { obj3[attrname] = obj1[attrname]; } } for (var attr in obj2) { if (obj2.hasOwnProperty(attr)) { obj3[attr] = obj2[attr]; } } return obj3; }; Utils.fail = function fail(msg, taskHelp) { try { log.debug('Utils.fail', msg, taskHelp); log.debug('Utils.fail stack', msg.stack); // If an error handler is set, use it. Otherwise, just print basic info. if (Utils.errorHandler) { log.debug('Utils.errorHandler is set, calling that now'); return Utils.errorHandler(msg, taskHelp); } log.error('An error occurred in Ionic App Lib and no error handler was set.'); log.error(msg); process.exit(1); return ''; } catch (ex) { log.debug('Utils.fail: ', ex); } }; Utils.gulpInstalledGlobally = function gulpInstalledGlobally() { var result = shelljs.exec('gulp -v', { silent: true }); if (result.code !== 0) { return false; } return true; }; Utils.cordovaInstalled = function cordovaInstalled() { var Info = require('./info'); var info = {}; Info.getCordovaInfo(info); return info.cordova !== 'Not installed'; }; Utils.findIonicRoot = function findIonicRoot(dir) { var IonicProject = require('./project'); if (!dir) { var pwd = process.env.PWD; var cwd = process.cwd(); if (pwd && pwd !== cwd && pwd !== 'undefined') { return Utils.findIonicRoot(pwd) || Utils.findIonicRoot(cwd); } return Utils.findIonicRoot(cwd); } for (var i = 0; i < 1000; i += 1) { if (fs.existsSync(path.join(dir, IonicProject.PROJECT_FILE))) { return dir; } // TODO: deprecated if (fs.existsSync(path.join(dir, IonicProject.OLD_PROJECT_FILE))) { return dir; } // TODO: deprecated if (fs.existsSync(path.join(dir, 'ionic.config.js'))) { return dir; } var parentDir = path.normalize(path.join(dir, '..')); // Detect fs root. if (parentDir === dir) { return null; } dir = parentDir; } log.error('Hit an unhandled case in utils.findIonicRoot'); return null; }; Utils.cdIonicRoot = function cdIonicRoot() { var IonicProject = require('./project'); log.debug('Looking up Ionic root, cwd:', process.cwd()); var rootDir = this.findIonicRoot(); if (!rootDir) { log.error('Couldn\'t find ' + IonicProject.PROJECT_FILE + ' file. Are you in an Ionic project?'); process.exit(1); } log.debug('Ionic root directory: ', process.cwd()); process.env.PWD = rootDir; process.chdir(rootDir); return rootDir; };