From a31f4148af338cec13afa8805e3a15de82f42d12 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Wed, 20 Nov 2013 08:30:31 -0800 Subject: [PATCH 0001/1100] Initial commit --- LICENSE | 20 ++++++++++++++++++++ README.md | 4 ++++ 2 files changed, 24 insertions(+) create mode 100644 LICENSE create mode 100644 README.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..5387be979f --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Drifty + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000000..00cc6c4f0b --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +ionic-cli +========= + +The Ionic Framework command line utility From f462e727f27dea9272e0bfba2146877aa52f0b40 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Wed, 20 Nov 2013 10:34:52 -0600 Subject: [PATCH 0002/1100] Inital commit --- ionic.js | 90 ++++++++++++++++++++++++++ lib/ionic/start.js | 58 +++++++++++++++++ lib/ionic/task.js | 21 ++++++ npm-debug.log | 154 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 18 ++++++ template/index.html | 0 template/t | 0 7 files changed, 341 insertions(+) create mode 100644 ionic.js create mode 100644 lib/ionic/start.js create mode 100644 lib/ionic/task.js create mode 100644 npm-debug.log create mode 100644 package.json create mode 100644 template/index.html create mode 100644 template/t diff --git a/ionic.js b/ionic.js new file mode 100644 index 0000000000..5585912d22 --- /dev/null +++ b/ionic.js @@ -0,0 +1,90 @@ +/* + __ __ +| / \ |\ | | / ` +| \__/ | \| | \__, + +http://ionicframework.com/ + +A utility for starting and administering Ionic based mobile app projects. +Licensed under the MITlicense. See LICENSE For more. + +Copyright 2013 Drifty (http://drifty.com/) +*/ + +var IonicStartTask = require('./lib/ionic/start.js').IonicStartTask; + +var argv = require('optimist').argv; + +var TASKS = [ + { + title: 'start', + name: 'start', + usage: 'appname', + task: IonicStartTask + } +]; + +Ionic = function() {}; + +Ionic.prototype = { + _tryBuildingTask: function() { + if(argv._.length == 0) { + return false; + } + var taskName = argv._[0]; + + return this._getTaskWithName(taskName); + }, + + _getTaskWithName: function(name) { + for(var i = 0; i < TASKS.length; i++) { + var t = TASKS[i]; + if(t.name === name) { + return t; + } + } + }, + + _printGenericUsage: function() { + this._printIonic(); + process.stderr.write('Usage: ionic task args\n\n===============\n\nAvailable tasks:\n\n'); + + for(var i = 0; i < TASKS.length; i++) { + var task = TASKS[i]; + process.stderr.write(' ' + task.name + '\t\t' + task.task.HELP_LINE + '\n'); + } + + process.exit(1); + }, + + _printIonic: function() { + process.stdout.write('\n __ __ \n'); + process.stdout.write('| / \\ |\\ | | / `\n' + '| \\__/ | \\| | \\__,\n\n'); + }, + + _loadTaskRunner: function(which) { + + }, + + run: function() { + var task = this._tryBuildingTask(); + if(!task) { + return this._printGenericUsage(); + } + + console.log('Running', task.title, 'task...') + + var taskObj = new task.task(); + taskObj.run(this); + }, + + fail: function(msg) { + process.stderr.write(msg + '\n'); + process.exit(1); + }, + +}; + + +var ionic = new Ionic(); +ionic.run(); diff --git a/lib/ionic/start.js b/lib/ionic/start.js new file mode 100644 index 0000000000..e6904f76e0 --- /dev/null +++ b/lib/ionic/start.js @@ -0,0 +1,58 @@ +var fs = require('fs'), + ncp = require('ncp').ncp, + path = require('path'), + IonicTask = require('./task').IonicTask; + +var argv = require('optimist').argv; + +var IonicStartTask = function() { +} + +IonicStartTask.HELP_LINE = 'Start a new Ionic project with the given name.'; + +IonicStartTask.prototype = new IonicTask(); + +IonicStartTask.prototype._printUsage = function() { + process.stderr.write('ionic start appname\n'); +} + +IonicStartTask.prototype.run = function(ionic) { + if(argv._.length < 2) { + ionic.fail('No app name specified'); + } + + this.appName = argv._[1]; + this.targetPath = path.resolve(this.appName); + + // Make sure to create this, or ask them if they want to override it + if(this._checkTargetPath() === false) { + process.stderr.write('Not continuing.'); + process.exit(1); + } + + console.log('Creating Ionic app in folder', this.targetPath); + + this._writeTemplateFolder(); +}; + +IonicStartTask.prototype._writeTemplateFolder = function() { + console.log('Copying template to', this.targetPath); + ncp('template', this.appName, function(err) { + if(err) { + this._fail('Unable to build starter folder', err); + } + }); +}; + +IonicStartTask.prototype._checkTargetPath = function() { + if(fs.existsSync(this.targetPath)) { + var resp = this.ask('The ' + this.targetPath + ' directory already exists. Overwrite files? (y/n)') + if(resp === 'y') { + return true; + } + return false; + } + return true; +}; + +exports.IonicStartTask = IonicStartTask; \ No newline at end of file diff --git a/lib/ionic/task.js b/lib/ionic/task.js new file mode 100644 index 0000000000..20b74b4c1f --- /dev/null +++ b/lib/ionic/task.js @@ -0,0 +1,21 @@ +var fs = require('fs'); + +var IonicTask = function() { +}; + +IonicTask.prototype = { + // Prompt the user for a response + ask: function(question) { + var response; + + process.stdout.write(question + ' '); + process.stdin.resume(); + response = fs.readSync(process.stdin.fd, 100, 0, "utf8"); + process.stdin.pause(); + return response[0].trim(); + }, + run: function(ionic) { + } +}; + +exports.IonicTask = IonicTask; \ No newline at end of file diff --git a/npm-debug.log b/npm-debug.log new file mode 100644 index 0000000000..aee84a0a4b --- /dev/null +++ b/npm-debug.log @@ -0,0 +1,154 @@ +0 info it worked if it ends with ok +1 verbose cli [ 'node', '/usr/local/bin/npm', 'publish' ] +2 info using npm@1.2.32 +3 info using node@v0.10.12 +4 verbose publish [ '.' ] +5 verbose read json /Users/driftyadmin/git/ionic/tools/ionic/package.json +6 warn package.json ionic@0.9.05 No repository field. +7 warn package.json ionic@0.9.05 No readme data. +8 verbose cache add [ '.', null ] +9 verbose cache add name=undefined spec="." args=[".",null] +10 verbose parsed url { protocol: null, +10 verbose parsed url slashes: null, +10 verbose parsed url auth: null, +10 verbose parsed url host: null, +10 verbose parsed url port: null, +10 verbose parsed url hostname: null, +10 verbose parsed url hash: null, +10 verbose parsed url search: null, +10 verbose parsed url query: null, +10 verbose parsed url pathname: '.', +10 verbose parsed url path: '.', +10 verbose parsed url href: '.' } +11 silly lockFile 3a52ce78- . +12 verbose lock . /Users/driftyadmin/.npm/3a52ce78-.lock +13 verbose read json package.json +14 verbose tar pack [ '/var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz', +14 verbose tar pack '.' ] +15 verbose tarball /var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz +16 verbose folder . +17 info prepublish ionic@0.9.05 +18 silly lockFile 1f1177db-tar tar://. +19 verbose lock tar://. /Users/driftyadmin/.npm/1f1177db-tar.lock +20 silly lockFile 74dec091-97226-0-8251332442741841-tmp-tgz tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz +21 verbose lock tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz /Users/driftyadmin/.npm/74dec091-97226-0-8251332442741841-tmp-tgz.lock +22 silly lockFile 1f1177db-tar tar://. +23 silly lockFile 1f1177db-tar tar://. +24 silly lockFile 74dec091-97226-0-8251332442741841-tmp-tgz tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz +25 silly lockFile 74dec091-97226-0-8251332442741841-tmp-tgz tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz +26 verbose tar unpack /var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz +27 silly lockFile c4b8a2e3-97226-0-8251332442741841-package tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package +28 verbose lock tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package /Users/driftyadmin/.npm/c4b8a2e3-97226-0-8251332442741841-package.lock +29 silly lockFile 74dec091-97226-0-8251332442741841-tmp-tgz tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz +30 verbose lock tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz /Users/driftyadmin/.npm/74dec091-97226-0-8251332442741841-tmp-tgz.lock +31 silly gunzTarPerm modes [ '755', '644' ] +32 silly gunzTarPerm extractEntry package.json +33 silly gunzTarPerm extractEntry ionic.js +34 silly gunzTarPerm extractEntry lib/ionic/start.js +35 silly gunzTarPerm extractEntry lib/ionic/task.js +36 silly gunzTarPerm extractEntry template/index.html +37 silly gunzTarPerm extractEntry template/t +38 verbose read json /var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package/package.json +39 silly lockFile c4b8a2e3-97226-0-8251332442741841-package tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package +40 silly lockFile c4b8a2e3-97226-0-8251332442741841-package tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package +41 silly lockFile 74dec091-97226-0-8251332442741841-tmp-tgz tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz +42 silly lockFile 74dec091-97226-0-8251332442741841-tmp-tgz tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz +43 verbose from cache /var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package/package.json +44 verbose tar pack [ '/Users/driftyadmin/.npm/ionic/0.9.05/package.tgz', +44 verbose tar pack '/var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package' ] +45 verbose tarball /Users/driftyadmin/.npm/ionic/0.9.05/package.tgz +46 verbose folder /var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package +47 silly lockFile c4b8a2e3-97226-0-8251332442741841-package tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package +48 verbose lock tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package /Users/driftyadmin/.npm/c4b8a2e3-97226-0-8251332442741841-package.lock +49 silly lockFile be49939d-min-npm-ionic-0-9-05-package-tgz tar:///Users/driftyadmin/.npm/ionic/0.9.05/package.tgz +50 verbose lock tar:///Users/driftyadmin/.npm/ionic/0.9.05/package.tgz /Users/driftyadmin/.npm/be49939d-min-npm-ionic-0-9-05-package-tgz.lock +51 silly lockFile c4b8a2e3-97226-0-8251332442741841-package tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package +52 silly lockFile c4b8a2e3-97226-0-8251332442741841-package tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package +53 silly lockFile be49939d-min-npm-ionic-0-9-05-package-tgz tar:///Users/driftyadmin/.npm/ionic/0.9.05/package.tgz +54 silly lockFile be49939d-min-npm-ionic-0-9-05-package-tgz tar:///Users/driftyadmin/.npm/ionic/0.9.05/package.tgz +55 silly lockFile 1478df52-tyadmin-npm-ionic-0-9-05-package /Users/driftyadmin/.npm/ionic/0.9.05/package +56 verbose lock /Users/driftyadmin/.npm/ionic/0.9.05/package /Users/driftyadmin/.npm/1478df52-tyadmin-npm-ionic-0-9-05-package.lock +57 silly lockFile 1478df52-tyadmin-npm-ionic-0-9-05-package /Users/driftyadmin/.npm/ionic/0.9.05/package +58 silly lockFile 1478df52-tyadmin-npm-ionic-0-9-05-package /Users/driftyadmin/.npm/ionic/0.9.05/package +59 verbose tar unpack /Users/driftyadmin/.npm/ionic/0.9.05/package.tgz +60 silly lockFile ce6e26b9-tyadmin-npm-ionic-0-9-05-package tar:///Users/driftyadmin/.npm/ionic/0.9.05/package +61 verbose lock tar:///Users/driftyadmin/.npm/ionic/0.9.05/package /Users/driftyadmin/.npm/ce6e26b9-tyadmin-npm-ionic-0-9-05-package.lock +62 silly lockFile be49939d-min-npm-ionic-0-9-05-package-tgz tar:///Users/driftyadmin/.npm/ionic/0.9.05/package.tgz +63 verbose lock tar:///Users/driftyadmin/.npm/ionic/0.9.05/package.tgz /Users/driftyadmin/.npm/be49939d-min-npm-ionic-0-9-05-package-tgz.lock +64 silly gunzTarPerm modes [ '755', '644' ] +65 silly gunzTarPerm extractEntry package.json +66 silly gunzTarPerm extractEntry ionic.js +67 silly gunzTarPerm extractEntry lib/ionic/start.js +68 silly gunzTarPerm extractEntry lib/ionic/task.js +69 silly gunzTarPerm extractEntry template/index.html +70 silly gunzTarPerm extractEntry template/t +71 verbose read json /Users/driftyadmin/.npm/ionic/0.9.05/package/package.json +72 silly lockFile ce6e26b9-tyadmin-npm-ionic-0-9-05-package tar:///Users/driftyadmin/.npm/ionic/0.9.05/package +73 silly lockFile ce6e26b9-tyadmin-npm-ionic-0-9-05-package tar:///Users/driftyadmin/.npm/ionic/0.9.05/package +74 silly lockFile be49939d-min-npm-ionic-0-9-05-package-tgz tar:///Users/driftyadmin/.npm/ionic/0.9.05/package.tgz +75 silly lockFile be49939d-min-npm-ionic-0-9-05-package-tgz tar:///Users/driftyadmin/.npm/ionic/0.9.05/package.tgz +76 verbose from cache /Users/driftyadmin/.npm/ionic/0.9.05/package/package.json +77 verbose chmod /Users/driftyadmin/.npm/ionic/0.9.05/package.tgz 644 +78 verbose chown /Users/driftyadmin/.npm/ionic/0.9.05/package.tgz [ 503, 20 ] +79 silly lockFile 3a52ce78- . +80 silly lockFile 3a52ce78- . +81 silly publish { name: 'ionic', +81 silly publish version: '0.9.05', +81 silly publish preferGlobal: true, +81 silly publish description: 'A tool for creating and building Ionic Framework mobile apps.', +81 silly publish homepage: 'http://ionicframework.com/', +81 silly publish main: 'ionic.js', +81 silly publish scripts: { test: 'echo "Error: no test specified" && exit 1' }, +81 silly publish author: { name: 'Max Lynch', email: 'max@drifty.com' }, +81 silly publish license: 'MIT', +81 silly publish dependencies: { cordova: '>=3.1.0', optimist: '0.0.2', wrench: '1.5.1' }, +81 silly publish readme: 'ERROR: No README data found!', +81 silly publish _id: 'ionic@0.9.05', +81 silly publish dist: { shasum: '901fb6f02fcfb8f2944c61b575ba88f70395b720' }, +81 silly publish _from: '.' } +82 verbose url raw ionic +83 verbose url resolving [ 'https://registry.npmjs.org/', './ionic' ] +84 verbose url resolved https://registry.npmjs.org/ionic +85 info trying registry request attempt 1 at 10:29:58 +86 http PUT https://registry.npmjs.org/ionic +87 http 409 https://registry.npmjs.org/ionic +88 verbose url raw ionic +89 verbose url resolving [ 'https://registry.npmjs.org/', './ionic' ] +90 verbose url resolved https://registry.npmjs.org/ionic +91 info trying registry request attempt 1 at 10:29:58 +92 http GET https://registry.npmjs.org/ionic +93 http 200 https://registry.npmjs.org/ionic +94 verbose uploading [ 'ionic/-/ionic-0.9.05.tgz/-rev/3-eaa605a9ad6fae3bb8c301e3127943db', +94 verbose uploading '/Users/driftyadmin/.npm/ionic/0.9.05/package.tgz' ] +95 verbose url raw ionic/-/ionic-0.9.05.tgz/-rev/3-eaa605a9ad6fae3bb8c301e3127943db +96 verbose url resolving [ 'https://registry.npmjs.org/', +96 verbose url resolving './ionic/-/ionic-0.9.05.tgz/-rev/3-eaa605a9ad6fae3bb8c301e3127943db' ] +97 verbose url resolved https://registry.npmjs.org/ionic/-/ionic-0.9.05.tgz/-rev/3-eaa605a9ad6fae3bb8c301e3127943db +98 info trying registry request attempt 1 at 10:29:59 +99 http PUT https://registry.npmjs.org/ionic/-/ionic-0.9.05.tgz/-rev/3-eaa605a9ad6fae3bb8c301e3127943db +100 http 403 https://registry.npmjs.org/ionic/-/ionic-0.9.05.tgz/-rev/3-eaa605a9ad6fae3bb8c301e3127943db +101 info trying registry request attempt 1 at 10:29:59 +102 http PUT https://registry.npmjs.org/ionic/-/ionic-0.9.05.tgz/-rev/3-eaa605a9ad6fae3bb8c301e3127943db +103 http 403 https://registry.npmjs.org/ionic/-/ionic-0.9.05.tgz/-rev/3-eaa605a9ad6fae3bb8c301e3127943db +104 error publish Error uploading package +105 error Error: forbidden user: drifty not authorized to modify ionic +105 error Added: _attachments.ionic-0.9.05.tgz: ionic/-/ionic-0.9.05.tgz/-rev/3-eaa605a9ad6fae3bb8c301e3127943db +105 error at RegClient. (/usr/local/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:272:14) +105 error at Request.self.callback (/usr/local/lib/node_modules/npm/node_modules/request/index.js:148:22) +105 error at Request.EventEmitter.emit (events.js:98:17) +105 error at Request. (/usr/local/lib/node_modules/npm/node_modules/request/index.js:876:14) +105 error at Request.EventEmitter.emit (events.js:117:20) +105 error at IncomingMessage. (/usr/local/lib/node_modules/npm/node_modules/request/index.js:827:12) +105 error at IncomingMessage.EventEmitter.emit (events.js:117:20) +105 error at _stream_readable.js:910:16 +105 error at process._tickCallback (node.js:415:13) +106 error If you need help, you may report this log at: +106 error +106 error or email it to: +106 error +107 error System Darwin 12.5.0 +108 error command "node" "/usr/local/bin/npm" "publish" +109 error cwd /Users/driftyadmin/git/ionic/tools/ionic +110 error node -v v0.10.12 +111 error npm -v 1.2.32 +112 verbose exit [ 1, true ] diff --git a/package.json b/package.json new file mode 100644 index 0000000000..23b00115a2 --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "ionic", + "version": "0.9.05", + "preferGlobal": true, + "description": "A tool for creating and building Ionic Framework mobile apps.", + "homepage": "http://ionicframework.com/", + "main": "ionic.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Max Lynch ", + "license": "MIT", + "dependencies": { + "cordova": ">=3.1.0", + "optimist": "0.0.2", + "wrench": "1.5.1" + } +} diff --git a/template/index.html b/template/index.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/template/t b/template/t new file mode 100644 index 0000000000..e69de29bb2 From a72f43128adc296cd00fb07a6225942e59c1edcf Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Wed, 20 Nov 2013 10:46:41 -0600 Subject: [PATCH 0003/1100] Fixed package.json --- .gitignore | 1 + lib/ionic/start.js | 2 +- npm-debug.log | 154 --------------------------------------------- package.json | 14 ++++- template/t | 0 5 files changed, 15 insertions(+), 156 deletions(-) create mode 100644 .gitignore delete mode 100644 npm-debug.log delete mode 100644 template/t diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..1ee84da988 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.sw* diff --git a/lib/ionic/start.js b/lib/ionic/start.js index e6904f76e0..9e978bdf93 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -55,4 +55,4 @@ IonicStartTask.prototype._checkTargetPath = function() { return true; }; -exports.IonicStartTask = IonicStartTask; \ No newline at end of file +exports.IonicStartTask = IonicStartTask; diff --git a/npm-debug.log b/npm-debug.log deleted file mode 100644 index aee84a0a4b..0000000000 --- a/npm-debug.log +++ /dev/null @@ -1,154 +0,0 @@ -0 info it worked if it ends with ok -1 verbose cli [ 'node', '/usr/local/bin/npm', 'publish' ] -2 info using npm@1.2.32 -3 info using node@v0.10.12 -4 verbose publish [ '.' ] -5 verbose read json /Users/driftyadmin/git/ionic/tools/ionic/package.json -6 warn package.json ionic@0.9.05 No repository field. -7 warn package.json ionic@0.9.05 No readme data. -8 verbose cache add [ '.', null ] -9 verbose cache add name=undefined spec="." args=[".",null] -10 verbose parsed url { protocol: null, -10 verbose parsed url slashes: null, -10 verbose parsed url auth: null, -10 verbose parsed url host: null, -10 verbose parsed url port: null, -10 verbose parsed url hostname: null, -10 verbose parsed url hash: null, -10 verbose parsed url search: null, -10 verbose parsed url query: null, -10 verbose parsed url pathname: '.', -10 verbose parsed url path: '.', -10 verbose parsed url href: '.' } -11 silly lockFile 3a52ce78- . -12 verbose lock . /Users/driftyadmin/.npm/3a52ce78-.lock -13 verbose read json package.json -14 verbose tar pack [ '/var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz', -14 verbose tar pack '.' ] -15 verbose tarball /var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz -16 verbose folder . -17 info prepublish ionic@0.9.05 -18 silly lockFile 1f1177db-tar tar://. -19 verbose lock tar://. /Users/driftyadmin/.npm/1f1177db-tar.lock -20 silly lockFile 74dec091-97226-0-8251332442741841-tmp-tgz tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz -21 verbose lock tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz /Users/driftyadmin/.npm/74dec091-97226-0-8251332442741841-tmp-tgz.lock -22 silly lockFile 1f1177db-tar tar://. -23 silly lockFile 1f1177db-tar tar://. -24 silly lockFile 74dec091-97226-0-8251332442741841-tmp-tgz tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz -25 silly lockFile 74dec091-97226-0-8251332442741841-tmp-tgz tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz -26 verbose tar unpack /var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz -27 silly lockFile c4b8a2e3-97226-0-8251332442741841-package tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package -28 verbose lock tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package /Users/driftyadmin/.npm/c4b8a2e3-97226-0-8251332442741841-package.lock -29 silly lockFile 74dec091-97226-0-8251332442741841-tmp-tgz tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz -30 verbose lock tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz /Users/driftyadmin/.npm/74dec091-97226-0-8251332442741841-tmp-tgz.lock -31 silly gunzTarPerm modes [ '755', '644' ] -32 silly gunzTarPerm extractEntry package.json -33 silly gunzTarPerm extractEntry ionic.js -34 silly gunzTarPerm extractEntry lib/ionic/start.js -35 silly gunzTarPerm extractEntry lib/ionic/task.js -36 silly gunzTarPerm extractEntry template/index.html -37 silly gunzTarPerm extractEntry template/t -38 verbose read json /var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package/package.json -39 silly lockFile c4b8a2e3-97226-0-8251332442741841-package tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package -40 silly lockFile c4b8a2e3-97226-0-8251332442741841-package tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package -41 silly lockFile 74dec091-97226-0-8251332442741841-tmp-tgz tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz -42 silly lockFile 74dec091-97226-0-8251332442741841-tmp-tgz tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/tmp.tgz -43 verbose from cache /var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package/package.json -44 verbose tar pack [ '/Users/driftyadmin/.npm/ionic/0.9.05/package.tgz', -44 verbose tar pack '/var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package' ] -45 verbose tarball /Users/driftyadmin/.npm/ionic/0.9.05/package.tgz -46 verbose folder /var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package -47 silly lockFile c4b8a2e3-97226-0-8251332442741841-package tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package -48 verbose lock tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package /Users/driftyadmin/.npm/c4b8a2e3-97226-0-8251332442741841-package.lock -49 silly lockFile be49939d-min-npm-ionic-0-9-05-package-tgz tar:///Users/driftyadmin/.npm/ionic/0.9.05/package.tgz -50 verbose lock tar:///Users/driftyadmin/.npm/ionic/0.9.05/package.tgz /Users/driftyadmin/.npm/be49939d-min-npm-ionic-0-9-05-package-tgz.lock -51 silly lockFile c4b8a2e3-97226-0-8251332442741841-package tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package -52 silly lockFile c4b8a2e3-97226-0-8251332442741841-package tar:///var/folders/yc/wb4ct6qn5vl2r6hv8k3mck_w0000gq/T/npm-51998/1384964997226-0.8251332442741841/package -53 silly lockFile be49939d-min-npm-ionic-0-9-05-package-tgz tar:///Users/driftyadmin/.npm/ionic/0.9.05/package.tgz -54 silly lockFile be49939d-min-npm-ionic-0-9-05-package-tgz tar:///Users/driftyadmin/.npm/ionic/0.9.05/package.tgz -55 silly lockFile 1478df52-tyadmin-npm-ionic-0-9-05-package /Users/driftyadmin/.npm/ionic/0.9.05/package -56 verbose lock /Users/driftyadmin/.npm/ionic/0.9.05/package /Users/driftyadmin/.npm/1478df52-tyadmin-npm-ionic-0-9-05-package.lock -57 silly lockFile 1478df52-tyadmin-npm-ionic-0-9-05-package /Users/driftyadmin/.npm/ionic/0.9.05/package -58 silly lockFile 1478df52-tyadmin-npm-ionic-0-9-05-package /Users/driftyadmin/.npm/ionic/0.9.05/package -59 verbose tar unpack /Users/driftyadmin/.npm/ionic/0.9.05/package.tgz -60 silly lockFile ce6e26b9-tyadmin-npm-ionic-0-9-05-package tar:///Users/driftyadmin/.npm/ionic/0.9.05/package -61 verbose lock tar:///Users/driftyadmin/.npm/ionic/0.9.05/package /Users/driftyadmin/.npm/ce6e26b9-tyadmin-npm-ionic-0-9-05-package.lock -62 silly lockFile be49939d-min-npm-ionic-0-9-05-package-tgz tar:///Users/driftyadmin/.npm/ionic/0.9.05/package.tgz -63 verbose lock tar:///Users/driftyadmin/.npm/ionic/0.9.05/package.tgz /Users/driftyadmin/.npm/be49939d-min-npm-ionic-0-9-05-package-tgz.lock -64 silly gunzTarPerm modes [ '755', '644' ] -65 silly gunzTarPerm extractEntry package.json -66 silly gunzTarPerm extractEntry ionic.js -67 silly gunzTarPerm extractEntry lib/ionic/start.js -68 silly gunzTarPerm extractEntry lib/ionic/task.js -69 silly gunzTarPerm extractEntry template/index.html -70 silly gunzTarPerm extractEntry template/t -71 verbose read json /Users/driftyadmin/.npm/ionic/0.9.05/package/package.json -72 silly lockFile ce6e26b9-tyadmin-npm-ionic-0-9-05-package tar:///Users/driftyadmin/.npm/ionic/0.9.05/package -73 silly lockFile ce6e26b9-tyadmin-npm-ionic-0-9-05-package tar:///Users/driftyadmin/.npm/ionic/0.9.05/package -74 silly lockFile be49939d-min-npm-ionic-0-9-05-package-tgz tar:///Users/driftyadmin/.npm/ionic/0.9.05/package.tgz -75 silly lockFile be49939d-min-npm-ionic-0-9-05-package-tgz tar:///Users/driftyadmin/.npm/ionic/0.9.05/package.tgz -76 verbose from cache /Users/driftyadmin/.npm/ionic/0.9.05/package/package.json -77 verbose chmod /Users/driftyadmin/.npm/ionic/0.9.05/package.tgz 644 -78 verbose chown /Users/driftyadmin/.npm/ionic/0.9.05/package.tgz [ 503, 20 ] -79 silly lockFile 3a52ce78- . -80 silly lockFile 3a52ce78- . -81 silly publish { name: 'ionic', -81 silly publish version: '0.9.05', -81 silly publish preferGlobal: true, -81 silly publish description: 'A tool for creating and building Ionic Framework mobile apps.', -81 silly publish homepage: 'http://ionicframework.com/', -81 silly publish main: 'ionic.js', -81 silly publish scripts: { test: 'echo "Error: no test specified" && exit 1' }, -81 silly publish author: { name: 'Max Lynch', email: 'max@drifty.com' }, -81 silly publish license: 'MIT', -81 silly publish dependencies: { cordova: '>=3.1.0', optimist: '0.0.2', wrench: '1.5.1' }, -81 silly publish readme: 'ERROR: No README data found!', -81 silly publish _id: 'ionic@0.9.05', -81 silly publish dist: { shasum: '901fb6f02fcfb8f2944c61b575ba88f70395b720' }, -81 silly publish _from: '.' } -82 verbose url raw ionic -83 verbose url resolving [ 'https://registry.npmjs.org/', './ionic' ] -84 verbose url resolved https://registry.npmjs.org/ionic -85 info trying registry request attempt 1 at 10:29:58 -86 http PUT https://registry.npmjs.org/ionic -87 http 409 https://registry.npmjs.org/ionic -88 verbose url raw ionic -89 verbose url resolving [ 'https://registry.npmjs.org/', './ionic' ] -90 verbose url resolved https://registry.npmjs.org/ionic -91 info trying registry request attempt 1 at 10:29:58 -92 http GET https://registry.npmjs.org/ionic -93 http 200 https://registry.npmjs.org/ionic -94 verbose uploading [ 'ionic/-/ionic-0.9.05.tgz/-rev/3-eaa605a9ad6fae3bb8c301e3127943db', -94 verbose uploading '/Users/driftyadmin/.npm/ionic/0.9.05/package.tgz' ] -95 verbose url raw ionic/-/ionic-0.9.05.tgz/-rev/3-eaa605a9ad6fae3bb8c301e3127943db -96 verbose url resolving [ 'https://registry.npmjs.org/', -96 verbose url resolving './ionic/-/ionic-0.9.05.tgz/-rev/3-eaa605a9ad6fae3bb8c301e3127943db' ] -97 verbose url resolved https://registry.npmjs.org/ionic/-/ionic-0.9.05.tgz/-rev/3-eaa605a9ad6fae3bb8c301e3127943db -98 info trying registry request attempt 1 at 10:29:59 -99 http PUT https://registry.npmjs.org/ionic/-/ionic-0.9.05.tgz/-rev/3-eaa605a9ad6fae3bb8c301e3127943db -100 http 403 https://registry.npmjs.org/ionic/-/ionic-0.9.05.tgz/-rev/3-eaa605a9ad6fae3bb8c301e3127943db -101 info trying registry request attempt 1 at 10:29:59 -102 http PUT https://registry.npmjs.org/ionic/-/ionic-0.9.05.tgz/-rev/3-eaa605a9ad6fae3bb8c301e3127943db -103 http 403 https://registry.npmjs.org/ionic/-/ionic-0.9.05.tgz/-rev/3-eaa605a9ad6fae3bb8c301e3127943db -104 error publish Error uploading package -105 error Error: forbidden user: drifty not authorized to modify ionic -105 error Added: _attachments.ionic-0.9.05.tgz: ionic/-/ionic-0.9.05.tgz/-rev/3-eaa605a9ad6fae3bb8c301e3127943db -105 error at RegClient. (/usr/local/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:272:14) -105 error at Request.self.callback (/usr/local/lib/node_modules/npm/node_modules/request/index.js:148:22) -105 error at Request.EventEmitter.emit (events.js:98:17) -105 error at Request. (/usr/local/lib/node_modules/npm/node_modules/request/index.js:876:14) -105 error at Request.EventEmitter.emit (events.js:117:20) -105 error at IncomingMessage. (/usr/local/lib/node_modules/npm/node_modules/request/index.js:827:12) -105 error at IncomingMessage.EventEmitter.emit (events.js:117:20) -105 error at _stream_readable.js:910:16 -105 error at process._tickCallback (node.js:415:13) -106 error If you need help, you may report this log at: -106 error -106 error or email it to: -106 error -107 error System Darwin 12.5.0 -108 error command "node" "/usr/local/bin/npm" "publish" -109 error cwd /Users/driftyadmin/git/ionic/tools/ionic -110 error node -v v0.10.12 -111 error npm -v 1.2.32 -112 verbose exit [ 1, true ] diff --git a/package.json b/package.json index 23b00115a2..6790582e9b 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,22 @@ { - "name": "ionic", + "name": "ionic-framework", "version": "0.9.05", "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", "main": "ionic.js", + "keywords": [ + "ionic", + "ionic framework", + "mobile", + "html5", + "cordova", + "phonegap" + ], + "repository": { + "type": "git", + "url": "https://github.com/driftyco/ionic-cli.git" + }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, diff --git a/template/t b/template/t deleted file mode 100644 index e69de29bb2..0000000000 From fba2e19291e3132bf52426c4c3f00949ed6ace99 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Wed, 20 Nov 2013 10:48:43 -0600 Subject: [PATCH 0004/1100] Updated optimist --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6790582e9b..2b0ea80abb 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "license": "MIT", "dependencies": { "cordova": ">=3.1.0", - "optimist": "0.0.2", + "optimist": "0.6.0", "wrench": "1.5.1" } } From cd3f536dcf0ae7208e41edc98f6e875a96377e27 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Wed, 20 Nov 2013 11:21:57 -0600 Subject: [PATCH 0005/1100] Updated deps --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 2b0ea80abb..f15132d587 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "license": "MIT", "dependencies": { "cordova": ">=3.1.0", + "ncp": "0.4.2", "optimist": "0.6.0", "wrench": "1.5.1" } From ad0748bfb7e0c9c95c8bde1dc22ad9a680e16b0d Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Wed, 20 Nov 2013 11:28:06 -0600 Subject: [PATCH 0006/1100] Updated git ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1ee84da988..be106cacde 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.sw* +node_modules/ From 21ff99d7b24611d9caa19926b58919d90c633f2d Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Wed, 20 Nov 2013 11:35:31 -0600 Subject: [PATCH 0007/1100] Created ionic binary: --- bin/ionic | 10 ++++++++++ ionic.js => lib/ionic.js | 8 +++----- package.json | 4 +++- 3 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 bin/ionic rename ionic.js => lib/ionic.js (91%) diff --git a/bin/ionic b/bin/ionic new file mode 100644 index 0000000000..0454b93e1f --- /dev/null +++ b/bin/ionic @@ -0,0 +1,10 @@ +#!/usr/bin/env node + +'use strict'; + +process.title = 'ionic'; + +var Ionic = require('../lib/ionic').Ionic; + +var ionic = new Ionic(); +ionic.run(); diff --git a/ionic.js b/lib/ionic.js similarity index 91% rename from ionic.js rename to lib/ionic.js index 5585912d22..8dc4cbcb5c 100644 --- a/ionic.js +++ b/lib/ionic.js @@ -6,12 +6,12 @@ http://ionicframework.com/ A utility for starting and administering Ionic based mobile app projects. -Licensed under the MITlicense. See LICENSE For more. +Licensed under the MIT license. See LICENSE For more. Copyright 2013 Drifty (http://drifty.com/) */ -var IonicStartTask = require('./lib/ionic/start.js').IonicStartTask; +var IonicStartTask = require('./ionic/start.js').IonicStartTask; var argv = require('optimist').argv; @@ -85,6 +85,4 @@ Ionic.prototype = { }; - -var ionic = new Ionic(); -ionic.run(); +exports.Ionic = Ionic; diff --git a/package.json b/package.json index f15132d587..95cc6d189b 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,9 @@ "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", - "main": "ionic.js", + "bin": { + "ionic": "bin/ionic" + }, "keywords": [ "ionic", "ionic framework", From ce657333766b733bba87ba7da08a3fb9b37f1092 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Wed, 20 Nov 2013 15:05:16 -0600 Subject: [PATCH 0008/1100] Working start --- lib/ionic.js | 1 + lib/ionic/start.js | 73 +++++++++++++++++++++++++++++++++++++++++----- package.json | 5 +++- 3 files changed, 71 insertions(+), 8 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 8dc4cbcb5c..2316f539df 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -54,6 +54,7 @@ Ionic.prototype = { process.stderr.write(' ' + task.name + '\t\t' + task.task.HELP_LINE + '\n'); } + process.stderr.write('\n'); process.exit(1); }, diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 9e978bdf93..ec6e01d99a 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -1,10 +1,32 @@ var fs = require('fs'), + os = require('os'), + path = require('path'), + request = require('request'), ncp = require('ncp').ncp, path = require('path'), + shelljs = require('shelljs/global'), + unzip = require('unzip'), IonicTask = require('./task').IonicTask; var argv = require('optimist').argv; +fs.mkdirParent = function(dirPath, mode, callback) { + //Call the standard fs.mkdir + console.log('MKDIR'); + fs.mkdir(dirPath, mode, function(error) { + //When it fail in this way, do the custom steps + if (error && error.errno === 34) { + //Create all the parents recursively + fs.mkdirParent(path.dirname(dirPath), mode, callback); + //And then the directory + fs.mkdirParent(dirPath, mode, callback); + } + console.log('MKDIR DONE'); + //Manually run the callback since we used our own callback to do all these + callback && callback(error); + }); +}; + var IonicStartTask = function() { } @@ -18,21 +40,61 @@ IonicStartTask.prototype._printUsage = function() { IonicStartTask.prototype.run = function(ionic) { if(argv._.length < 2) { - ionic.fail('No app name specified'); + ionic.fail('No app name specified, exiting.'); } + // Grab the name of the app this.appName = argv._[1]; + + // Grab the target path from the command line, or use this as the app name + var targetPathName = argv._[2] || this.appName; + this.targetPath = path.resolve(this.appName); // Make sure to create this, or ask them if they want to override it if(this._checkTargetPath() === false) { - process.stderr.write('Not continuing.'); + process.stderr.write('Exiting.\n'); process.exit(1); } console.log('Creating Ionic app in folder', this.targetPath); - this._writeTemplateFolder(); + fs.mkdirSync(this.targetPath); + + //this._writeTemplateFolder(); + this._fetchAndWriteSeed(); + +}; + +IonicStartTask.prototype._fetchAndWriteSeed = function() { + var _this = this; + + var templateUrl = 'https://github.com/driftyco/ionic-angular-cordova-seed/archive/master.zip' ; + console.log('Downloading starter template from', templateUrl); + + var tmpFolder = os.tmpdir(); + var tempZipFilePath = tmpFolder + 'ionic-angular-cordova-seed' + new Date().getTime() + '.zip'; + console.log('Writing to', tempZipFilePath); + var tempZipFileStream = fs.createWriteStream(tempZipFilePath) + + var unzipRepo = function(fileName) { + var readStream = fs.createReadStream(fileName); + + var writeStream = unzip.Extract({ path: _this.targetPath }); + writeStream.on('close', function() { + console.log('END'); + //fs.renameSync(_this.targetPath + '/' + 'ionic-angular-cordova-seed-master', _this.targetPath + '/app'); + cp('-R', _this.targetPath + '/' + 'ionic-angular-cordova-seed-master/.', _this.targetPath); + rm('-rf', _this.targetPath + '/' + 'ionic-angular-cordova-seed-master/'); + }); + readStream.pipe(writeStream); + }; + + request({ url: templateUrl, encoding: null }, function(err, res, body) { + tempZipFileStream.write(body); + tempZipFileStream.close(); + unzipRepo(tempZipFilePath); + }); }; IonicStartTask.prototype._writeTemplateFolder = function() { @@ -46,10 +108,7 @@ IonicStartTask.prototype._writeTemplateFolder = function() { IonicStartTask.prototype._checkTargetPath = function() { if(fs.existsSync(this.targetPath)) { - var resp = this.ask('The ' + this.targetPath + ' directory already exists. Overwrite files? (y/n)') - if(resp === 'y') { - return true; - } + process.stderr.write('The directory ' + this.targetPath + ' already exists, please remove it if you would like to create a new ionic project there.\n'); return false; } return true; diff --git a/package.json b/package.json index 95cc6d189b..3c24a5d63d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic-framework", - "version": "0.9.05", + "version": "0.9.06", "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -26,6 +26,9 @@ "license": "MIT", "dependencies": { "cordova": ">=3.1.0", + "request": "2.27.0", + "shelljs": "0.2.6", + "unzip": "0.1.9", "ncp": "0.4.2", "optimist": "0.6.0", "wrench": "1.5.1" From e236311e76dee7c5634ce11ab5c2a42dc07bb06a Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Wed, 20 Nov 2013 15:06:58 -0600 Subject: [PATCH 0009/1100] Updated debugging message --- lib/ionic/start.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index ec6e01d99a..e057cd5124 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -74,7 +74,6 @@ IonicStartTask.prototype._fetchAndWriteSeed = function() { var tmpFolder = os.tmpdir(); var tempZipFilePath = tmpFolder + 'ionic-angular-cordova-seed' + new Date().getTime() + '.zip'; - console.log('Writing to', tempZipFilePath); var tempZipFileStream = fs.createWriteStream(tempZipFilePath) var unzipRepo = function(fileName) { @@ -82,7 +81,6 @@ IonicStartTask.prototype._fetchAndWriteSeed = function() { var writeStream = unzip.Extract({ path: _this.targetPath }); writeStream.on('close', function() { - console.log('END'); //fs.renameSync(_this.targetPath + '/' + 'ionic-angular-cordova-seed-master', _this.targetPath + '/app'); cp('-R', _this.targetPath + '/' + 'ionic-angular-cordova-seed-master/.', _this.targetPath); rm('-rf', _this.targetPath + '/' + 'ionic-angular-cordova-seed-master/'); From 6afddb94b0a831296da0049765d8ff4e8d707caa Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Wed, 20 Nov 2013 15:08:59 -0600 Subject: [PATCH 0010/1100] Note --- lib/ionic/start.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index e057cd5124..7170c1df3e 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -84,6 +84,7 @@ IonicStartTask.prototype._fetchAndWriteSeed = function() { //fs.renameSync(_this.targetPath + '/' + 'ionic-angular-cordova-seed-master', _this.targetPath + '/app'); cp('-R', _this.targetPath + '/' + 'ionic-angular-cordova-seed-master/.', _this.targetPath); rm('-rf', _this.targetPath + '/' + 'ionic-angular-cordova-seed-master/'); + console.log('Project created!'); }); readStream.pipe(writeStream); }; From 699dc374abbceb7bc9215e6109703ae648c5d252 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Wed, 20 Nov 2013 15:15:33 -0600 Subject: [PATCH 0011/1100] Better ascii --- lib/ionic.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 2316f539df..1c5870f7ce 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -1,7 +1,10 @@ /* - __ __ -| / \ |\ | | / ` -| \__/ | \| | \__, + _ _ +(_) (_) + _ ___ _ __ _ ___ +| |/ _ \| '_ \| |/ __| +| | (_) | | | | | (__ +|_|\___/|_| |_|_|\___| http://ionicframework.com/ From 236a7464de93eb38b8c371b25aca3b0076f25a6f Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Thu, 21 Nov 2013 11:40:39 -0600 Subject: [PATCH 0012/1100] Moved ionic-framework npm to ionic, thanks @jaredhanson --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3c24a5d63d..476e893bb9 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "ionic-framework", + "name": "ionic", "version": "0.9.06", "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", From 3cbe371e41bd8f9e34695c98a7f7221f54daa54b Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Thu, 21 Nov 2013 11:45:05 -0600 Subject: [PATCH 0013/1100] Updated package --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 476e893bb9..6c9d78c74a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "0.9.06", + "version": "0.9.08", "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 465dea10198e4927457f3c6d130dc770b1a84f03 Mon Sep 17 00:00:00 2001 From: Matt Goodall Date: Thu, 28 Nov 2013 16:42:13 +0000 Subject: [PATCH 0014/1100] Build seed zip path correctly when temp dir does not end with path separator. --- lib/ionic/start.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 7170c1df3e..42f16ebd70 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -73,7 +73,7 @@ IonicStartTask.prototype._fetchAndWriteSeed = function() { console.log('Downloading starter template from', templateUrl); var tmpFolder = os.tmpdir(); - var tempZipFilePath = tmpFolder + 'ionic-angular-cordova-seed' + new Date().getTime() + '.zip'; + var tempZipFilePath = path.join(tmpFolder, 'ionic-angular-cordova-seed' + new Date().getTime() + '.zip'); var tempZipFileStream = fs.createWriteStream(tempZipFilePath) var unzipRepo = function(fileName) { From e264593c81fe0238447fa08b82661cc78b70f135 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 9 Dec 2013 09:12:15 -0600 Subject: [PATCH 0015/1100] Updated version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6c9d78c74a..2a8b42c5ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "0.9.08", + "version": "0.9.09", "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From c7861ba72326a10529d424c949a372921c1dea90 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 9 Dec 2013 09:15:29 -0600 Subject: [PATCH 0016/1100] Removed unnecesary path --- lib/ionic/start.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 42f16ebd70..650efb1d7b 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -1,6 +1,5 @@ var fs = require('fs'), os = require('os'), - path = require('path'), request = require('request'), ncp = require('ncp').ncp, path = require('path'), @@ -12,7 +11,6 @@ var argv = require('optimist').argv; fs.mkdirParent = function(dirPath, mode, callback) { //Call the standard fs.mkdir - console.log('MKDIR'); fs.mkdir(dirPath, mode, function(error) { //When it fail in this way, do the custom steps if (error && error.errno === 34) { @@ -21,7 +19,6 @@ fs.mkdirParent = function(dirPath, mode, callback) { //And then the directory fs.mkdirParent(dirPath, mode, callback); } - console.log('MKDIR DONE'); //Manually run the callback since we used our own callback to do all these callback && callback(error); }); From f8516bce8b539de83ab976016957bde6915c13f2 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 9 Dec 2013 09:27:01 -0600 Subject: [PATCH 0017/1100] Removed unused package --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 2a8b42c5ad..41e12d4066 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,5 @@ "unzip": "0.1.9", "ncp": "0.4.2", "optimist": "0.6.0", - "wrench": "1.5.1" } } From 1dc96637239f0077324203a19b4de2998f75ccaf Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 9 Dec 2013 09:27:19 -0600 Subject: [PATCH 0018/1100] Updated v --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 41e12d4066..b3d59a251e 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,6 @@ "shelljs": "0.2.6", "unzip": "0.1.9", "ncp": "0.4.2", - "optimist": "0.6.0", + "optimist": "0.6.0" } } From d6e30540b832f1036666042e89b76cd0152c1edc Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Fri, 13 Dec 2013 10:42:35 -0600 Subject: [PATCH 0019/1100] Moved cordova version to 3.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b3d59a251e..74e910bd49 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "author": "Max Lynch ", "license": "MIT", "dependencies": { - "cordova": ">=3.1.0", + "cordova": "~3.2.0", "request": "2.27.0", "shelljs": "0.2.6", "unzip": "0.1.9", From a33fac7ed14889415168a23a76eb2077a94dbeff Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Thu, 19 Dec 2013 14:04:59 -0600 Subject: [PATCH 0020/1100] Add initial build task --- bin/ionic | 0 lib/ionic.js | 7 +++++++ lib/ionic/build.js | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+) mode change 100644 => 100755 bin/ionic create mode 100644 lib/ionic/build.js diff --git a/bin/ionic b/bin/ionic old mode 100644 new mode 100755 diff --git a/lib/ionic.js b/lib/ionic.js index 1c5870f7ce..bbac5d4dcb 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -15,6 +15,7 @@ Copyright 2013 Drifty (http://drifty.com/) */ var IonicStartTask = require('./ionic/start.js').IonicStartTask; +var IonicBuildTask = require('./ionic/build.js').IonicBuildTask; var argv = require('optimist').argv; @@ -24,6 +25,12 @@ var TASKS = [ name: 'start', usage: 'appname', task: IonicStartTask + }, + { + title: 'build', + name: 'build', + usage: 'platform', + task: IonicBuildTask } ]; diff --git a/lib/ionic/build.js b/lib/ionic/build.js new file mode 100644 index 0000000000..40e4d5a18a --- /dev/null +++ b/lib/ionic/build.js @@ -0,0 +1,32 @@ +var fs = require('fs'), + os = require('os'), + request = require('request'), + ncp = require('ncp').ncp, + path = require('path'), + shelljs = require('shelljs/global'), + unzip = require('unzip'), + IonicTask = require('./task').IonicTask; + +var argv = require('optimist').argv; + +var IonicBuildTask = function() { +} + +IonicBuildTask.HELP_LINE = 'Build an Ionic project for the given plaform.'; + +IonicBuildTask.prototype = new IonicTask(); + +IonicBuildTask.prototype._printUsage = function() { + process.stderr.write('ionic build plaform (eg. android, ios)\n'); +} + +IonicBuildTask.prototype.run = function(ionic) { + if(argv._.length < 2) { + ionic.fail('No plaform specified, exiting.'); + } + + // Grab the name of the app + this.platform = argv._[1]; +}; + +exports.IonicBuildTask = IonicBuildTask; From 95a080a803e08e8b425a527be9a4c819fe20dc34 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Fri, 20 Dec 2013 17:45:26 -0600 Subject: [PATCH 0021/1100] I don't understand the error --- lib/ionic.js | 11 +++++++++-- lib/ionic/build.js | 10 ++-------- lib/ionic/login.js | 29 +++++++++++++++++++++++++++++ lib/ionic/start.js | 3 +-- 4 files changed, 41 insertions(+), 12 deletions(-) create mode 100644 lib/ionic/login.js diff --git a/lib/ionic.js b/lib/ionic.js index bbac5d4dcb..492d2c3ad7 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -14,8 +14,9 @@ Licensed under the MIT license. See LICENSE For more. Copyright 2013 Drifty (http://drifty.com/) */ -var IonicStartTask = require('./ionic/start.js').IonicStartTask; -var IonicBuildTask = require('./ionic/build.js').IonicBuildTask; +var IonicStartTask = require('./ionic/start.js').IonicStartTask, + IonicLoginTask = require('./ionic/login.js').IonicLoginTask, + IonicBuildTask = require('./ionic/build.js').IonicBuildTask; var argv = require('optimist').argv; @@ -26,6 +27,12 @@ var TASKS = [ usage: 'appname', task: IonicStartTask }, + { + title: 'login', + name: 'login', + usage: '[email]', + task: IonicLoginTask + }, { title: 'build', name: 'build', diff --git a/lib/ionic/build.js b/lib/ionic/build.js index 40e4d5a18a..7f94545266 100644 --- a/lib/ionic/build.js +++ b/lib/ionic/build.js @@ -1,14 +1,8 @@ -var fs = require('fs'), - os = require('os'), - request = require('request'), - ncp = require('ncp').ncp, - path = require('path'), +var request = require('request'), shelljs = require('shelljs/global'), - unzip = require('unzip'), + argv = require('optimist').argv, IonicTask = require('./task').IonicTask; -var argv = require('optimist').argv; - var IonicBuildTask = function() { } diff --git a/lib/ionic/login.js b/lib/ionic/login.js new file mode 100644 index 0000000000..ed60ab9192 --- /dev/null +++ b/lib/ionic/login.js @@ -0,0 +1,29 @@ +var request = require('request'), + shelljs = require('shelljs/global'), + argv = require('optimist').argv, + IonicTask = require('./task').IonicTask; + +var IonicLoginTask = function() { +} + +IonicLoginTask.HELP_LINE = 'Login to Ionic Studio'; + +IonicLoginTask.prototype = new IonicTask(); + +IonicLoginTask.prototype._printUsage = function() { + process.stderr.write('ionic login [email]\n'); +} + +IonicLoginTask.prototype.run = function(ionic) { + if(argv._.length >= 2) { + // Grab the email for login + this.email = argv._[1]; + } else { + this.email = this.ask("Email?"); + } + + this.password = this.ask("Password?", true); + +}; + +exports.IonicLoginTask = IonicLoginTask; diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 650efb1d7b..73e8863db4 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -5,10 +5,9 @@ var fs = require('fs'), path = require('path'), shelljs = require('shelljs/global'), unzip = require('unzip'), + argv = require('optimist').argv, IonicTask = require('./task').IonicTask; -var argv = require('optimist').argv; - fs.mkdirParent = function(dirPath, mode, callback) { //Call the standard fs.mkdir fs.mkdir(dirPath, mode, function(error) { From 21e1a21f8c91253b07cb4281dcf4edb3f53def53 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Sat, 21 Dec 2013 00:47:56 -0600 Subject: [PATCH 0022/1100] Use prompt package --- lib/ionic/build.js | 4 ---- lib/ionic/login.js | 12 ++++-------- lib/ionic/start.js | 4 ---- lib/ionic/task.js | 11 +---------- package.json | 3 ++- 5 files changed, 7 insertions(+), 27 deletions(-) diff --git a/lib/ionic/build.js b/lib/ionic/build.js index 7f94545266..593e624102 100644 --- a/lib/ionic/build.js +++ b/lib/ionic/build.js @@ -10,10 +10,6 @@ IonicBuildTask.HELP_LINE = 'Build an Ionic project for the given plaform.'; IonicBuildTask.prototype = new IonicTask(); -IonicBuildTask.prototype._printUsage = function() { - process.stderr.write('ionic build plaform (eg. android, ios)\n'); -} - IonicBuildTask.prototype.run = function(ionic) { if(argv._.length < 2) { ionic.fail('No plaform specified, exiting.'); diff --git a/lib/ionic/login.js b/lib/ionic/login.js index ed60ab9192..42dbb89382 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -1,7 +1,7 @@ var request = require('request'), shelljs = require('shelljs/global'), - argv = require('optimist').argv, - IonicTask = require('./task').IonicTask; + IonicTask = require('./task').IonicTask, + argv = require('optimist').argv; var IonicLoginTask = function() { } @@ -10,19 +10,15 @@ IonicLoginTask.HELP_LINE = 'Login to Ionic Studio'; IonicLoginTask.prototype = new IonicTask(); -IonicLoginTask.prototype._printUsage = function() { - process.stderr.write('ionic login [email]\n'); -} - IonicLoginTask.prototype.run = function(ionic) { if(argv._.length >= 2) { // Grab the email for login this.email = argv._[1]; } else { - this.email = this.ask("Email?"); + // this.email = this.ask("Email?"); } - this.password = this.ask("Password?", true); + // this.password = this.ask("Password?", true); }; diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 73e8863db4..af342fee23 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -30,10 +30,6 @@ IonicStartTask.HELP_LINE = 'Start a new Ionic project with the given name.'; IonicStartTask.prototype = new IonicTask(); -IonicStartTask.prototype._printUsage = function() { - process.stderr.write('ionic start appname\n'); -} - IonicStartTask.prototype.run = function(ionic) { if(argv._.length < 2) { ionic.fail('No app name specified, exiting.'); diff --git a/lib/ionic/task.js b/lib/ionic/task.js index 20b74b4c1f..24bbcd9921 100644 --- a/lib/ionic/task.js +++ b/lib/ionic/task.js @@ -1,19 +1,10 @@ var fs = require('fs'); var IonicTask = function() { + HELP_LINE: '' }; IonicTask.prototype = { - // Prompt the user for a response - ask: function(question) { - var response; - - process.stdout.write(question + ' '); - process.stdin.resume(); - response = fs.readSync(process.stdin.fd, 100, 0, "utf8"); - process.stdin.pause(); - return response[0].trim(); - }, run: function(ionic) { } }; diff --git a/package.json b/package.json index 74e910bd49..8f994518ed 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "shelljs": "0.2.6", "unzip": "0.1.9", "ncp": "0.4.2", - "optimist": "0.6.0" + "optimist": "0.6.0", + "prompt": "0.2.12" } } From 1986415508ff22a3b0712b1a727e971901971ce5 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Sat, 21 Dec 2013 01:13:37 -0600 Subject: [PATCH 0023/1100] Initial use of prompt for login --- lib/ionic/login.js | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 42dbb89382..1fa97c9896 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -1,7 +1,7 @@ var request = require('request'), - shelljs = require('shelljs/global'), - IonicTask = require('./task').IonicTask, - argv = require('optimist').argv; + argv = require('optimist').argv, + prompt = require('prompt'), + IonicTask = require('./task').IonicTask; var IonicLoginTask = function() { } @@ -11,13 +11,32 @@ IonicLoginTask.HELP_LINE = 'Login to Ionic Studio'; IonicLoginTask.prototype = new IonicTask(); IonicLoginTask.prototype.run = function(ionic) { + var schema = [{ + name: 'password', + hidden: true, + required: true + }]; + if(argv._.length >= 2) { // Grab the email for login this.email = argv._[1]; } else { - // this.email = this.ask("Email?"); + schema.unshift({ + name: 'email', + pattern: /^\w+[@]\w+[\.]\w+$/, // Find a better email regex + message: 'Email for Ionic Studio login', + required: true + }); } + prompt.start(); + + prompt.get(schema, function (err, result) { + console.log('Command-line input received:'); + console.log(' email: ' + result.email); + console.log(' password: ' + result.password); + }); + // this.password = this.ask("Password?", true); }; From ac2b2aea5df0bc7debc8d585aa2f7624d9d8cfb5 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Sun, 22 Dec 2013 21:36:15 -0600 Subject: [PATCH 0024/1100] Make it cleaner! --- lib/ionic/login.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 1fa97c9896..827993b6cd 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -12,22 +12,23 @@ IonicLoginTask.prototype = new IonicTask(); IonicLoginTask.prototype.run = function(ionic) { var schema = [{ + name: 'email', + pattern: /^\w+[@]\w+[\.]\w+$/, // Find a better email regex + message: 'Email for Ionic Studio login', + required: true + }, { name: 'password', hidden: true, required: true }]; + // Grab the email for login if(argv._.length >= 2) { - // Grab the email for login this.email = argv._[1]; - } else { - schema.unshift({ - name: 'email', - pattern: /^\w+[@]\w+[\.]\w+$/, // Find a better email regex - message: 'Email for Ionic Studio login', - required: true - }); - } + schema.shift(); + } + + // prompt.override = optimist.argv; prompt.start(); @@ -37,7 +38,6 @@ IonicLoginTask.prototype.run = function(ionic) { console.log(' password: ' + result.password); }); - // this.password = this.ask("Password?", true); }; From a3c8433d7ee2501b83a5c89602cb154eb0790d64 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Sun, 22 Dec 2013 22:04:04 -0600 Subject: [PATCH 0025/1100] Awesome sauce. --- lib/ionic/login.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 827993b6cd..f00bcdf441 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -11,31 +11,37 @@ IonicLoginTask.HELP_LINE = 'Login to Ionic Studio'; IonicLoginTask.prototype = new IonicTask(); IonicLoginTask.prototype.run = function(ionic) { + var _this = this; var schema = [{ name: 'email', - pattern: /^\w+[@]\w+[\.]\w+$/, // Find a better email regex + pattern: /^[A-z0-9!#$%&'*+\/=?^_{|}~-]+(?:\.[A-z0-9!#$%&'*+\/=?^_{|}~-]+)*@(?:[A-z0-9](?:[A-z0-9-]*[A-z0-9])?\.)+[A-z0-9](?:[A-z0-9-]*[A-z0-9])?$/, message: 'Email for Ionic Studio login', required: true }, { - name: 'password', - hidden: true, - required: true + name: 'password', + hidden: true, + required: true }]; // Grab the email for login - if(argv._.length >= 2) { - this.email = argv._[1]; + if(argv._.length >= 2 && schema[0].pattern.test(argv._[1])) { + this.email = argv._[1].toLowerCase(); schema.shift(); } - // prompt.override = optimist.argv; + prompt.override = argv; prompt.start(); prompt.get(schema, function (err, result) { + if(!_this.email) { + _this.email = result.email.toLowerCase(); + } + _this.password = result.password; + console.log('Command-line input received:'); - console.log(' email: ' + result.email); - console.log(' password: ' + result.password); + console.log(' email: ' + _this.email); + console.log(' password: ' + _this.password); }); From d8757d9c272d149e835a8f56b96b434ea3313d26 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Mon, 30 Dec 2013 15:09:28 -0600 Subject: [PATCH 0026/1100] Initial login request and cookie storage --- lib/ionic/login.js | 8 ++++++++ lib/ionic/task.js | 5 ++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/ionic/login.js b/lib/ionic/login.js index f00bcdf441..9c8b187281 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -42,6 +42,14 @@ IonicLoginTask.prototype.run = function(ionic) { console.log('Command-line input received:'); console.log(' email: ' + _this.email); console.log(' password: ' + _this.password); + + var jar = request.jar(); + console.log(IonicTask.IONIC_DASH); + // request({url: _this.IONIC_DASH+'login/', jar: jar}, function (error, response, body) { + // console.log(response); + // }); + + }); diff --git a/lib/ionic/task.js b/lib/ionic/task.js index 24bbcd9921..bdd9dde8d1 100644 --- a/lib/ionic/task.js +++ b/lib/ionic/task.js @@ -1,10 +1,9 @@ var fs = require('fs'); -var IonicTask = function() { - HELP_LINE: '' -}; +var IonicTask = function() { }; IonicTask.prototype = { + IONIC_DASH: 'http://virt/', run: function(ionic) { } }; From 7c3e7e7f2c6fe1b223df269899a64266c39861a5 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Mon, 30 Dec 2013 16:14:31 -0600 Subject: [PATCH 0027/1100] Logging in with the dash! --- .gitignore | 1 + lib/ionic/login.js | 48 ++++++++++++++++++++++++++++++++++++++-------- lib/ionic/task.js | 1 + 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index be106cacde..ccc574f1f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.sw* +*.cookies node_modules/ diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 9c8b187281..21afc74584 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -1,4 +1,5 @@ -var request = require('request'), +var fs = require('fs'), + request = require('request'), argv = require('optimist').argv, prompt = require('prompt'), IonicTask = require('./task').IonicTask; @@ -34,20 +35,51 @@ IonicLoginTask.prototype.run = function(ionic) { prompt.start(); prompt.get(schema, function (err, result) { + if(err) { + console.log(err); + return; + } + if(!_this.email) { _this.email = result.email.toLowerCase(); } _this.password = result.password; - console.log('Command-line input received:'); - console.log(' email: ' + _this.email); - console.log(' password: ' + _this.password); + // console.log('Command-line input received:'); + // console.log(' email: ' + _this.email); + // console.log(' password: ' + _this.password); var jar = request.jar(); - console.log(IonicTask.IONIC_DASH); - // request({url: _this.IONIC_DASH+'login/', jar: jar}, function (error, response, body) { - // console.log(response); - // }); + console.log(_this.IONIC_DASH); + request({url: _this.IONIC_DASH+'login', jar: jar}, function (err, response, body) { + if(err || jar.cookies.length == 0) { + console.log(err); + return; + } + + request({method: 'POST', url: _this.IONIC_DASH+'login', jar: jar, form: {username: _this.email, password: _this.password, csrfmiddlewaretoken: jar.cookies[0].value}}, function (err, response, body) { + if(err) { + console.log(err); + return; + } + // Should be a 304 redirect status code if correct + if(response.statusCode == 200) { + console.log('Email or Password incorrect. Please visit '+_this.IONIC_DASH+' for help. :)') + return; + } + + fs.writeFile(_this.IONIC_COOKIES, JSON.stringify(jar, null, 2), function(err) { + if(err) { + console.log(err); + } else { + console.log('Logged in! :)'); + } + }); + // console.log(response); + + }); + + }); }); diff --git a/lib/ionic/task.js b/lib/ionic/task.js index bdd9dde8d1..804d5bf0e9 100644 --- a/lib/ionic/task.js +++ b/lib/ionic/task.js @@ -4,6 +4,7 @@ var IonicTask = function() { }; IonicTask.prototype = { IONIC_DASH: 'http://virt/', + IONIC_COOKIES: 'ionic.cookies', run: function(ionic) { } }; From dc4ff1d61a3431c900e9b7da9c1671f0e2af650c Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Mon, 30 Dec 2013 21:08:22 -0600 Subject: [PATCH 0028/1100] Login cleanup --- lib/ionic/login.js | 23 ++++++++++++++++------- lib/ionic/task.js | 2 -- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 21afc74584..de76f6dd51 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -45,19 +45,29 @@ IonicLoginTask.prototype.run = function(ionic) { } _this.password = result.password; - // console.log('Command-line input received:'); - // console.log(' email: ' + _this.email); - // console.log(' password: ' + _this.password); - var jar = request.jar(); console.log(_this.IONIC_DASH); - request({url: _this.IONIC_DASH+'login', jar: jar}, function (err, response, body) { + request({ + url: _this.IONIC_DASH+'login', + jar: jar + }, + function (err, response, body) { if(err || jar.cookies.length == 0) { console.log(err); return; } - request({method: 'POST', url: _this.IONIC_DASH+'login', jar: jar, form: {username: _this.email, password: _this.password, csrfmiddlewaretoken: jar.cookies[0].value}}, function (err, response, body) { + request({ + method: 'POST', + url: _this.IONIC_DASH+'login', + jar: jar, + form: { + username: _this.email, + password: _this.password, + csrfmiddlewaretoken: jar.cookies[0].value + } + }, + function (err, response, body) { if(err) { console.log(err); return; @@ -75,7 +85,6 @@ IonicLoginTask.prototype.run = function(ionic) { console.log('Logged in! :)'); } }); - // console.log(response); }); diff --git a/lib/ionic/task.js b/lib/ionic/task.js index 804d5bf0e9..2335cefd14 100644 --- a/lib/ionic/task.js +++ b/lib/ionic/task.js @@ -1,5 +1,3 @@ -var fs = require('fs'); - var IonicTask = function() { }; IonicTask.prototype = { From 2d7dfde45131775dff8d4cbd0ff5726d7928eb30 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Mon, 30 Dec 2013 21:18:57 -0600 Subject: [PATCH 0029/1100] Indentation fix --- lib/ionic/login.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/ionic/login.js b/lib/ionic/login.js index de76f6dd51..b7c3153150 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -14,14 +14,14 @@ IonicLoginTask.prototype = new IonicTask(); IonicLoginTask.prototype.run = function(ionic) { var _this = this; var schema = [{ - name: 'email', - pattern: /^[A-z0-9!#$%&'*+\/=?^_{|}~-]+(?:\.[A-z0-9!#$%&'*+\/=?^_{|}~-]+)*@(?:[A-z0-9](?:[A-z0-9-]*[A-z0-9])?\.)+[A-z0-9](?:[A-z0-9-]*[A-z0-9])?$/, - message: 'Email for Ionic Studio login', - required: true - }, { - name: 'password', - hidden: true, - required: true + name: 'email', + pattern: /^[A-z0-9!#$%&'*+\/=?^_{|}~-]+(?:\.[A-z0-9!#$%&'*+\/=?^_{|}~-]+)*@(?:[A-z0-9](?:[A-z0-9-]*[A-z0-9])?\.)+[A-z0-9](?:[A-z0-9-]*[A-z0-9])?$/, + message: 'Email for Ionic Studio login', + required: true + }, { + name: 'password', + hidden: true, + required: true }]; // Grab the email for login @@ -40,11 +40,11 @@ IonicLoginTask.prototype.run = function(ionic) { return; } - if(!_this.email) { - _this.email = result.email.toLowerCase(); - } - _this.password = result.password; - + if(!_this.email) { + _this.email = result.email.toLowerCase(); + } + _this.password = result.password; + var jar = request.jar(); console.log(_this.IONIC_DASH); request({ From 6611930cb4654c53a7c3c506306b01bd2d5f5c54 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Tue, 31 Dec 2013 00:36:34 -0600 Subject: [PATCH 0030/1100] Build task using login! --- lib/ionic.js | 3 +++ lib/ionic/build.js | 9 ++++++- lib/ionic/login.js | 65 +++++++++++++++++++++++++++++----------------- lib/ionic/start.js | 10 +++---- lib/ionic/task.js | 2 -- 5 files changed, 57 insertions(+), 32 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 492d2c3ad7..6ac0cd8e66 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -44,6 +44,9 @@ var TASKS = [ Ionic = function() {}; Ionic.prototype = { + IONIC_DASH: 'http://virt/', + IONIC_COOKIES: 'ionic.cookies', + _tryBuildingTask: function() { if(argv._.length == 0) { return false; diff --git a/lib/ionic/build.js b/lib/ionic/build.js index 593e624102..cb1915ff19 100644 --- a/lib/ionic/build.js +++ b/lib/ionic/build.js @@ -1,7 +1,8 @@ var request = require('request'), shelljs = require('shelljs/global'), argv = require('optimist').argv, - IonicTask = require('./task').IonicTask; + IonicTask = require('./task').IonicTask, + IonicLoginTask = require('./login.js').IonicLoginTask; var IonicBuildTask = function() { } @@ -17,6 +18,12 @@ IonicBuildTask.prototype.run = function(ionic) { // Grab the name of the app this.platform = argv._[1]; + + var login = new IonicLoginTask(); + login.get(ionic, function(jar) { + console.log(jar); + }); + }; exports.IonicBuildTask = IonicBuildTask; diff --git a/lib/ionic/login.js b/lib/ionic/login.js index b7c3153150..a8198f8891 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -11,8 +11,8 @@ IonicLoginTask.HELP_LINE = 'Login to Ionic Studio'; IonicLoginTask.prototype = new IonicTask(); -IonicLoginTask.prototype.run = function(ionic) { - var _this = this; +IonicLoginTask.prototype.run = function(ionic, callback) { + var self = this; var schema = [{ name: 'email', pattern: /^[A-z0-9!#$%&'*+\/=?^_{|}~-]+(?:\.[A-z0-9!#$%&'*+\/=?^_{|}~-]+)*@(?:[A-z0-9](?:[A-z0-9-]*[A-z0-9])?\.)+[A-z0-9](?:[A-z0-9-]*[A-z0-9])?$/, @@ -40,18 +40,18 @@ IonicLoginTask.prototype.run = function(ionic) { return; } - if(!_this.email) { - _this.email = result.email.toLowerCase(); + if(!self.email) { + self.email = result.email.toLowerCase(); } - _this.password = result.password; + self.password = result.password; var jar = request.jar(); - console.log(_this.IONIC_DASH); + console.log(ionic.IONIC_DASH); request({ - url: _this.IONIC_DASH+'login', + url: ionic.IONIC_DASH+'login', jar: jar }, - function (err, response, body) { + function(err, response, body) { if(err || jar.cookies.length == 0) { console.log(err); return; @@ -59,11 +59,11 @@ IonicLoginTask.prototype.run = function(ionic) { request({ method: 'POST', - url: _this.IONIC_DASH+'login', + url: ionic.IONIC_DASH+'login', jar: jar, form: { - username: _this.email, - password: _this.password, + username: self.email, + password: self.password, csrfmiddlewaretoken: jar.cookies[0].value } }, @@ -74,26 +74,43 @@ IonicLoginTask.prototype.run = function(ionic) { } // Should be a 304 redirect status code if correct if(response.statusCode == 200) { - console.log('Email or Password incorrect. Please visit '+_this.IONIC_DASH+' for help. :)') + console.log('Email or Password incorrect. Please visit '+ionic.IONIC_DASH+' for help. :)') return; } - fs.writeFile(_this.IONIC_COOKIES, JSON.stringify(jar, null, 2), function(err) { - if(err) { - console.log(err); - } else { - console.log('Logged in! :)'); - } - }); + var err = fs.writeFileSync(ionic.IONIC_COOKIES, JSON.stringify(jar, null, 2)); + if(err) { + console.log(err); + return; + } - }); + console.log('Logged in! :)'); + if(callback) { + callback(jar); + } + }); }); - - }); - - }; +IonicLoginTask.prototype.get = function(ionic, callback) { + var self = this; + + if(fs.existsSync(ionic.IONIC_COOKIES)) { + var jar = JSON.parse(fs.readFileSync(ionic.IONIC_COOKIES)); + if(jar.cookies && jar.cookies.length > 0) { + for(i in jar.cookies) { + var cookie = jar.cookies[i]; + if(cookie.name == "sessionid" && new Date(cookie.expires) > new Date()) { + callback(jar); + return; + } + } + } + } + + this.run(ionic, callback); +} + exports.IonicLoginTask = IonicLoginTask; diff --git a/lib/ionic/start.js b/lib/ionic/start.js index af342fee23..5d05f13e84 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -59,7 +59,7 @@ IonicStartTask.prototype.run = function(ionic) { }; IonicStartTask.prototype._fetchAndWriteSeed = function() { - var _this = this; + var self = this; var templateUrl = 'https://github.com/driftyco/ionic-angular-cordova-seed/archive/master.zip' ; console.log('Downloading starter template from', templateUrl); @@ -71,11 +71,11 @@ IonicStartTask.prototype._fetchAndWriteSeed = function() { var unzipRepo = function(fileName) { var readStream = fs.createReadStream(fileName); - var writeStream = unzip.Extract({ path: _this.targetPath }); + var writeStream = unzip.Extract({ path: self.targetPath }); writeStream.on('close', function() { - //fs.renameSync(_this.targetPath + '/' + 'ionic-angular-cordova-seed-master', _this.targetPath + '/app'); - cp('-R', _this.targetPath + '/' + 'ionic-angular-cordova-seed-master/.', _this.targetPath); - rm('-rf', _this.targetPath + '/' + 'ionic-angular-cordova-seed-master/'); + //fs.renameSync(self.targetPath + '/' + 'ionic-angular-cordova-seed-master', self.targetPath + '/app'); + cp('-R', self.targetPath + '/' + 'ionic-angular-cordova-seed-master/.', self.targetPath); + rm('-rf', self.targetPath + '/' + 'ionic-angular-cordova-seed-master/'); console.log('Project created!'); }); readStream.pipe(writeStream); diff --git a/lib/ionic/task.js b/lib/ionic/task.js index 2335cefd14..8ac8da9425 100644 --- a/lib/ionic/task.js +++ b/lib/ionic/task.js @@ -1,8 +1,6 @@ var IonicTask = function() { }; IonicTask.prototype = { - IONIC_DASH: 'http://virt/', - IONIC_COOKIES: 'ionic.cookies', run: function(ionic) { } }; From f21ccc6bb83935884ade9229d361a8b0c2894b6e Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Thu, 2 Jan 2014 20:48:45 -0600 Subject: [PATCH 0031/1100] Create a custom config file for ionic --- lib/ionic.js | 3 +++ lib/ionic/build.js | 10 ++++++++++ lib/ionic/login.js | 16 +++++----------- lib/ionic/start.js | 16 ++++++++-------- template/index.html | 0 5 files changed, 26 insertions(+), 19 deletions(-) delete mode 100644 template/index.html diff --git a/lib/ionic.js b/lib/ionic.js index 6ac0cd8e66..2462140629 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -46,6 +46,9 @@ Ionic = function() {}; Ionic.prototype = { IONIC_DASH: 'http://virt/', IONIC_COOKIES: 'ionic.cookies', + IONIC_CONFIG: 'ionic.config', + IONIC_API_VERSION: 1, + IONIC_API: 'api/v'+this.IONIC_API_VERSION+'/', _tryBuildingTask: function() { if(argv._.length == 0) { diff --git a/lib/ionic/build.js b/lib/ionic/build.js index cb1915ff19..db350a7121 100644 --- a/lib/ionic/build.js +++ b/lib/ionic/build.js @@ -21,6 +21,16 @@ IonicBuildTask.prototype.run = function(ionic) { var login = new IonicLoginTask(); login.get(ionic, function(jar) { + request({ + method: 'POST', + url: ionic.IONIC_DASH+ionic.IONIC_API+'export/', + jar: jar + }, + function(err, response, body) { + if(err) { + ionic.fail(err); + } + }); console.log(jar); }); diff --git a/lib/ionic/login.js b/lib/ionic/login.js index a8198f8891..e1b3b31bc5 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -36,8 +36,7 @@ IonicLoginTask.prototype.run = function(ionic, callback) { prompt.get(schema, function (err, result) { if(err) { - console.log(err); - return; + ionic.fail(err); } if(!self.email) { @@ -46,15 +45,13 @@ IonicLoginTask.prototype.run = function(ionic, callback) { self.password = result.password; var jar = request.jar(); - console.log(ionic.IONIC_DASH); request({ url: ionic.IONIC_DASH+'login', jar: jar }, function(err, response, body) { if(err || jar.cookies.length == 0) { - console.log(err); - return; + ionic.fail(err); } request({ @@ -69,19 +66,16 @@ IonicLoginTask.prototype.run = function(ionic, callback) { }, function (err, response, body) { if(err) { - console.log(err); - return; + ionic.fail(err); } // Should be a 304 redirect status code if correct if(response.statusCode == 200) { - console.log('Email or Password incorrect. Please visit '+ionic.IONIC_DASH+' for help. :)') - return; + ionic.fail('Email or Password incorrect. Please visit '+ionic.IONIC_DASH+' for help. :)') } var err = fs.writeFileSync(ionic.IONIC_COOKIES, JSON.stringify(jar, null, 2)); if(err) { - console.log(err); - return; + ionic.fail(err); } console.log('Logged in! :)'); diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 5d05f13e84..e017052ac8 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -53,8 +53,8 @@ IonicStartTask.prototype.run = function(ionic) { fs.mkdirSync(this.targetPath); - //this._writeTemplateFolder(); this._fetchAndWriteSeed(); + this._writeConfig(ionic); }; @@ -88,13 +88,13 @@ IonicStartTask.prototype._fetchAndWriteSeed = function() { }); }; -IonicStartTask.prototype._writeTemplateFolder = function() { - console.log('Copying template to', this.targetPath); - ncp('template', this.appName, function(err) { - if(err) { - this._fail('Unable to build starter folder', err); - } - }); +IonicStartTask.prototype._writeConfig = function(ionic) { + console.log('Writing ionic.config', this.targetPath); + var err = fs.writeFileSync(this.targetPath + '/' + ionic.IONIC_CONFIG, JSON.stringify({name: this.appName}, null, 2)); + if(err) { + process.stderr.write('Error writing ' + this.targetPath + '/' + ionic.IONIC_CONFIG + ': ' + err + '\n'); + return; + } }; IonicStartTask.prototype._checkTargetPath = function() { diff --git a/template/index.html b/template/index.html deleted file mode 100644 index e69de29bb2..0000000000 From 481ed35e92052605d534e55346d2c1ae08811a80 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Fri, 3 Jan 2014 15:32:29 -0600 Subject: [PATCH 0032/1100] Load ionic.config for build --- lib/ionic/build.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/ionic/build.js b/lib/ionic/build.js index db350a7121..27bb1bdb89 100644 --- a/lib/ionic/build.js +++ b/lib/ionic/build.js @@ -1,4 +1,5 @@ -var request = require('request'), +var fs = require('fs'), + request = require('request'), shelljs = require('shelljs/global'), argv = require('optimist').argv, IonicTask = require('./task').IonicTask, @@ -16,9 +17,15 @@ IonicBuildTask.prototype.run = function(ionic) { ionic.fail('No plaform specified, exiting.'); } - // Grab the name of the app + // Grab the platform build for the app this.platform = argv._[1]; + if(!fs.existsSync(ionic.IONIC_CONFIG)) { + ionic.fail('Could not find ' + ionic.IONIC_CONFIG + '!'+ + ' Please run this command your root ionic project directory with that file.'); + } + var project = JSON.parse(fs.readFileSync(ionic.IONIC_CONFIG)); + var login = new IonicLoginTask(); login.get(ionic, function(jar) { request({ From 7645df6a3bedd7cfbc0c531d83f20d0cb1576322 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Tue, 7 Jan 2014 15:02:07 -0600 Subject: [PATCH 0033/1100] Initial upload code --- lib/ionic.js | 29 ++++++++++++++++++++++++++--- lib/ionic/build.js | 18 ++++++------------ lib/ionic/login.js | 8 ++++---- lib/ionic/upload.js | 36 ++++++++++++++++++++++++++++++++++++ package.json | 11 ++++++----- 5 files changed, 78 insertions(+), 24 deletions(-) create mode 100644 lib/ionic/upload.js diff --git a/lib/ionic.js b/lib/ionic.js index 2462140629..13b35f118e 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -16,9 +16,11 @@ Copyright 2013 Drifty (http://drifty.com/) var IonicStartTask = require('./ionic/start.js').IonicStartTask, IonicLoginTask = require('./ionic/login.js').IonicLoginTask, + IonicUploadTask = require('./ionic/upload.js').IonicUploadTask, IonicBuildTask = require('./ionic/build.js').IonicBuildTask; -var argv = require('optimist').argv; +var fs = require('fs'), + argv = require('optimist').argv; var TASKS = [ { @@ -33,6 +35,12 @@ var TASKS = [ usage: '[email]', task: IonicLoginTask }, + { + title: 'upload', + name: 'upload', + usage: '', + task: IonicUploadTask + }, { title: 'build', name: 'build', @@ -47,8 +55,7 @@ Ionic.prototype = { IONIC_DASH: 'http://virt/', IONIC_COOKIES: 'ionic.cookies', IONIC_CONFIG: 'ionic.config', - IONIC_API_VERSION: 1, - IONIC_API: 'api/v'+this.IONIC_API_VERSION+'/', + IONIC_API: 'api/v1/', _tryBuildingTask: function() { if(argv._.length == 0) { @@ -107,6 +114,22 @@ Ionic.prototype = { process.exit(1); }, + loadConfig: function() { + if(!fs.existsSync(this.IONIC_CONFIG)) { + this.fail('Could not find ' + this.IONIC_CONFIG + '!'+ + ' Please run this command your root ionic project directory with that file.'); + } + return JSON.parse(fs.readFileSync(this.IONIC_CONFIG)); + }, + + saveConfig: function(project) { + var err = fs.writeFileSync(this.IONIC_CONFIG, JSON.stringify(project, null, 2)); + if(err) { + process.stderr.write('Error writing ' + ionic.IONIC_CONFIG + ': ' + err + '\n'); + } + } + + }; exports.Ionic = Ionic; diff --git a/lib/ionic/build.js b/lib/ionic/build.js index 27bb1bdb89..811d7770fc 100644 --- a/lib/ionic/build.js +++ b/lib/ionic/build.js @@ -1,6 +1,4 @@ -var fs = require('fs'), - request = require('request'), - shelljs = require('shelljs/global'), +var request = require('request'), argv = require('optimist').argv, IonicTask = require('./task').IonicTask, IonicLoginTask = require('./login.js').IonicLoginTask; @@ -20,27 +18,23 @@ IonicBuildTask.prototype.run = function(ionic) { // Grab the platform build for the app this.platform = argv._[1]; - if(!fs.existsSync(ionic.IONIC_CONFIG)) { - ionic.fail('Could not find ' + ionic.IONIC_CONFIG + '!'+ - ' Please run this command your root ionic project directory with that file.'); - } - var project = JSON.parse(fs.readFileSync(ionic.IONIC_CONFIG)); + var project = ionic.loadConfig(); var login = new IonicLoginTask(); login.get(ionic, function(jar) { request({ method: 'POST', - url: ionic.IONIC_DASH+ionic.IONIC_API+'export/', + url: ionic.IONIC_DASH+'export/', jar: jar }, function(err, response, body) { if(err) { - ionic.fail(err); + ionic.fail("Error building: " + err); } }); console.log(jar); - }); - + console.log(project.name); + }); }; exports.IonicBuildTask = IonicBuildTask; diff --git a/lib/ionic/login.js b/lib/ionic/login.js index e1b3b31bc5..c749e61ebe 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -36,7 +36,7 @@ IonicLoginTask.prototype.run = function(ionic, callback) { prompt.get(schema, function (err, result) { if(err) { - ionic.fail(err); + ionic.fail('Error logging in: ' + err); } if(!self.email) { @@ -51,7 +51,7 @@ IonicLoginTask.prototype.run = function(ionic, callback) { }, function(err, response, body) { if(err || jar.cookies.length == 0) { - ionic.fail(err); + ionic.fail('Error logging in: ' + err); } request({ @@ -66,7 +66,7 @@ IonicLoginTask.prototype.run = function(ionic, callback) { }, function (err, response, body) { if(err) { - ionic.fail(err); + ionic.fail('Error logging in: ' + err); } // Should be a 304 redirect status code if correct if(response.statusCode == 200) { @@ -75,7 +75,7 @@ IonicLoginTask.prototype.run = function(ionic, callback) { var err = fs.writeFileSync(ionic.IONIC_COOKIES, JSON.stringify(jar, null, 2)); if(err) { - ionic.fail(err); + ionic.fail('Error writing ' + ionic.IONIC_COOKIES + ': ' + err); } console.log('Logged in! :)'); diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js new file mode 100644 index 0000000000..971db5b56c --- /dev/null +++ b/lib/ionic/upload.js @@ -0,0 +1,36 @@ +var request = require('request'), + argv = require('optimist').argv, + zip = require('adm-zip'); + IonicTask = require('./task').IonicTask, + IonicLoginTask = require('./login.js').IonicLoginTask; + +var IonicUploadTask = function() { +} + +IonicUploadTask.HELP_LINE = 'Upload an Ionic project.'; + +IonicUploadTask.prototype = new IonicTask(); + +IonicUploadTask.prototype.run = function(ionic) { + var project = ionic.loadConfig(); + + var login = new IonicLoginTask(); + login.get(ionic, function(jar) { + request({ + method: 'POST', + url: ionic.IONIC_DASH+'projects/'+(project.app_id?project.app_id:''), + jar: jar + }, + function(err, response, body) { + if(err) { + ionic.fail("Error uploading: " + err); + } + + + console.log(response); + }); + }); + +}; + +exports.IonicUploadTask = IonicUploadTask; diff --git a/package.json b/package.json index 8f994518ed..7b4a2f57f1 100644 --- a/package.json +++ b/package.json @@ -25,12 +25,13 @@ "author": "Max Lynch ", "license": "MIT", "dependencies": { - "cordova": "~3.2.0", - "request": "2.27.0", - "shelljs": "0.2.6", - "unzip": "0.1.9", + "adm-zip": "0.4.3", + "cordova": "~3.2.0", "ncp": "0.4.2", "optimist": "0.6.0", - "prompt": "0.2.12" + "prompt": "0.2.12", + "request": "2.27.0", + "shelljs": "0.2.6", + "unzip": "0.1.9" } } From 1ac019c4b8ae57e57b437c72b7559669550c98e4 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Tue, 7 Jan 2014 18:28:14 -0600 Subject: [PATCH 0034/1100] Broken code to upload zip --- lib/ionic/start.js | 1 - lib/ionic/upload.js | 38 ++++++++++++++++++++++++++++++++------ package.json | 2 +- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index e017052ac8..a9891e92d2 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -3,7 +3,6 @@ var fs = require('fs'), request = require('request'), ncp = require('ncp').ncp, path = require('path'), - shelljs = require('shelljs/global'), unzip = require('unzip'), argv = require('optimist').argv, IonicTask = require('./task').IonicTask; diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index 971db5b56c..1dd03d0684 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -1,6 +1,9 @@ -var request = require('request'), +var fs = require('fs'), + path = require('path'), + request = require('request'), argv = require('optimist').argv, - zip = require('adm-zip'); + FormData = require('form-data'); + Zip = require('adm-zip'); IonicTask = require('./task').IonicTask, IonicLoginTask = require('./login.js').IonicLoginTask; @@ -16,19 +19,42 @@ IonicUploadTask.prototype.run = function(ionic) { var login = new IonicLoginTask(); login.get(ionic, function(jar) { - request({ - method: 'POST', + var zip = new Zip(); + zip.addLocalFolder('www'); + zip.writeZip('www.zip'); + + // var form = new FormData(); + + // form.getLength(function(err,length){ + // console.log(length); + var r = request.post({ + // method: 'POST', url: ionic.IONIC_DASH+'projects/'+(project.app_id?project.app_id:''), - jar: jar + jar: jar, + // headers: { 'content-length': length } + // form: { + // csrfmiddlewaretoken: jar.cookies[0].value, + // app_file: fs.createReadStream('www.zip') + // } }, function(err, response, body) { + // fs.unlinkSync('www.zip'); + if(err) { ionic.fail("Error uploading: " + err); } - console.log(response); }); + + var form = r.form(); + form.append('csrfmiddlewaretoken', jar.cookies[0].value); + form.append('app_file', fs.createReadStream(path.resolve('www.zip')), {filename: 'www.zip'}); + // form.append('app_file', zip.toBuffer(), {filename: 'www.zip'}); + // r._form = form; + // }); + + console.log('poop'); }); }; diff --git a/package.json b/package.json index 7b4a2f57f1..8f9319776d 100644 --- a/package.json +++ b/package.json @@ -27,11 +27,11 @@ "dependencies": { "adm-zip": "0.4.3", "cordova": "~3.2.0", + "form-data": "~0.1.0", "ncp": "0.4.2", "optimist": "0.6.0", "prompt": "0.2.12", "request": "2.27.0", - "shelljs": "0.2.6", "unzip": "0.1.9" } } From 1fbd0468dce7ab943bae844ead0ed018b7c693ac Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Mon, 13 Jan 2014 11:35:23 -0600 Subject: [PATCH 0035/1100] Upload zips! --- lib/ionic/upload.js | 52 ++++++++++++++++++++------------------------- package.json | 2 +- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index 1dd03d0684..e532ce7c77 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -1,9 +1,10 @@ var fs = require('fs'), path = require('path'), + parseUrl = require('url').parse, request = require('request'), argv = require('optimist').argv, - FormData = require('form-data'); - Zip = require('adm-zip'); + FormData = require('form-data'), + Zip = require('adm-zip'), IonicTask = require('./task').IonicTask, IonicLoginTask = require('./login.js').IonicLoginTask; @@ -23,40 +24,33 @@ IonicUploadTask.prototype.run = function(ionic) { zip.addLocalFolder('www'); zip.writeZip('www.zip'); - // var form = new FormData(); - - // form.getLength(function(err,length){ - // console.log(length); - var r = request.post({ - // method: 'POST', - url: ionic.IONIC_DASH+'projects/'+(project.app_id?project.app_id:''), - jar: jar, - // headers: { 'content-length': length } - // form: { - // csrfmiddlewaretoken: jar.cookies[0].value, - // app_file: fs.createReadStream('www.zip') - // } - }, - function(err, response, body) { + var form = new FormData(); + form.append('csrfmiddlewaretoken', jar.cookies[0].value); + // form.append('app_file', fs.createReadStream(path.resolve('www.zip')), {filename: 'www.zip'}); + form.append('app_file', zip.toBuffer(), {filename: 'www.zip', contentType: 'application/zip'}); + + var url = ionic.IONIC_DASH+'projects/'+(project.app_id?project.app_id:''); + var params = parseUrl(url); + + form.submit({ + host: params.host, + path: params.path, + headers: form.getHeaders({ + cookie: jar.cookies.map(function (c) { + return c.name + "=" + encodeURIComponent(c.value) + }).join("; ") + }) + }, function(err, response, body) { // fs.unlinkSync('www.zip'); + console.log(response); + // console.log(body); if(err) { + console.trace(); ionic.fail("Error uploading: " + err); } - - console.log(response); }); - - var form = r.form(); - form.append('csrfmiddlewaretoken', jar.cookies[0].value); - form.append('app_file', fs.createReadStream(path.resolve('www.zip')), {filename: 'www.zip'}); - // form.append('app_file', zip.toBuffer(), {filename: 'www.zip'}); - // r._form = form; - // }); - - console.log('poop'); }); - }; exports.IonicUploadTask = IonicUploadTask; diff --git a/package.json b/package.json index 8f9319776d..cd652b3163 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, - "author": "Max Lynch ", + "author": "Max Lynch and Peter Collins ", "license": "MIT", "dependencies": { "adm-zip": "0.4.3", From 8906a89c53a560e6f31fd5fce114352537c51bae Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Mon, 13 Jan 2014 12:20:55 -0600 Subject: [PATCH 0036/1100] Save app_id for project when uploaded --- lib/ionic/upload.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index e532ce7c77..d734bcfb43 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -40,15 +40,15 @@ IonicUploadTask.prototype.run = function(ionic) { return c.name + "=" + encodeURIComponent(c.value) }).join("; ") }) - }, function(err, response, body) { - // fs.unlinkSync('www.zip'); - console.log(response); - // console.log(body); - + }, function(err, response) { if(err) { - console.trace(); ionic.fail("Error uploading: " + err); } + if(response.statusCode == 302) { + var redirectPath = parseUrl(response.headers.location); + project.app_id = redirectPath.pathname.match(/^\/projects\/(\w+)\/?$/)[1]; + ionic.saveConfig(project); + } }); }); }; From a7c6684bdb9e2daafeee20c5b2bcf59372eeec49 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Mon, 13 Jan 2014 14:08:50 -0600 Subject: [PATCH 0037/1100] Don't write zip file --- lib/ionic/upload.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index d734bcfb43..ccc5750702 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -22,7 +22,6 @@ IonicUploadTask.prototype.run = function(ionic) { login.get(ionic, function(jar) { var zip = new Zip(); zip.addLocalFolder('www'); - zip.writeZip('www.zip'); var form = new FormData(); form.append('csrfmiddlewaretoken', jar.cookies[0].value); From c8aedac953ce975366d44fb29259e59a40e68656 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 13 Jan 2014 19:10:55 -0600 Subject: [PATCH 0038/1100] Added initial plugin install, and build and run --- lib/ionic.js | 23 ++++++++++++++++++++- lib/ionic/build.js | 46 ++++++++++++++++++++++++++++++++++++++++++ lib/ionic/platform.js | 47 +++++++++++++++++++++++++++++++++++++++++++ lib/ionic/run.js | 46 ++++++++++++++++++++++++++++++++++++++++++ lib/ionic/start.js | 11 ++++++++++ package.json | 2 +- 6 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 lib/ionic/build.js create mode 100644 lib/ionic/platform.js create mode 100644 lib/ionic/run.js diff --git a/lib/ionic.js b/lib/ionic.js index 1c5870f7ce..9d1186486a 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -15,6 +15,9 @@ Copyright 2013 Drifty (http://drifty.com/) */ var IonicStartTask = require('./ionic/start.js').IonicStartTask; +var IonicPlatformTask = require('./ionic/platform.js').IonicPlatformTask; +var IonicRunTask = require('./ionic/run.js').IonicRunTask; +var IonicBuildTask = require('./ionic/build.js').IonicBuildTask; var argv = require('optimist').argv; @@ -24,6 +27,24 @@ var TASKS = [ name: 'start', usage: 'appname', task: IonicStartTask + }, + { + title: 'run', + name: 'run', + usage: 'run', + task: IonicRunTask + }, + { + title: 'build', + name: 'build', + usage: 'build', + task: IonicBuildTask + }, + { + title: 'platform', + name: 'platform', + usage: 'platform', + task: IonicPlatformTask } ]; @@ -54,7 +75,7 @@ Ionic.prototype = { for(var i = 0; i < TASKS.length; i++) { var task = TASKS[i]; - process.stderr.write(' ' + task.name + '\t\t' + task.task.HELP_LINE + '\n'); + process.stderr.write(' ' + task.name + ' - ' + task.task.HELP_LINE + '\n'); } process.stderr.write('\n'); diff --git a/lib/ionic/build.js b/lib/ionic/build.js new file mode 100644 index 0000000000..486ffc909a --- /dev/null +++ b/lib/ionic/build.js @@ -0,0 +1,46 @@ +var fs = require('fs'), + os = require('os'), + request = require('request'), + ncp = require('ncp').ncp, + path = require('path'), + shelljs = require('shelljs/global'), + unzip = require('unzip'), + IonicTask = require('./task').IonicTask; + +var argv = require('optimist').argv; + +var IonicBuildTask = function() { +} + +IonicBuildTask.HELP_LINE = 'Locally build an ionic project for a given platform'; + +IonicBuildTask.prototype = new IonicTask(); + +IonicBuildTask.prototype._printUsage = function() { + process.stderr.write('\nUsage: ionic build [platform]\n'); +} + +IonicBuildTask.prototype.run = function(ionic) { + var patform; + + if(argv._.length < 2) { + IonicBuildTask.prototype._printUsage(); + ionic.fail('No platforms specified, exiting.'); + } + + var platforms = argv._.slice(1); + + if(platforms.length < 1) { + ionic.fail('No platforms specified, exiting.'); + } + + for(var i = 0; i < platforms.length; i++) { + platform = platforms[i]; + console.log('Building platform', platform); + if(exec("cordova build " + platform).code !== 0) { + ionic.fail('Unable to build app on platform ' + platform + '. Please see console for more info.'); + } + } +}; + +exports.IonicBuildTask = IonicBuildTask; diff --git a/lib/ionic/platform.js b/lib/ionic/platform.js new file mode 100644 index 0000000000..9993cb3ac1 --- /dev/null +++ b/lib/ionic/platform.js @@ -0,0 +1,47 @@ +var fs = require('fs'), + os = require('os'), + request = require('request'), + ncp = require('ncp').ncp, + path = require('path'), + shelljs = require('shelljs/global'), + unzip = require('unzip'), + IonicTask = require('./task').IonicTask; + +var argv = require('optimist').argv; + +var IonicPlatformTask = function() { +} + +IonicPlatformTask.HELP_LINE = 'Configure platform targets for building an Ionic app'; + +IonicPlatformTask.prototype = new IonicTask(); + +IonicPlatformTask.prototype._printUsage = function() { + process.stderr.write('\nUsage: ionic platforms [platform1, platform2]\nSupported platforms currently "ios" and "android"\n\n'); +} + +IonicPlatformTask.prototype.run = function(ionic) { + var patform; + + if(argv._.length < 2) { + IonicPlatformTask.prototype._printUsage(); + ionic.fail('No platforms specified, exiting.'); + } + + // Grab the name of the app + var platforms = argv._.slice(1); + + if(platforms.length < 1) { + ionic.fail('No platforms specified, exiting.'); + } + + for(var i = 0; i < platforms.length; i++) { + platform = platforms[i]; + console.log('Adding platform', platform); + if(exec("cordova platform add " + platform).code !== 0) { + process.stderr.write('Unable to add platform ' + platform + '. Please see console for more info.\n'); + } + } +}; + +exports.IonicPlatformTask = IonicPlatformTask; diff --git a/lib/ionic/run.js b/lib/ionic/run.js new file mode 100644 index 0000000000..6010728608 --- /dev/null +++ b/lib/ionic/run.js @@ -0,0 +1,46 @@ +var fs = require('fs'), + os = require('os'), + request = require('request'), + ncp = require('ncp').ncp, + path = require('path'), + shelljs = require('shelljs/global'), + unzip = require('unzip'), + IonicTask = require('./task').IonicTask; + +var argv = require('optimist').argv; + +var IonicRunTask = function() { +} + +IonicRunTask.HELP_LINE = 'Run an ionic project on a connected device.'; + +IonicRunTask.prototype = new IonicTask(); + +IonicRunTask.prototype._printUsage = function() { + process.stderr.write('\nUsage: ionic run [platform]\n'); +} + +IonicRunTask.prototype.run = function(ionic) { + var patform; + + if(argv._.length < 2) { + IonicRunTask.prototype._printUsage(); + ionic.fail('No platforms specified, exiting.'); + } + + var platforms = argv._.slice(1); + + if(platforms.length < 1) { + ionic.fail('No platforms specified, exiting.'); + } + + for(var i = 0; i < platforms.length; i++) { + platform = platforms[i]; + console.log('Running app on platform', platform); + if(exec("cordova run " + platform).code !== 0) { + ionic.fail('Unable to run app on platform ' + platform + '. Please see console for more info.'); + } + } +}; + +exports.IonicRunTask = IonicRunTask; diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 650efb1d7b..9079838880 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -82,6 +82,8 @@ IonicStartTask.prototype._fetchAndWriteSeed = function() { cp('-R', _this.targetPath + '/' + 'ionic-angular-cordova-seed-master/.', _this.targetPath); rm('-rf', _this.targetPath + '/' + 'ionic-angular-cordova-seed-master/'); console.log('Project created!'); + + _this._finish(); }); readStream.pipe(writeStream); }; @@ -93,6 +95,15 @@ IonicStartTask.prototype._fetchAndWriteSeed = function() { }); }; +IonicStartTask.prototype._finish = function() { + cd(this.targetPath); + console.log('Initializing cordova project.'); + if(!exec('cordova plugin add org.apache.cordova.device') || !exec('cordova plugin add org.apache.cordova.console') || + !exec('cordova plugin add org.apache.cordova.statusbar')) { + process.stderr.write('Unable to install one or more cordova plugins.\n'); + } +}; + IonicStartTask.prototype._writeTemplateFolder = function() { console.log('Copying template to', this.targetPath); ncp('template', this.appName, function(err) { diff --git a/package.json b/package.json index 74e910bd49..4cada292c0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "0.9.09", + "version": "0.9.10", "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From bbbba6eb8b9cc4d842f24d2edf6a917a2be42ab5 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Tue, 14 Jan 2014 14:25:50 -0600 Subject: [PATCH 0039/1100] Move build to package and added alt names for commands --- lib/ionic.js | 16 +++++++++++++--- lib/ionic/{build.js => package.js} | 10 +++++----- 2 files changed, 18 insertions(+), 8 deletions(-) rename lib/ionic/{build.js => package.js} (70%) diff --git a/lib/ionic.js b/lib/ionic.js index 13b35f118e..6e3498c172 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -38,14 +38,16 @@ var TASKS = [ { title: 'upload', name: 'upload', + alt: ['up'], usage: '', task: IonicUploadTask }, { - title: 'build', - name: 'build', + title: 'package', + name: 'package', + alt: ['pack'], usage: 'platform', - task: IonicBuildTask + task: IonicPackageTask } ]; @@ -72,6 +74,14 @@ Ionic.prototype = { if(t.name === name) { return t; } + if(t.alt) { + for(var j = 0; j < t.alt.length; j++) { + var alt = t.alt[j]; + if(alt === name) { + return t; + } + } + } } }, diff --git a/lib/ionic/build.js b/lib/ionic/package.js similarity index 70% rename from lib/ionic/build.js rename to lib/ionic/package.js index 811d7770fc..fd93cb1b28 100644 --- a/lib/ionic/build.js +++ b/lib/ionic/package.js @@ -3,14 +3,14 @@ var request = require('request'), IonicTask = require('./task').IonicTask, IonicLoginTask = require('./login.js').IonicLoginTask; -var IonicBuildTask = function() { +var IonicPackageTask = function() { } -IonicBuildTask.HELP_LINE = 'Build an Ionic project for the given plaform.'; +IonicPackageTask.HELP_LINE = 'Package an Ionic project for the given plaform.'; -IonicBuildTask.prototype = new IonicTask(); +IonicPackageTask.prototype = new IonicTask(); -IonicBuildTask.prototype.run = function(ionic) { +IonicPackageTask.prototype.run = function(ionic) { if(argv._.length < 2) { ionic.fail('No plaform specified, exiting.'); } @@ -37,4 +37,4 @@ IonicBuildTask.prototype.run = function(ionic) { }); }; -exports.IonicBuildTask = IonicBuildTask; +exports.IonicPackageTask = IonicPackageTask; From 025bd8bdc196a6ef796460d3f4367e3757713394 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Tue, 14 Jan 2014 14:57:04 -0600 Subject: [PATCH 0040/1100] Fix formatting and make more consistent code --- lib/ionic/build.js | 16 +++------------- lib/ionic/package.js | 38 +++++++++++++++++++++++--------------- lib/ionic/platform.js | 16 +++------------- lib/ionic/run.js | 16 +++------------- 4 files changed, 32 insertions(+), 54 deletions(-) diff --git a/lib/ionic/build.js b/lib/ionic/build.js index 486ffc909a..eb948aff7d 100644 --- a/lib/ionic/build.js +++ b/lib/ionic/build.js @@ -1,14 +1,6 @@ -var fs = require('fs'), - os = require('os'), - request = require('request'), - ncp = require('ncp').ncp, - path = require('path'), - shelljs = require('shelljs/global'), - unzip = require('unzip'), +var argv = require('optimist').argv, IonicTask = require('./task').IonicTask; -var argv = require('optimist').argv; - var IonicBuildTask = function() { } @@ -17,12 +9,10 @@ IonicBuildTask.HELP_LINE = 'Locally build an ionic project for a given platform' IonicBuildTask.prototype = new IonicTask(); IonicBuildTask.prototype._printUsage = function() { - process.stderr.write('\nUsage: ionic build [platform]\n'); + process.stderr.write('\nUsage: ionic build platform [more platforms,...]\n'); } IonicBuildTask.prototype.run = function(ionic) { - var patform; - if(argv._.length < 2) { IonicBuildTask.prototype._printUsage(); ionic.fail('No platforms specified, exiting.'); @@ -35,7 +25,7 @@ IonicBuildTask.prototype.run = function(ionic) { } for(var i = 0; i < platforms.length; i++) { - platform = platforms[i]; + var platform = platforms[i]; console.log('Building platform', platform); if(exec("cordova build " + platform).code !== 0) { ionic.fail('Unable to build app on platform ' + platform + '. Please see console for more info.'); diff --git a/lib/ionic/package.js b/lib/ionic/package.js index fd93cb1b28..26b46adec7 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -10,30 +10,38 @@ IonicPackageTask.HELP_LINE = 'Package an Ionic project for the given plaform.'; IonicPackageTask.prototype = new IonicTask(); +IonicPackageTask.prototype._printUsage = function() { + process.stderr.write('\nUsage: ionic package platform [more platforms,...]\n'); +} + IonicPackageTask.prototype.run = function(ionic) { if(argv._.length < 2) { - ionic.fail('No plaform specified, exiting.'); + IonicPackageTask.prototype._printUsage(); + ionic.fail('No platforms specified, exiting.'); } - // Grab the platform build for the app - this.platform = argv._[1]; + var platforms = argv._.slice(1); + + if(platforms.length < 1) { + ionic.fail('No platforms specified, exiting.'); + } var project = ionic.loadConfig(); var login = new IonicLoginTask(); login.get(ionic, function(jar) { - request({ - method: 'POST', - url: ionic.IONIC_DASH+'export/', - jar: jar - }, - function(err, response, body) { - if(err) { - ionic.fail("Error building: " + err); - } - }); - console.log(jar); - console.log(project.name); + for(var i = 0; i < platforms.length; i++) { + request({ + method: 'POST', + url: ionic.IONIC_DASH+'export/', + jar: jar + }, + function(err, response, body) { + if(err) { + ionic.fail("Error building: " + err); + } + }); + } }); }; diff --git a/lib/ionic/platform.js b/lib/ionic/platform.js index 9993cb3ac1..68eb4c5042 100644 --- a/lib/ionic/platform.js +++ b/lib/ionic/platform.js @@ -1,14 +1,6 @@ -var fs = require('fs'), - os = require('os'), - request = require('request'), - ncp = require('ncp').ncp, - path = require('path'), - shelljs = require('shelljs/global'), - unzip = require('unzip'), +var argv = require('optimist').argv, IonicTask = require('./task').IonicTask; -var argv = require('optimist').argv; - var IonicPlatformTask = function() { } @@ -17,12 +9,10 @@ IonicPlatformTask.HELP_LINE = 'Configure platform targets for building an Ionic IonicPlatformTask.prototype = new IonicTask(); IonicPlatformTask.prototype._printUsage = function() { - process.stderr.write('\nUsage: ionic platforms [platform1, platform2]\nSupported platforms currently "ios" and "android"\n\n'); + process.stderr.write('\nUsage: ionic platform [more platforms,...]\nSupported platforms currently "ios" and "android"\n\n'); } IonicPlatformTask.prototype.run = function(ionic) { - var patform; - if(argv._.length < 2) { IonicPlatformTask.prototype._printUsage(); ionic.fail('No platforms specified, exiting.'); @@ -36,7 +26,7 @@ IonicPlatformTask.prototype.run = function(ionic) { } for(var i = 0; i < platforms.length; i++) { - platform = platforms[i]; + var platform = platforms[i]; console.log('Adding platform', platform); if(exec("cordova platform add " + platform).code !== 0) { process.stderr.write('Unable to add platform ' + platform + '. Please see console for more info.\n'); diff --git a/lib/ionic/run.js b/lib/ionic/run.js index 6010728608..995c801b46 100644 --- a/lib/ionic/run.js +++ b/lib/ionic/run.js @@ -1,14 +1,6 @@ -var fs = require('fs'), - os = require('os'), - request = require('request'), - ncp = require('ncp').ncp, - path = require('path'), - shelljs = require('shelljs/global'), - unzip = require('unzip'), +var argv = require('optimist').argv, IonicTask = require('./task').IonicTask; -var argv = require('optimist').argv; - var IonicRunTask = function() { } @@ -17,12 +9,10 @@ IonicRunTask.HELP_LINE = 'Run an ionic project on a connected device.'; IonicRunTask.prototype = new IonicTask(); IonicRunTask.prototype._printUsage = function() { - process.stderr.write('\nUsage: ionic run [platform]\n'); + process.stderr.write('\nUsage: ionic run platform [more platforms,...]\n'); } IonicRunTask.prototype.run = function(ionic) { - var patform; - if(argv._.length < 2) { IonicRunTask.prototype._printUsage(); ionic.fail('No platforms specified, exiting.'); @@ -35,7 +25,7 @@ IonicRunTask.prototype.run = function(ionic) { } for(var i = 0; i < platforms.length; i++) { - platform = platforms[i]; + var platform = platforms[i]; console.log('Running app on platform', platform); if(exec("cordova run " + platform).code !== 0) { ionic.fail('Unable to run app on platform ' + platform + '. Please see console for more info.'); From 746055bc6f5984f9c9cdc1fe391ec15c04e9209e Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Wed, 15 Jan 2014 14:21:57 -0600 Subject: [PATCH 0041/1100] Getting ready to export --- lib/ionic.js | 29 +++++++++++++------ lib/ionic/login.js | 12 ++++++++ lib/ionic/package.js | 68 ++++++++++++++++++++++++++++++-------------- lib/ionic/upload.js | 8 ++++-- 4 files changed, 86 insertions(+), 31 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index ca4bd69086..de4e1caecf 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -14,13 +14,13 @@ Licensed under the MIT license. See LICENSE For more. Copyright 2013 Drifty (http://drifty.com/) */ -var IonicStartTask = require('./ionic/start.js').IonicStartTask, - IonicPlatformTask = require('./ionic/platform.js').IonicPlatformTask; - IonicRunTask = require('./ionic/run.js').IonicRunTask; - IonicBuildTask = require('./ionic/build.js').IonicBuildTask; - IonicLoginTask = require('./ionic/login.js').IonicLoginTask, - IonicUploadTask = require('./ionic/upload.js').IonicUploadTask, - IonicPackageTask = require('./ionic/package.js').IonicPackageTask; +var IonicStartTask = require('./ionic/start').IonicStartTask, + IonicPlatformTask = require('./ionic/platform').IonicPlatformTask, + IonicRunTask = require('./ionic/run').IonicRunTask, + IonicBuildTask = require('./ionic/build').IonicBuildTask, + IonicLoginTask = require('./ionic/login').IonicLoginTask, + IonicUploadTask = require('./ionic/upload').IonicUploadTask, + IonicPackageTask = require('./ionic/package').IonicPackageTask; var fs = require('fs'), argv = require('optimist').argv; @@ -67,7 +67,7 @@ var TASKS = [ title: 'package', name: 'package', alt: ['pack'], - usage: 'platform', + usage: 'mode platform', task: IonicPackageTask } ]; @@ -79,6 +79,19 @@ Ionic.prototype = { IONIC_COOKIES: 'ionic.cookies', IONIC_CONFIG: 'ionic.config', IONIC_API: 'api/v1/', + CONFIG_DEFAULT: { + name: '', + email: '', + app_id: '', + package_name: '', + ios_certificate: '', + ios_certificate_password: '', + ios_profile: '', + android_keystore: '', + android_keystore_alias: '', + android_keystore_password: '', + android_key_password: '' + }, _tryBuildingTask: function() { if(argv._.length == 0) { diff --git a/lib/ionic/login.js b/lib/ionic/login.js index c749e61ebe..cb40e3077d 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -24,11 +24,18 @@ IonicLoginTask.prototype.run = function(ionic, callback) { required: true }]; + var project = ionic.loadConfig(); + // Grab the email for login if(argv._.length >= 2 && schema[0].pattern.test(argv._[1])) { this.email = argv._[1].toLowerCase(); schema.shift(); } + // Assume project email is login email if it exists + else if(project.email && project.email != '') { + this.email = project.email; + schema.shift(); + } prompt.override = argv; @@ -43,6 +50,11 @@ IonicLoginTask.prototype.run = function(ionic, callback) { self.email = result.email.toLowerCase(); } self.password = result.password; + + if(!project.email || project.email == '') { + project.email = self.email; + ionic.saveConfig(project); + } var jar = request.jar(); request({ diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 26b46adec7..1e021438a4 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -1,7 +1,9 @@ var request = require('request'), argv = require('optimist').argv, + prompt = require('prompt'), IonicTask = require('./task').IonicTask, - IonicLoginTask = require('./login.js').IonicLoginTask; + IonicUploadTask = require('./upload').IonicUploadTask; + IonicLoginTask = require('./login').IonicLoginTask; var IonicPackageTask = function() { } @@ -11,38 +13,62 @@ IonicPackageTask.HELP_LINE = 'Package an Ionic project for the given plaform.'; IonicPackageTask.prototype = new IonicTask(); IonicPackageTask.prototype._printUsage = function() { - process.stderr.write('\nUsage: ionic package platform [more platforms,...]\n'); + process.stderr.write('\nUsage: ionic package mode(debug|release) platform [more platforms,...]\n'); } IonicPackageTask.prototype.run = function(ionic) { - if(argv._.length < 2) { + if(argv._.length < 3) { IonicPackageTask.prototype._printUsage(); - ionic.fail('No platforms specified, exiting.'); + ionic.fail('No platforms or build mode specified, exiting.'); } - var platforms = argv._.slice(1); + var mode = argv._[1].toLowerCase(); + if(mode != 'debug' && mode != 'release') { + IonicPackageTask.prototype._printUsage(); + ionic.fail('Package build mode must be debug or release, exiting.'); + } + + var platforms = argv._.slice(2); if(platforms.length < 1) { ionic.fail('No platforms specified, exiting.'); } - var project = ionic.loadConfig(); - - var login = new IonicLoginTask(); - login.get(ionic, function(jar) { - for(var i = 0; i < platforms.length; i++) { - request({ - method: 'POST', - url: ionic.IONIC_DASH+'export/', - jar: jar - }, - function(err, response, body) { - if(err) { - ionic.fail("Error building: " + err); + var upload = new IonicUploadTask(); + upload.run(ionic, function() { + + var login = new IonicLoginTask(); + login.get(ionic, function(jar) { + + var project = ionic.loadConfig(); + + for(var i = 0; i < platforms.length; i++) { + var platform = platforms[i]; + + switch (platform) { + case 'android': + break; + case 'ios': + break; + default: + process.stderr.write('\nUnknown platform: "'+platform+'"\nSupported platforms currently "ios" and "android"\n\n'); + continue; } - }); - } - }); + + request({ + method: 'POST', + url: ionic.IONIC_DASH+'export/', + jar: jar + }, + function(err, response, body) { + if(err) { + ionic.fail("Error packaging: " + err); + } + }); + } + + }); + }); }; exports.IonicPackageTask = IonicPackageTask; diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index ccc5750702..457cceb099 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -6,7 +6,7 @@ var fs = require('fs'), FormData = require('form-data'), Zip = require('adm-zip'), IonicTask = require('./task').IonicTask, - IonicLoginTask = require('./login.js').IonicLoginTask; + IonicLoginTask = require('./login').IonicLoginTask; var IonicUploadTask = function() { } @@ -15,7 +15,7 @@ IonicUploadTask.HELP_LINE = 'Upload an Ionic project.'; IonicUploadTask.prototype = new IonicTask(); -IonicUploadTask.prototype.run = function(ionic) { +IonicUploadTask.prototype.run = function(ionic, callback) { var project = ionic.loadConfig(); var login = new IonicLoginTask(); @@ -47,6 +47,10 @@ IonicUploadTask.prototype.run = function(ionic) { var redirectPath = parseUrl(response.headers.location); project.app_id = redirectPath.pathname.match(/^\/projects\/(\w+)\/?$/)[1]; ionic.saveConfig(project); + + if (callback) { + callback(); + }; } }); }); From 3ea1d14780d02df0f77de00cbfd4c71a4f3581d7 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Wed, 15 Jan 2014 15:59:57 -0600 Subject: [PATCH 0042/1100] Use default config for template --- lib/ionic.js | 6 +++--- lib/ionic/package.js | 28 +++++++++++++++++++--------- lib/ionic/start.js | 12 ++++++------ 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index de4e1caecf..da3269216d 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -166,10 +166,10 @@ Ionic.prototype = { return JSON.parse(fs.readFileSync(this.IONIC_CONFIG)); }, - saveConfig: function(project) { - var err = fs.writeFileSync(this.IONIC_CONFIG, JSON.stringify(project, null, 2)); + saveConfig: function(project, targetPath) { + var err = fs.writeFileSync((targetPath?targetPath+'/':'')+this.IONIC_CONFIG, JSON.stringify(project, null, 2)); if(err) { - process.stderr.write('Error writing ' + ionic.IONIC_CONFIG + ': ' + err + '\n'); + process.stderr.write('Error writing ' + (targetPath?targetPath+'/':'')+ ionic.IONIC_CONFIG + ': ' + err + '\n'); } } diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 1e021438a4..8555502843 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -2,7 +2,7 @@ var request = require('request'), argv = require('optimist').argv, prompt = require('prompt'), IonicTask = require('./task').IonicTask, - IonicUploadTask = require('./upload').IonicUploadTask; + IonicUploadTask = require('./upload').IonicUploadTask, IonicLoginTask = require('./login').IonicLoginTask; var IonicPackageTask = function() { @@ -45,8 +45,12 @@ IonicPackageTask.prototype.run = function(ionic) { for(var i = 0; i < platforms.length; i++) { var platform = platforms[i]; + prompt.override = argv; + prompt.start(); + var schema; + switch (platform) { - case 'android': + case 'android': break; case 'ios': break; @@ -55,15 +59,21 @@ IonicPackageTask.prototype.run = function(ionic) { continue; } - request({ - method: 'POST', - url: ionic.IONIC_DASH+'export/', - jar: jar - }, - function(err, response, body) { + prompt.get(schema, function (err, result) { if(err) { - ionic.fail("Error packaging: " + err); + ionic.fail('Error packaging: ' + err); } + + request({ + method: 'POST', + url: ionic.IONIC_DASH+'export/', + jar: jar + }, + function(err, response, body) { + if(err) { + ionic.fail("Error packaging: " + err); + } + }); }); } diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 5eb8133f7e..516a3a8505 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -95,12 +95,12 @@ IonicStartTask.prototype._fetchAndWriteSeed = function() { }; IonicStartTask.prototype._writeConfig = function(ionic) { - console.log('Writing ionic.config', this.targetPath); - var err = fs.writeFileSync(this.targetPath + '/' + ionic.IONIC_CONFIG, JSON.stringify({name: this.appName}, null, 2)); - if(err) { - process.stderr.write('Error writing ' + this.targetPath + '/' + ionic.IONIC_CONFIG + ': ' + err + '\n'); - return; - } + console.log('Writing '+this.targetPath+'/ionic.config'); + + var project = ionic.CONFIG_DEFAULT; + project.name = this.appName; + + ionic.saveConfig(project, this.targetPath); }; IonicStartTask.prototype._checkTargetPath = function() { From 013d6f5f7623e6b8bd226491a31d73b297f562a8 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Wed, 15 Jan 2014 19:06:30 -0600 Subject: [PATCH 0043/1100] Add package properties --- lib/ionic.js | 5 ++++ lib/ionic/package.js | 70 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index da3269216d..9bc2852ffa 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -167,6 +167,11 @@ Ionic.prototype = { }, saveConfig: function(project, targetPath) { + if(!project) { + console.trace(); + this.fail('This should never happen!'); + } + var err = fs.writeFileSync((targetPath?targetPath+'/':'')+this.IONIC_CONFIG, JSON.stringify(project, null, 2)); if(err) { process.stderr.write('Error writing ' + (targetPath?targetPath+'/':'')+ ionic.IONIC_CONFIG + ': ' + err + '\n'); diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 8555502843..113ebea833 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -47,26 +47,88 @@ IonicPackageTask.prototype.run = function(ionic) { prompt.override = argv; prompt.start(); - var schema; + var properties = {}; switch (platform) { - case 'android': + case 'android': + if(mode == 'release') { + properties = { + android_keystore: { + description: 'Android Keystore File (.keystore)', + message: 'Relative path to your release keystore file (eg. release.keystore)', + required: true + }, + android_keystore_alias: { + description: 'Keystore Alias', + message: 'Alias of the Keystore', + required: true + }, + android_keystore_password: { + description: 'Keystore Password', + message: 'Password of the Keystore', + hidden: true, + required: true + }, + android_key_password: { + description: 'Key Password', + message: 'Password for Key (usually same as Keystore Password and if left blank will use it)', + hidden: true, + } + }; + } + break; case 'ios': + properties = { + ios_certificate: { + description: 'iOS Certificate File (.p12)', + message: 'Relative path to your certificate file (eg. cert.p12)', + required: true + }, + ios_certificate_password: { + description: 'Certificate Password', + message: 'Password of the Certificate', + hidden: true, + required: true + }, + ios_profile: { + description: 'iOS Mobile Provisioning Profile (.mobileprofile)', + message: 'Relative path to your Mobile Provisioning Profile (eg. my.mobileprofile)', + required: true + }, + }; break; default: process.stderr.write('\nUnknown platform: "'+platform+'"\nSupported platforms currently "ios" and "android"\n\n'); continue; } - prompt.get(schema, function (err, result) { + properties.package_name = { + description: 'Package Name (eg. com.mycompany.app)', + message: 'Package Name (eg. com.mycompany.app) not required', + }; + + for (var property in properties) { + if(project[property] && project[project] == '') { + delete properties[property]; + } + } + + prompt.get({properties: properties}, function (err, result) { if(err) { ionic.fail('Error packaging: ' + err); } + + for (var property in properties) { + if(project[property] && project[project] == '') { + project[property] = result[property]; + } + } + ionic.saveConfig(project); request({ method: 'POST', - url: ionic.IONIC_DASH+'export/', + url: ionic.IONIC_DASH+'export/'+project.app_id, jar: jar }, function(err, response, body) { From 23602bd42ee8d4f504700dbc6a1f4c64cd036533 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Thu, 16 Jan 2014 14:39:58 -0600 Subject: [PATCH 0044/1100] Finish package command --- lib/ionic/package.js | 82 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 9 deletions(-) diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 113ebea833..df39bcd421 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -49,8 +49,10 @@ IonicPackageTask.prototype.run = function(ionic) { prompt.start(); var properties = {}; + // Just prompt for some build properties switch (platform) { case 'android': + // Android debug doesn't require anything if(mode == 'release') { properties = { android_keystore: { @@ -99,15 +101,17 @@ IonicPackageTask.prototype.run = function(ionic) { }; break; default: - process.stderr.write('\nUnknown platform: "'+platform+'"\nSupported platforms currently "ios" and "android"\n\n'); + process.stderr.write('\nUnknown platform: "'+platform+'"\nSupported platforms currently are "ios" and "android"\n\n'); continue; } + // Should we just not add this since it's optional? properties.package_name = { - description: 'Package Name (eg. com.mycompany.app)', - message: 'Package Name (eg. com.mycompany.app) not required', + description: 'Package Name (eg. com.mycompany.app) optional', + message: 'Package Name (eg. com.mycompany.app) optional', }; + // Don't prompt for properties we already have in the config for (var property in properties) { if(project[property] && project[project] == '') { delete properties[property]; @@ -119,19 +123,79 @@ IonicPackageTask.prototype.run = function(ionic) { ionic.fail('Error packaging: ' + err); } + // Overwrite any empty properties with prompt responses for (var property in properties) { if(project[property] && project[project] == '') { project[property] = result[property]; } } ionic.saveConfig(project); + + var form = new FormData(); + form.append('platform', platform); + form.append('build_mode', mode); + form.append('csrfmiddlewaretoken', jar.cookies[0].value); + + // Add the project properties to the post + for (var property in project) { + if(property.indexOf(platform)) { + form.append(property, project[project]); + } + } - request({ - method: 'POST', - url: ionic.IONIC_DASH+'export/'+project.app_id, - jar: jar - }, - function(err, response, body) { + // Gimmie those sweet sweet files + switch(platform) { + case 'android': + var keystoreFilePath = path.resolve(project.android_keystore); + if(mode = 'release') { + if(fs.existsSync(keystoreFilePath)) { + form.append('android_keystore_file', fs.createReadStream(keystoreFilePath)); + } else { + delete project.android_keystore; + ionic.saveConfig(project); + process.stderr.write('\nCan\'t find file: "'+keystoreFilePath+'"\nSkipping build..."\n\n'); + return; + } + } + break; + case 'ios': + var certificateFilePath = path.resolve(project.ios_certificate); + if(fs.existsSync(certificateFilePath)) { + form.append('ios_certificate_file', fs.createReadStream(certificateFilePath)); + } else { + delete project.ios_certificate; + ionic.saveConfig(project); + process.stderr.write('\nCan\'t find file: "'+certificateFilePath+'"\nSkipping build..."\n\n'); + return; + } + var profileFilePath = path.resolve(project.ios_profile); + if(fs.existsSync(profileFilePath)) { + form.append('ios_profile_file', fs.createReadStream(profileFilePath)); + } else { + delete project.ios_profile; + ionic.saveConfig(project); + process.stderr.write('\nCan\'t find file: "'+profileFilePath+'"\nSkipping build..."\n\n'); + return; + } + break; + default: + console.trace(); + process.stderr.write('\nUnknown platform: "'+platform+'"\nWe should never get here"\n\n'); + break; + } + + var url = ionic.IONIC_DASH+'export/'+project.app_id; + var params = parseUrl(url); + + form.submit({ + host: params.host, + path: params.path, + headers: form.getHeaders({ + cookie: jar.cookies.map(function (c) { + return c.name + "=" + encodeURIComponent(c.value) + }).join("; ") + }) + }, function(err, response) { if(err) { ionic.fail("Error packaging: " + err); } From 132e7a47eba46fc19c1bb14d401494c429074e9d Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Thu, 16 Jan 2014 15:57:47 -0600 Subject: [PATCH 0045/1100] Small fixes --- lib/ionic/package.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/ionic/package.js b/lib/ionic/package.js index df39bcd421..825c1c9297 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -1,6 +1,9 @@ -var request = require('request'), +var fs = require('fs'), + path = require('path'), + parseUrl = require('url').parse, argv = require('optimist').argv, prompt = require('prompt'), + FormData = require('form-data'), IonicTask = require('./task').IonicTask, IonicUploadTask = require('./upload').IonicUploadTask, IonicLoginTask = require('./login').IonicLoginTask; @@ -132,13 +135,15 @@ IonicPackageTask.prototype.run = function(ionic) { ionic.saveConfig(project); var form = new FormData(); + form.append('email', project.email); form.append('platform', platform); form.append('build_mode', mode); form.append('csrfmiddlewaretoken', jar.cookies[0].value); - // Add the project properties to the post + // Add the platform specific project properties to the post + console.log(project); for (var property in project) { - if(property.indexOf(platform)) { + if(property.indexOf(platform) == 0) { form.append(property, project[project]); } } @@ -146,10 +151,10 @@ IonicPackageTask.prototype.run = function(ionic) { // Gimmie those sweet sweet files switch(platform) { case 'android': - var keystoreFilePath = path.resolve(project.android_keystore); - if(mode = 'release') { + if(mode == 'release') { + var keystoreFilePath = path.resolve(project.android_keystore); if(fs.existsSync(keystoreFilePath)) { - form.append('android_keystore_file', fs.createReadStream(keystoreFilePath)); + form.append('android_keystore_file', fs.createReadStream(keystoreFilePath), {filename: 'www.zip'}); } else { delete project.android_keystore; ionic.saveConfig(project); From e25a00e8b21332f66c0c36ac147fc25a6b2184cc Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Thu, 16 Jan 2014 21:19:27 -0600 Subject: [PATCH 0046/1100] Try using tar.gz doesn't work --- lib/ionic/package.js | 23 +++++++++++++---------- lib/ionic/start.js | 3 ++- lib/ionic/upload.js | 20 ++++++++++++++------ package.json | 4 +++- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 825c1c9297..b5ffb76f05 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -37,8 +37,8 @@ IonicPackageTask.prototype.run = function(ionic) { ionic.fail('No platforms specified, exiting.'); } - var upload = new IonicUploadTask(); - upload.run(ionic, function() { + //var upload = new IonicUploadTask(); + //upload.run(ionic, function() { var login = new IonicLoginTask(); login.get(ionic, function(jar) { @@ -75,7 +75,7 @@ IonicPackageTask.prototype.run = function(ionic) { required: true }, android_key_password: { - description: 'Key Password', + description: 'Key Password (optional)', message: 'Password for Key (usually same as Keystore Password and if left blank will use it)', hidden: true, } @@ -109,10 +109,10 @@ IonicPackageTask.prototype.run = function(ionic) { } // Should we just not add this since it's optional? - properties.package_name = { - description: 'Package Name (eg. com.mycompany.app) optional', - message: 'Package Name (eg. com.mycompany.app) optional', - }; + // properties.package_name = { + // description: 'Package Name (eg. com.mycompany.app) optional', + // message: 'Package Name (eg. com.mycompany.app) optional', + // }; // Don't prompt for properties we already have in the config for (var property in properties) { @@ -141,14 +141,13 @@ IonicPackageTask.prototype.run = function(ionic) { form.append('csrfmiddlewaretoken', jar.cookies[0].value); // Add the platform specific project properties to the post - console.log(project); for (var property in project) { if(property.indexOf(platform) == 0) { form.append(property, project[project]); } } - // Gimmie those sweet sweet files + // Gimmie dem sweet sweet files switch(platform) { case 'android': if(mode == 'release') { @@ -201,6 +200,10 @@ IonicPackageTask.prototype.run = function(ionic) { }).join("; ") }) }, function(err, response) { + response.on("data", function(data) { + console.log(data); + }); + if(err) { ionic.fail("Error packaging: " + err); } @@ -209,7 +212,7 @@ IonicPackageTask.prototype.run = function(ionic) { } }); - }); + //}); }; exports.IonicPackageTask = IonicPackageTask; diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 516a3a8505..37ed0fbc63 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -3,6 +3,7 @@ var fs = require('fs'), request = require('request'), ncp = require('ncp').ncp, path = require('path'), + shelljs = require('shelljs/global'), unzip = require('unzip'), argv = require('optimist').argv, IonicTask = require('./task').IonicTask; @@ -77,7 +78,7 @@ IonicStartTask.prototype._fetchAndWriteSeed = function() { rm('-rf', self.targetPath + '/' + 'ionic-angular-cordova-seed-master/'); console.log('Project created!'); - cd(this.targetPath); + cd(self.targetPath); console.log('Initializing cordova project.'); if(!exec('cordova plugin add org.apache.cordova.device') || !exec('cordova plugin add org.apache.cordova.console') || !exec('cordova plugin add org.apache.cordova.statusbar')) { diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index 457cceb099..d3724be4ad 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -3,8 +3,11 @@ var fs = require('fs'), parseUrl = require('url').parse, request = require('request'), argv = require('optimist').argv, + shelljs = require('shelljs/global'), + fstream = require('fstream'), + tar = require('tar'), + zlib = require('zlib'), FormData = require('form-data'), - Zip = require('adm-zip'), IonicTask = require('./task').IonicTask, IonicLoginTask = require('./login').IonicLoginTask; @@ -20,13 +23,17 @@ IonicUploadTask.prototype.run = function(ionic, callback) { var login = new IonicLoginTask(); login.get(ionic, function(jar) { - var zip = new Zip(); - zip.addLocalFolder('www'); - var form = new FormData(); form.append('csrfmiddlewaretoken', jar.cookies[0].value); - // form.append('app_file', fs.createReadStream(path.resolve('www.zip')), {filename: 'www.zip'}); - form.append('app_file', zip.toBuffer(), {filename: 'www.zip', contentType: 'application/zip'}); + + + // Using this http://stackoverflow.com/questions/15530435/node-js-zip-unzip-a-folder + fstream.Reader({ 'path': 'www', 'type': 'Directory' }) /* Read the source directory */ + .pipe(tar.Pack()) /* Convert the directory to a .tar file */ + .pipe(zlib.Gzip()) /* Compress the .tar file */ + .pipe(fstream.Writer({ 'path': 'www.tar.gz', 'type': 'File' })); + form.append('app_file', fs.createReadStream(path.resolve('www.tar.gz')), {filename: 'www.tar.gz', contentType: 'application/x-gzip'}); + // form.append('app_file', zip.toBuffer(), {filename: 'www.zip', contentType: 'application/zip'}); var url = ionic.IONIC_DASH+'projects/'+(project.app_id?project.app_id:''); var params = parseUrl(url); @@ -40,6 +47,7 @@ IonicUploadTask.prototype.run = function(ionic, callback) { }).join("; ") }) }, function(err, response) { + //rm('-f', 'www.tar.gz'); if(err) { ionic.fail("Error uploading: " + err); } diff --git a/package.json b/package.json index 9ae2025ed9..56cbfad473 100644 --- a/package.json +++ b/package.json @@ -25,13 +25,15 @@ "author": "Max Lynch and Peter Collins ", "license": "MIT", "dependencies": { - "adm-zip": "0.4.3", "cordova": "~3.2.0", "form-data": "~0.1.0", + "fstream": "0.1.25", "ncp": "0.4.2", "optimist": "0.6.0", "prompt": "0.2.12", "request": "2.27.0", + "shelljs": "0.2.6", + "tar": "0.1.19", "unzip": "0.1.9" } } From fe3955c09eae7fdccff4a43df4f7056493c26851 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Thu, 16 Jan 2014 22:01:31 -0600 Subject: [PATCH 0047/1100] Got zips to work and upload --- lib/ionic/package.js | 7 ++-- lib/ionic/upload.js | 77 ++++++++++++++++++++++++-------------------- package.json | 3 +- 3 files changed, 46 insertions(+), 41 deletions(-) diff --git a/lib/ionic/package.js b/lib/ionic/package.js index b5ffb76f05..324d86d242 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -37,8 +37,8 @@ IonicPackageTask.prototype.run = function(ionic) { ionic.fail('No platforms specified, exiting.'); } - //var upload = new IonicUploadTask(); - //upload.run(ionic, function() { + var upload = new IonicUploadTask(); + upload.run(ionic, function() { var login = new IonicLoginTask(); login.get(ionic, function(jar) { @@ -210,9 +210,8 @@ IonicPackageTask.prototype.run = function(ionic) { }); }); } - }); - //}); + }); }; exports.IonicPackageTask = IonicPackageTask; diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index d3724be4ad..3e1837f966 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -1,12 +1,8 @@ var fs = require('fs'), path = require('path'), parseUrl = require('url').parse, - request = require('request'), + archiver = require('archiver'), argv = require('optimist').argv, - shelljs = require('shelljs/global'), - fstream = require('fstream'), - tar = require('tar'), - zlib = require('zlib'), FormData = require('form-data'), IonicTask = require('./task').IonicTask, IonicLoginTask = require('./login').IonicLoginTask; @@ -23,43 +19,54 @@ IonicUploadTask.prototype.run = function(ionic, callback) { var login = new IonicLoginTask(); login.get(ionic, function(jar) { - var form = new FormData(); - form.append('csrfmiddlewaretoken', jar.cookies[0].value); + + var zip = fs.createWriteStream('www.zip'); + var archive = archiver('zip'); + archive.pipe(zip); - // Using this http://stackoverflow.com/questions/15530435/node-js-zip-unzip-a-folder - fstream.Reader({ 'path': 'www', 'type': 'Directory' }) /* Read the source directory */ - .pipe(tar.Pack()) /* Convert the directory to a .tar file */ - .pipe(zlib.Gzip()) /* Compress the .tar file */ - .pipe(fstream.Writer({ 'path': 'www.tar.gz', 'type': 'File' })); - form.append('app_file', fs.createReadStream(path.resolve('www.tar.gz')), {filename: 'www.tar.gz', contentType: 'application/x-gzip'}); - // form.append('app_file', zip.toBuffer(), {filename: 'www.zip', contentType: 'application/zip'}); + archive.bulk([ + { expand: true, cwd: 'www/', src: ['**'] } + ]); - var url = ionic.IONIC_DASH+'projects/'+(project.app_id?project.app_id:''); - var params = parseUrl(url); - - form.submit({ - host: params.host, - path: params.path, - headers: form.getHeaders({ - cookie: jar.cookies.map(function (c) { - return c.name + "=" + encodeURIComponent(c.value) - }).join("; ") - }) - }, function(err, response) { - //rm('-f', 'www.tar.gz'); + archive.finalize(function(err, bytes) { if(err) { ionic.fail("Error uploading: " + err); } - if(response.statusCode == 302) { - var redirectPath = parseUrl(response.headers.location); - project.app_id = redirectPath.pathname.match(/^\/projects\/(\w+)\/?$/)[1]; - ionic.saveConfig(project); + }); - if (callback) { - callback(); - }; - } + zip.on('close', function() { + var form = new FormData(); + form.append('csrfmiddlewaretoken', jar.cookies[0].value); + form.append('app_file', fs.createReadStream(path.resolve('www.zip')), {filename: 'www.zip', contentType: 'application/zip'}); + // form.append('app_file', zip.toBuffer(), {filename: 'www.zip', contentType: 'application/zip'}); + + var url = ionic.IONIC_DASH+'projects/'+(project.app_id?project.app_id:''); + var params = parseUrl(url); + + form.submit({ + host: params.host, + path: params.path, + headers: form.getHeaders({ + cookie: jar.cookies.map(function (c) { + return c.name + "=" + encodeURIComponent(c.value) + }).join("; ") + }) + }, function(err, response) { + rm('-f', 'www.zip'); + if(err) { + ionic.fail("Error uploading: " + err); + } + if(response.statusCode == 302) { + var redirectPath = parseUrl(response.headers.location); + project.app_id = redirectPath.pathname.match(/^\/projects\/(\w+)\/?$/)[1]; + ionic.saveConfig(project); + + if (callback) { + callback(); + }; + } + }); }); }); }; diff --git a/package.json b/package.json index 56cbfad473..d4c193df28 100644 --- a/package.json +++ b/package.json @@ -25,15 +25,14 @@ "author": "Max Lynch and Peter Collins ", "license": "MIT", "dependencies": { + "archiver": "0.5.1", "cordova": "~3.2.0", "form-data": "~0.1.0", - "fstream": "0.1.25", "ncp": "0.4.2", "optimist": "0.6.0", "prompt": "0.2.12", "request": "2.27.0", "shelljs": "0.2.6", - "tar": "0.1.19", "unzip": "0.1.9" } } From fb79e3a6b8ec17afac7505c159b2af4cc40ac723 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Fri, 17 Jan 2014 15:15:14 -0600 Subject: [PATCH 0048/1100] Handle errors in package --- lib/ionic/package.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 324d86d242..6746589084 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -200,8 +200,15 @@ IonicPackageTask.prototype.run = function(ionic) { }).join("; ") }) }, function(err, response) { + + response.setEncoding('utf8'); response.on("data", function(data) { - console.log(data); + var json = JSON.parse(data); + if(json.errors) { + for (var j = 0; j < json.errors.length; j++) { + process.stderr.write(json.errors[j]); + } + } }); if(err) { From 0cd395a25db282cb826748fb9782d5089a44e6ef Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Fri, 17 Jan 2014 16:18:02 -0600 Subject: [PATCH 0049/1100] Upload name with app --- lib/ionic/package.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 6746589084..7db6b4fbab 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -136,6 +136,7 @@ IonicPackageTask.prototype.run = function(ionic) { var form = new FormData(); form.append('email', project.email); + form.append('name', project.name); form.append('platform', platform); form.append('build_mode', mode); form.append('csrfmiddlewaretoken', jar.cookies[0].value); From 26b034211d575a8555db51706fdc7e64d176e7cc Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 3 Feb 2014 22:38:26 -0600 Subject: [PATCH 0050/1100] Updated build and emulate --- lib/ionic.js | 8 + lib/ionic/build.js | 5 +- lib/ionic/config.js | 46 ++++ lib/ionic/emulate.js | 49 ++++ lib/ionic/run.js | 5 +- lib/ionic/start.js | 5 +- lib/ionic/stats.js | 542 +++++++++++++++++++++++++++++++++++++++++++ lib/ionic/task.js | 15 +- package.json | 5 +- 9 files changed, 670 insertions(+), 10 deletions(-) create mode 100644 lib/ionic/config.js create mode 100644 lib/ionic/emulate.js create mode 100644 lib/ionic/stats.js diff --git a/lib/ionic.js b/lib/ionic.js index 9d1186486a..ce21f1c517 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -17,6 +17,7 @@ Copyright 2013 Drifty (http://drifty.com/) var IonicStartTask = require('./ionic/start.js').IonicStartTask; var IonicPlatformTask = require('./ionic/platform.js').IonicPlatformTask; var IonicRunTask = require('./ionic/run.js').IonicRunTask; +var IonicEmulateTask = require('./ionic/emulate.js').IonicEmulateTask; var IonicBuildTask = require('./ionic/build.js').IonicBuildTask; var argv = require('optimist').argv; @@ -28,6 +29,12 @@ var TASKS = [ usage: 'appname', task: IonicStartTask }, + { + title: 'emulate', + name: 'emulate', + usage: 'emulate', + task: IonicEmulateTask + }, { title: 'run', name: 'run', @@ -51,6 +58,7 @@ var TASKS = [ Ionic = function() {}; Ionic.prototype = { + IONIC_CONF: '.ionic', _tryBuildingTask: function() { if(argv._.length == 0) { return false; diff --git a/lib/ionic/build.js b/lib/ionic/build.js index 486ffc909a..1c5cee9200 100644 --- a/lib/ionic/build.js +++ b/lib/ionic/build.js @@ -5,7 +5,8 @@ var fs = require('fs'), path = require('path'), shelljs = require('shelljs/global'), unzip = require('unzip'), - IonicTask = require('./task').IonicTask; + IonicTask = require('./task').IonicTask, + IonicStats = require('./stats').IonicStats; var argv = require('optimist').argv; @@ -34,6 +35,8 @@ IonicBuildTask.prototype.run = function(ionic) { ionic.fail('No platforms specified, exiting.'); } + IonicStats.t('build', { 'platform': platforms.join(',') }); + for(var i = 0; i < platforms.length; i++) { platform = platforms[i]; console.log('Building platform', platform); diff --git a/lib/ionic/config.js b/lib/ionic/config.js new file mode 100644 index 0000000000..af16f1066f --- /dev/null +++ b/lib/ionic/config.js @@ -0,0 +1,46 @@ +var fs = require('fs'), + path = require('path'), + ionic = require('../ionic'); + +var home = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE; + +module.exports = { + CONF_DIR: '.ionic', + CONF_FILE: '.ionic/info.json', + getConfig: function() { + if(fs.existsSync(path.join(home, this.CONF_FILE))) { + var data = JSON.parse(fs.readFileSync(path.join(home, this.CONF_FILE))); + this.data = data; + } else { + this.create(); + } + return this; + }, + get: function(k) { + return this.data[k]; + }, + set: function(k, v) { + if(!this.data) { + this.create(); + } + this.data[k] = v; + this.save(); + }, + save: function() { + if(!this.data) { return; } + + var dirPath = path.join(home, this.CONF_DIR); + var p = path.join(home, this.CONF_FILE); + + if(!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath); + } + if(!fs.exists(p)) { + fs.writeFileSync(p, JSON.stringify(this.data)); + } + }, + create: function() { + this.data = {}; + this.save(); + } +}; diff --git a/lib/ionic/emulate.js b/lib/ionic/emulate.js new file mode 100644 index 0000000000..0c1934b12e --- /dev/null +++ b/lib/ionic/emulate.js @@ -0,0 +1,49 @@ +var fs = require('fs'), + os = require('os'), + request = require('request'), + ncp = require('ncp').ncp, + path = require('path'), + shelljs = require('shelljs/global'), + unzip = require('unzip'), + IonicTask = require('./task').IonicTask, + IonicStats = require('./stats').IonicStats; + +var argv = require('optimist').argv; + +var IonicEmulateTask = function() { +} + +IonicEmulateTask.HELP_LINE = 'Emulate an ionic project on a simulator or emulator.'; + +IonicEmulateTask.prototype = new IonicTask(); + +IonicEmulateTask.prototype._printUsage = function() { + process.stderr.write('\nUsage: ionic emulate [platform]\n'); +} + +IonicEmulateTask.prototype.run = function(ionic) { + var patform; + + if(argv._.length < 2) { + IonicEmulateTask.prototype._printUsage(); + ionic.fail('No platforms specified, exiting.'); + } + + var platforms = argv._.slice(1); + + if(platforms.length < 1) { + ionic.fail('No platforms specified, exiting.'); + } + + IonicStats.t('emulate', { 'platform': platforms.join(',') }); + + for(var i = 0; i < platforms.length; i++) { + platform = platforms[i]; + console.log('Emulating app on platform', platform); + if(exec("cordova emulate " + platform).code !== 0) { + ionic.fail('Unable to emulate app on platform ' + platform + '. Please see console for more info.'); + } + } +}; + +exports.IonicEmulateTask = IonicEmulateTask; diff --git a/lib/ionic/run.js b/lib/ionic/run.js index 6010728608..cb47f6a8f0 100644 --- a/lib/ionic/run.js +++ b/lib/ionic/run.js @@ -5,7 +5,8 @@ var fs = require('fs'), path = require('path'), shelljs = require('shelljs/global'), unzip = require('unzip'), - IonicTask = require('./task').IonicTask; + IonicTask = require('./task').IonicTask, + IonicStats = require('./stats').IonicStats; var argv = require('optimist').argv; @@ -34,6 +35,8 @@ IonicRunTask.prototype.run = function(ionic) { ionic.fail('No platforms specified, exiting.'); } + IonicStats.t('run', { 'platform': platforms.join(',') }); + for(var i = 0; i < platforms.length; i++) { platform = platforms[i]; console.log('Running app on platform', platform); diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 9079838880..8025e79e0a 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -5,7 +5,8 @@ var fs = require('fs'), path = require('path'), shelljs = require('shelljs/global'), unzip = require('unzip'), - IonicTask = require('./task').IonicTask; + IonicTask = require('./task').IonicTask, + IonicStats = require('./stats').IonicStats; var argv = require('optimist').argv; @@ -102,6 +103,8 @@ IonicStartTask.prototype._finish = function() { !exec('cordova plugin add org.apache.cordova.statusbar')) { process.stderr.write('Unable to install one or more cordova plugins.\n'); } + + IonicStats.t('start', {}); }; IonicStartTask.prototype._writeTemplateFolder = function() { diff --git a/lib/ionic/stats.js b/lib/ionic/stats.js new file mode 100644 index 0000000000..7dc7d5948c --- /dev/null +++ b/lib/ionic/stats.js @@ -0,0 +1,542 @@ +var MixpanelAPI, crypto, Buffer, http, querystring, util, path, fs, ionic, os, IonicConfig; +var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + __slice = Array.prototype.slice; + +path = require('path'), +http = require('http'); +querystring = require('querystring'); +crypto = require('crypto'); +Buffer = require('buffer').Buffer, +util = require('util'); +es = require('event-stream'); +fs = require('fs'); +os = require('os'); +ionic = require('../ionic'); +IonicConfig = require('./config'); + +/* + Heavily inspired by the original js library copyright Mixpanel, Inc. + (http://mixpanel.com/) + + Copyright (c) 2012 Carl Sverre + + Released under the MIT license. +*/ + +var create_client = function(token, config) { + var metrics = {}; + + if(!token) { + throw new Error("The Mixpanel Client needs a Mixpanel token: `init(token)`"); + } + + metrics.config = { + test: false, + debug: false, + verbose: false + }; + + metrics.token = token; + + /** + send_request(data) + --- + this function sends an async GET request to mixpanel + + data:object the data to send in the request + callback:function(err:Error) callback is called when the request is + finished or an error occurs + */ + metrics.send_request = function(endpoint, data, callback) { + callback = callback || function() {}; + var event_data = new Buffer(JSON.stringify(data)); + var request_data = { + 'data': event_data.toString('base64'), + 'ip': 0, + 'verbose': metrics.config.verbose ? 1 : 0 + }; + + if (endpoint === '/import') { + var key = metrics.config.key; + if (!key) { + throw new Error("The Mixpanel Client needs a Mixpanel api key when importing old events: `init(token, { key: ... })`"); + } + request_data.api_key = key; + } + + var request_options = { + host: 'api.mixpanel.com', + port: 80, + headers: {} + }; + + if (metrics.config.test) { request_data.test = 1; } + + var query = querystring.stringify(request_data); + + request_options.path = [endpoint,"?",query].join(""); + + http.get(request_options, function(res) { + var data = ""; + res.on('data', function(chunk) { + data += chunk; + }); + + res.on('end', function() { + var e; + if(metrics.config.verbose) { + try { + var result = JSON.parse(data); + if(result.status != 1) { + e = new Error("Mixpanel Server Error: " + result.error); + } + } + catch(ex) { + e = new Error("Could not parse response from Mixpanel"); + } + } + else { + e = (data !== '1') ? new Error("Mixpanel Server Error: " + data) : undefined; + } + + callback(e); + }); + }).on('error', function(e) { + if(metrics.config.debug) { + console.log("Got Error: " + e.message); + } + callback(e); + }); + }; + + /** + track(event, properties, callback) + --- + this function sends an event to mixpanel. + + event:string the event name + properties:object additional event properties to send + callback:function(err:Error) callback is called when the request is + finished or an error occurs + */ + metrics.track = function(event, properties, callback) { + if (typeof(properties) === 'function' || !properties) { + callback = properties; + properties = {}; + } + + // if properties.time exists, use import endpoint + var endpoint = (typeof(properties.time) === 'number') ? '/import' : '/track'; + + properties.token = metrics.token; + properties.mp_lib = "node"; + + var data = { + 'event' : event, + 'properties' : properties + }; + + if (metrics.config.debug) { + console.log("Sending the following event to Mixpanel:"); + console.log(data); + } + + metrics.send_request(endpoint, data, callback); + }; + + /** + import(event, properties, callback) + --- + This function sends an event to mixpanel using the import + endpoint. The time argument should be either a Date or Number, + and should signify the time the event occurred. + + It is highly recommended that you specify the distinct_id + property for each event you import, otherwise the events will be + tied to the IP address of the sending machine. + + For more information look at: + https://mixpanel.com/docs/api-documentation/importing-events-older-than-31-days + + event:string the event name + time:date|number the time of the event + properties:object additional event properties to send + callback:function(err:Error) callback is called when the request is + finished or an error occurs + */ + metrics.import = function(event, time, properties, callback) { + if (typeof(properties) === 'function' || !properties) { + callback = properties; + properties = {}; + } + + if (time === void 0) { + throw new Error("The import method requires you to specify the time of the event"); + } else if (Object.prototype.toString.call(time) === '[object Date]') { + time = Math.floor(time.getTime() / 1000); + } + + properties.time = time; + + metrics.track(event, properties, callback); + }; + + /** + alias(distinct_id, alias) + --- + This function creates an alias for distinct_id + + For more information look at: + https://mixpanel.com/docs/integration-libraries/using-mixpanel-alias + + distinct_id:string the current identifier + alias:string the future alias + */ + metrics.alias = function(distinct_id, alias, callback) { + var properties = { + distinct_id: distinct_id, + alias: alias + }; + + metrics.track('$create_alias', properties, callback); + }; + + metrics.people = { + /** people.set_once(distinct_id, prop, to, callback) + --- + The same as people.set but in the words of mixpanel: + mixpanel.people.set_once + + " This method allows you to set a user attribute, only if + it is not currently set. It can be called multiple times + safely, so is perfect for storing things like the first date + you saw a user, or the referrer that brought them to your + website for the first time. " + + */ + set_once: function(distinct_id, prop, to, callback) { + var $set = {}, data = {}; + + if (typeof(prop) === 'object') { + callback = to; + $set = prop; + } else { + $set[prop] = to; + } + + this._set(distinct_id, $set, callback, { set_once: true }); + }, + + /** + people.set(distinct_id, prop, to, callback) + --- + set properties on an user record in engage + + usage: + + mixpanel.people.set('bob', 'gender', 'm'); + + mixpanel.people.set('joe', { + 'company': 'acme', + 'plan': 'premium' + }); + */ + set: function(distinct_id, prop, to, callback) { + var $set = {}, data = {}; + + if (typeof(prop) === 'object') { + callback = to; + $set = prop; + } else { + $set[prop] = to; + } + + this._set(distinct_id, $set, callback); + }, + + // used internally by set and set_once + _set: function(distinct_id, $set, callback, options) { + var set_key = (options && options.set_once) ? "$set_once" : "$set"; + + var data = { + '$token': metrics.token, + '$distinct_id': distinct_id + }; + data[set_key] = $set; + + if ('ip' in $set) { + data.$ip = $set.ip; + delete $set.ip; + } + + if ($set.$ignore_time) { + data.$ignore_time = $set.$ignore_time; + delete $set.$ignore_time; + } + + if(metrics.config.debug) { + console.log("Sending the following data to Mixpanel (Engage):"); + console.log(data); + } + + metrics.send_request('/engage', data, callback); + }, + + /** + people.increment(distinct_id, prop, to, callback) + --- + increment/decrement properties on an user record in engage + + usage: + + mixpanel.people.increment('bob', 'page_views', 1); + + // or, for convenience, if you're just incrementing a counter by 1, you can + // simply do + mixpanel.people.increment('bob', 'page_views'); + + // to decrement a counter, pass a negative number + mixpanel.people.increment('bob', 'credits_left', -1); + + // like mixpanel.people.set(), you can increment multiple properties at once: + mixpanel.people.increment('bob', { + counter1: 1, + counter2: 3, + counter3: -2 + }); + */ + increment: function(distinct_id, prop, by, callback) { + var $add = {}; + + if (typeof(prop) === 'object') { + callback = by; + Object.keys(prop).forEach(function(key) { + var val = prop[key]; + + if (isNaN(parseFloat(val))) { + if (metrics.config.debug) { + console.error("Invalid increment value passed to mixpanel.people.increment - must be a number"); + console.error("Passed " + key + ":" + val); + } + return; + } else { + $add[key] = val; + } + }); + } else { + if (!by) { by = 1; } + $add[prop] = by; + } + + var data = { + '$add': $add, + '$token': metrics.token, + '$distinct_id': distinct_id + }; + + if(metrics.config.debug) { + console.log("Sending the following data to Mixpanel (Engage):"); + console.log(data); + } + + metrics.send_request('/engage', data, callback); + }, + + /** + people.track_charge(distinct_id, amount, properties, callback) + --- + Record that you have charged the current user a certain + amount of money. + + usage: + + // charge a user $29.99 + mixpanel.people.track_charge('bob', 29.99); + + // charge a user $19 on the 1st of february + mixpanel.people.track_charge('bob', 19, { '$time': new Date('feb 1 2012') }); + */ + track_charge: function(distinct_id, amount, properties, callback) { + var $append = {}; + + if (!properties) { properties = {}; } + + if (typeof(amount) !== 'number') { + amount = parseFloat(amount); + if (isNaN(amount)) { + console.error("Invalid value passed to mixpanel.people.track_charge - must be a number"); + return; + } + } + + properties.$amount = amount; + + if (properties.hasOwnProperty('$time')) { + var time = properties.$time; + if (Object.prototype.toString.call(time) === '[object Date]') { + properties.$time = time.toISOString(); + } + } + + var data = { + '$append': { '$transactions': properties }, + '$token': metrics.token, + '$distinct_id': distinct_id + }; + + if(metrics.config.debug) { + console.log("Sending the following data to Mixpanel (Engage):"); + console.log(data); + } + + metrics.send_request('/engage', data, callback); + }, + + /** + people.clear_charges(distinct_id, callback) + --- + Clear all the current user's transactions. + + usage: + + mixpanel.people.clear_charges('bob'); + */ + clear_charges: function(distinct_id, callback) { + var data = { + '$set': { '$transactions': [] }, + '$token': metrics.token, + '$distinct_id': distinct_id + }; + + if(metrics.config.debug) { + console.log("Clearing this user's charges:", distinct_id); + } + + metrics.send_request('/engage', data, callback); + }, + + /** + people.delete_user(distinct_id, callback) + --- + delete an user record in engage + + usage: + + mixpanel.people.delete_user('bob'); + */ + delete_user: function(distinct_id, callback) { + var data = { + '$delete': distinct_id, + '$token': metrics.token, + '$distinct_id': distinct_id + }; + + if(metrics.config.debug) { + console.log("Deleting the user from engage:", distinct_id); + } + + metrics.send_request('/engage', data, callback); + }, + + /** + people.unset(distinct_id, prop, callback) + --- + delete a property on an user record in engage + + usage: + + mixpanel.people.unset('bob', 'page_views'); + + mixpanel.people.unset('bob', ['page_views', 'last_login']); + */ + unset: function(distinct_id, prop, callback) { + var $unset = []; + + if (util.isArray(prop)) { + $unset = prop; + } else if (typeof(prop) === 'string') { + $unset = [prop]; + } else { + if (metrics.config.debug) { + console.error("Invalid argument passed to mixpanel.people.unset - must be a string or array"); + console.error("Passed: " + prop); + } + return; + } + + data = { + '$unset': $unset, + '$token': metrics.token, + '$distinct_id': distinct_id + }; + + if(metrics.config.debug) { + console.log("Sending the following data to Mixpanel (Engage):"); + console.log(data); + } + + metrics.send_request('/engage', data, callback); + } + }; + + /** + set_config(config) + --- + Modifies the mixpanel config + + config:object an object with properties to override in the + mixpanel client config + */ + metrics.set_config = function(config) { + for (var c in config) { + if (config.hasOwnProperty(c)) { + metrics.config[c] = config[c]; + } + } + }; + + if (config) { + metrics.set_config(config); + } + + return metrics; +}; + +// module exporting +/* +module.exports = { + Client: function(token) { + console.warn("The function `Client(token)` is deprecated. It is now called `init(token)`."); + return create_client(token); + }, + init: create_client +}; +*/ + +var mixpanel = create_client('69f7271aa8f3d43f2e1b6baf698159b7'); + +var ionicConfig = IonicConfig.getConfig(); + + +exports.IonicStats = { + t: function(e, d) { + var unique_id = ionicConfig.get('ank'); + if(!unique_id) { + this.createdId(); + unique_id = ionicConfig.get('ank'); + } + d.distinct_id = unique_id; + mixpanel.track(e, d, function(err, data) { + }); + }, + createId: function() { + var d = new Date().getTime(); + var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = (d + Math.random()*16)%16 | 0; + d = Math.floor(d/16); + return (c=='x' ? r : (r&0x7|0x8)).toString(16); + }); + + ionicConfig.set('ank', uuid); + } +} diff --git a/lib/ionic/task.js b/lib/ionic/task.js index 20b74b4c1f..6a8dcf9d3c 100644 --- a/lib/ionic/task.js +++ b/lib/ionic/task.js @@ -5,17 +5,22 @@ var IonicTask = function() { IonicTask.prototype = { // Prompt the user for a response - ask: function(question) { + ask: function(question, cb) { var response; process.stdout.write(question + ' '); + process.stdin.resume(); - response = fs.readSync(process.stdin.fd, 100, 0, "utf8"); - process.stdin.pause(); - return response[0].trim(); + process.stdin.setEncoding('utf8'); + var util = require('util'); + + process.stdin.on('data', function (text) { + cb(util.inspect(text)); + process.stdin.pause(); + }); }, run: function(ionic) { } }; -exports.IonicTask = IonicTask; \ No newline at end of file +exports.IonicTask = IonicTask; diff --git a/package.json b/package.json index 4cada292c0..c1de740772 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "0.9.10", + "version": "0.9.11", "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -30,6 +30,7 @@ "shelljs": "0.2.6", "unzip": "0.1.9", "ncp": "0.4.2", - "optimist": "0.6.0" + "optimist": "0.6.0", + "event-stream": "3.0.x" } } From 83b36d900e37f0218128f8ed6b0624dd785d33a0 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 3 Feb 2014 22:47:36 -0600 Subject: [PATCH 0051/1100] Platform --- lib/ionic/platform.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/ionic/platform.js b/lib/ionic/platform.js index 9993cb3ac1..873d7736a4 100644 --- a/lib/ionic/platform.js +++ b/lib/ionic/platform.js @@ -5,7 +5,8 @@ var fs = require('fs'), path = require('path'), shelljs = require('shelljs/global'), unzip = require('unzip'), - IonicTask = require('./task').IonicTask; + IonicTask = require('./task').IonicTask, + IonicStats = require('./stats').IonicStats; var argv = require('optimist').argv; @@ -35,6 +36,8 @@ IonicPlatformTask.prototype.run = function(ionic) { ionic.fail('No platforms specified, exiting.'); } + IonicStats.t('platform', { 'platform': platforms.join(',') }); + for(var i = 0; i < platforms.length; i++) { platform = platforms[i]; console.log('Adding platform', platform); From bd5546d3e66c55f1e15c0e6033c3800105d51af5 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 3 Feb 2014 22:48:00 -0600 Subject: [PATCH 0052/1100] version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c1de740772..a46d586acc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "0.9.11", + "version": "0.9.12", "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 52f84c55e39a0239e2bfe258fc893b7ce3edb048 Mon Sep 17 00:00:00 2001 From: Andy Joslin Date: Tue, 4 Feb 2014 08:56:53 -0500 Subject: [PATCH 0053/1100] fix(stats): typo s/createdId/createId --- lib/ionic/stats.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/stats.js b/lib/ionic/stats.js index 7dc7d5948c..bddc6bba87 100644 --- a/lib/ionic/stats.js +++ b/lib/ionic/stats.js @@ -522,7 +522,7 @@ exports.IonicStats = { t: function(e, d) { var unique_id = ionicConfig.get('ank'); if(!unique_id) { - this.createdId(); + this.createId(); unique_id = ionicConfig.get('ank'); } d.distinct_id = unique_id; From 421a035ff09579a96e6d359d6eb468e7f511644e Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Tue, 4 Feb 2014 08:13:10 -0600 Subject: [PATCH 0054/1100] Package --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a46d586acc..7e482cd965 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "0.9.12", + "version": "0.9.13", "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 285772fc82b19d2844179a87d7cdc468b1277db0 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Tue, 4 Feb 2014 09:51:12 -0600 Subject: [PATCH 0055/1100] Readme info --- README.md | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 00cc6c4f0b..a6c22c3037 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,44 @@ -ionic-cli +Ionic-Cli ========= -The Ionic Framework command line utility +The Ionic Framework command line utility makes it easy to start, build, run, and emulate Ionic apps. In the future, it will also have support for our mobile development services and tools that make Ionic even more powerful. + +## Installing + +```bash +$ sudo npm install -g ionic +``` + +## Starting an Ionic App + +```bash +$ ionic start myApp +``` + +## Adding a platform target + +```bash +$ ionic platform ios android +``` + +## Building your app + +```bash +$ ionic build ios +``` + +## Emulating your app + +```bash +$ ionic emulate ios +``` + +## Running your app + +```bash +$ ionic run ios +``` + +Ionic uses Cordova underneath, so you can also substitute Cordova commands to prepare/build/emulate/run, or to add additional plugins. + +Note: we occasionally anonymous usage statistics to the Ionic team to make the tool better. From 7ea32c2acf483545c42206328f140a760714b4f9 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Tue, 4 Feb 2014 09:51:28 -0600 Subject: [PATCH 0056/1100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a6c22c3037..87439694e8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Ionic-Cli ========= -The Ionic Framework command line utility makes it easy to start, build, run, and emulate Ionic apps. In the future, it will also have support for our mobile development services and tools that make Ionic even more powerful. +The Ionic Framework command line utility makes it easy to start, build, run, and emulate [Ionic](http://ionicframework.com/) apps. In the future, it will also have support for our mobile development services and tools that make Ionic even more powerful. ## Installing From 78df86502d821d12c42498f6d4afc2bc41ff0abd Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Sat, 8 Feb 2014 09:25:26 -0600 Subject: [PATCH 0057/1100] Added some protection --- lib/ionic/config.js | 18 +++++++++++------- package.json | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/ionic/config.js b/lib/ionic/config.js index af16f1066f..2b9e1b6c67 100644 --- a/lib/ionic/config.js +++ b/lib/ionic/config.js @@ -29,14 +29,18 @@ module.exports = { save: function() { if(!this.data) { return; } - var dirPath = path.join(home, this.CONF_DIR); - var p = path.join(home, this.CONF_FILE); + try { + var dirPath = path.join(home, this.CONF_DIR); + var p = path.join(home, this.CONF_FILE); - if(!fs.existsSync(dirPath)) { - fs.mkdirSync(dirPath); - } - if(!fs.exists(p)) { - fs.writeFileSync(p, JSON.stringify(this.data)); + if(!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath); + } + if(!fs.exists(p)) { + fs.writeFileSync(p, JSON.stringify(this.data)); + } + } catch(e) { + console.error('Unable to save settings file:', e); } }, create: function() { diff --git a/package.json b/package.json index 7e482cd965..db428156d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "0.9.13", + "version": "0.9.14", "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From eb33cbf47ebe55fd8d39fee632af75cfb206a7cf Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Sat, 8 Feb 2014 10:17:51 -0600 Subject: [PATCH 0058/1100] Unix line endings From 59518ac11c1519376d305855629bc7bb2489092a Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Sat, 8 Feb 2014 10:18:35 -0600 Subject: [PATCH 0059/1100] Carriage return --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index db428156d9..621b96a50f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "0.9.14", + "version": "0.9.15", "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From e45c39b660100d71e522f2a194df70c46bf370c1 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Sat, 8 Feb 2014 10:22:54 -0600 Subject: [PATCH 0060/1100] Trying to force line ending fix --- bin/ionic | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/ionic b/bin/ionic index 0454b93e1f..171649cb21 100644 --- a/bin/ionic +++ b/bin/ionic @@ -1,5 +1,6 @@ #!/usr/bin/env node + 'use strict'; process.title = 'ionic'; From 73a66f697a37ef6988f7831a52c84c96696176ae Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Sat, 8 Feb 2014 10:42:47 -0600 Subject: [PATCH 0061/1100] Ban CRLF forever --- .gitattributes | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..83cb362591 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# FUCK CRLF +* text eol=lf From a44fd0fc4f62d05d9a860d974ab7619e0e0224b6 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Sat, 8 Feb 2014 10:44:04 -0600 Subject: [PATCH 0062/1100] vBump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 621b96a50f..7bd10d86f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "0.9.15", + "version": "0.9.16", "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From d306ae25d73fcf3648b2e4bb0e4ad50a8d95ceb9 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Mon, 10 Feb 2014 11:37:16 -0600 Subject: [PATCH 0063/1100] Whoops finish merge --- lib/ionic/build.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/ionic/build.js b/lib/ionic/build.js index 5c6eef5e90..04e871ed02 100644 --- a/lib/ionic/build.js +++ b/lib/ionic/build.js @@ -1,9 +1,6 @@ -<<<<<<< HEAD -var argv = require('optimist').argv, - IonicTask = require('./task').IonicTask; -======= var fs = require('fs'), os = require('os'), + argv = require('optimist').argv, request = require('request'), ncp = require('ncp').ncp, path = require('path'), @@ -11,7 +8,6 @@ var fs = require('fs'), unzip = require('unzip'), IonicTask = require('./task').IonicTask, IonicStats = require('./stats').IonicStats; ->>>>>>> master var IonicBuildTask = function() { } From 224a385e6892a70af5123bd24c6091662c85ef38 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Mon, 17 Feb 2014 11:41:51 -0600 Subject: [PATCH 0064/1100] Change to official package site --- lib/ionic.js | 4 +- lib/ionic/login.js | 244 +++++++++++------------ lib/ionic/package.js | 456 ++++++++++++++++++++++--------------------- lib/ionic/upload.js | 8 + 4 files changed, 363 insertions(+), 349 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index dffbaf246c..1107c92990 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -17,7 +17,7 @@ Copyright 2013 Drifty (http://drifty.com/) var IonicStartTask = require('./ionic/start').IonicStartTask, IonicPlatformTask = require('./ionic/platform').IonicPlatformTask, IonicRunTask = require('./ionic/run').IonicRunTask, - IonicEmulateTask = require('./ionic/emulate.js').IonicEmulateTask; + IonicEmulateTask = require('./ionic/emulate').IonicEmulateTask; IonicBuildTask = require('./ionic/build').IonicBuildTask, IonicLoginTask = require('./ionic/login').IonicLoginTask, IonicUploadTask = require('./ionic/upload').IonicUploadTask, @@ -82,7 +82,7 @@ var TASKS = [ Ionic = function() {}; Ionic.prototype = { - IONIC_DASH: 'http://virt/', + IONIC_DASH: 'http://apps.ionicframework.com/', IONIC_COOKIES: 'ionic.cookies', IONIC_CONFIG: 'ionic.config', IONIC_CONF: '.ionic', diff --git a/lib/ionic/login.js b/lib/ionic/login.js index cb40e3077d..c631b6c353 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -1,122 +1,122 @@ -var fs = require('fs'), - request = require('request'), - argv = require('optimist').argv, - prompt = require('prompt'), - IonicTask = require('./task').IonicTask; - -var IonicLoginTask = function() { -} - -IonicLoginTask.HELP_LINE = 'Login to Ionic Studio'; - -IonicLoginTask.prototype = new IonicTask(); - -IonicLoginTask.prototype.run = function(ionic, callback) { - var self = this; - var schema = [{ - name: 'email', - pattern: /^[A-z0-9!#$%&'*+\/=?^_{|}~-]+(?:\.[A-z0-9!#$%&'*+\/=?^_{|}~-]+)*@(?:[A-z0-9](?:[A-z0-9-]*[A-z0-9])?\.)+[A-z0-9](?:[A-z0-9-]*[A-z0-9])?$/, - message: 'Email for Ionic Studio login', - required: true - }, { - name: 'password', - hidden: true, - required: true - }]; - - var project = ionic.loadConfig(); - - // Grab the email for login - if(argv._.length >= 2 && schema[0].pattern.test(argv._[1])) { - this.email = argv._[1].toLowerCase(); - schema.shift(); - } - // Assume project email is login email if it exists - else if(project.email && project.email != '') { - this.email = project.email; - schema.shift(); - } - - prompt.override = argv; - - prompt.start(); - - prompt.get(schema, function (err, result) { - if(err) { - ionic.fail('Error logging in: ' + err); - } - - if(!self.email) { - self.email = result.email.toLowerCase(); - } - self.password = result.password; - - if(!project.email || project.email == '') { - project.email = self.email; - ionic.saveConfig(project); - } - - var jar = request.jar(); - request({ - url: ionic.IONIC_DASH+'login', - jar: jar - }, - function(err, response, body) { - if(err || jar.cookies.length == 0) { - ionic.fail('Error logging in: ' + err); - } - - request({ - method: 'POST', - url: ionic.IONIC_DASH+'login', - jar: jar, - form: { - username: self.email, - password: self.password, - csrfmiddlewaretoken: jar.cookies[0].value - } - }, - function (err, response, body) { - if(err) { - ionic.fail('Error logging in: ' + err); - } - // Should be a 304 redirect status code if correct - if(response.statusCode == 200) { - ionic.fail('Email or Password incorrect. Please visit '+ionic.IONIC_DASH+' for help. :)') - } - - var err = fs.writeFileSync(ionic.IONIC_COOKIES, JSON.stringify(jar, null, 2)); - if(err) { - ionic.fail('Error writing ' + ionic.IONIC_COOKIES + ': ' + err); - } - - console.log('Logged in! :)'); - - if(callback) { - callback(jar); - } - }); - }); - }); -}; - -IonicLoginTask.prototype.get = function(ionic, callback) { - var self = this; - - if(fs.existsSync(ionic.IONIC_COOKIES)) { - var jar = JSON.parse(fs.readFileSync(ionic.IONIC_COOKIES)); - if(jar.cookies && jar.cookies.length > 0) { - for(i in jar.cookies) { - var cookie = jar.cookies[i]; - if(cookie.name == "sessionid" && new Date(cookie.expires) > new Date()) { - callback(jar); - return; - } - } - } - } - - this.run(ionic, callback); -} - -exports.IonicLoginTask = IonicLoginTask; +var fs = require('fs'), + request = require('request'), + argv = require('optimist').argv, + prompt = require('prompt'), + IonicTask = require('./task').IonicTask; + +var IonicLoginTask = function() { +} + +IonicLoginTask.HELP_LINE = 'Login to Ionic Studio'; + +IonicLoginTask.prototype = new IonicTask(); + +IonicLoginTask.prototype.run = function(ionic, callback) { + var self = this; + var schema = [{ + name: 'email', + pattern: /^[A-z0-9!#$%&'*+\/=?^_{|}~-]+(?:\.[A-z0-9!#$%&'*+\/=?^_{|}~-]+)*@(?:[A-z0-9](?:[A-z0-9-]*[A-z0-9])?\.)+[A-z0-9](?:[A-z0-9-]*[A-z0-9])?$/, + message: 'Email for Ionic Studio login', + required: true + }, { + name: 'password', + hidden: true, + required: true + }]; + + var project = ionic.loadConfig(); + + // Grab the email for login + if(argv._.length >= 2 && schema[0].pattern.test(argv._[1])) { + this.email = argv._[1].toLowerCase(); + schema.shift(); + } + // Assume project email is login email if it exists + else if(project.email && project.email != '') { + this.email = project.email; + schema.shift(); + } + + prompt.override = argv; + + prompt.start(); + + prompt.get(schema, function (err, result) { + if(err) { + ionic.fail('Error logging in: ' + err); + } + + if(!self.email) { + self.email = result.email.toLowerCase(); + } + self.password = result.password; + + if(!project.email || project.email == '') { + project.email = self.email; + ionic.saveConfig(project); + } + + var jar = request.jar(); + request({ + url: ionic.IONIC_DASH+'login', + jar: jar + }, + function(err, response, body) { + if(err || jar.cookies.length == 0) { + ionic.fail('Error logging in: ' + err); + } + + request({ + method: 'POST', + url: ionic.IONIC_DASH+'login', + jar: jar, + form: { + username: self.email, + password: self.password, + csrfmiddlewaretoken: jar.cookies[0].value + } + }, + function (err, response, body) { + if(err) { + ionic.fail('Error logging in: ' + err); + } + // Should be a 304 redirect status code if correct + if(response.statusCode == 200) { + ionic.fail('Email or Password incorrect. Please visit '+ionic.IONIC_DASH+' for help. :)') + } + + var err = fs.writeFileSync(ionic.IONIC_COOKIES, JSON.stringify(jar, null, 2)); + if(err) { + ionic.fail('Error writing ' + ionic.IONIC_COOKIES + ': ' + err); + } + + console.log('Logged in! :)'); + + if(callback) { + callback(jar); + } + }); + }); + }); +}; + +IonicLoginTask.prototype.get = function(ionic, callback) { + var self = this; + + if(fs.existsSync(ionic.IONIC_COOKIES)) { + var jar = JSON.parse(fs.readFileSync(ionic.IONIC_COOKIES)); + if(jar.cookies && jar.cookies.length > 0) { + for(i in jar.cookies) { + var cookie = jar.cookies[i]; + if(cookie.name == "sessionid" && new Date(cookie.expires) > new Date()) { + callback(jar); + return; + } + } + } + } + + this.run(ionic, callback); +} + +exports.IonicLoginTask = IonicLoginTask; diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 7db6b4fbab..113dcb67ff 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -1,225 +1,231 @@ -var fs = require('fs'), - path = require('path'), - parseUrl = require('url').parse, - argv = require('optimist').argv, - prompt = require('prompt'), - FormData = require('form-data'), - IonicTask = require('./task').IonicTask, - IonicUploadTask = require('./upload').IonicUploadTask, - IonicLoginTask = require('./login').IonicLoginTask; - -var IonicPackageTask = function() { -} - -IonicPackageTask.HELP_LINE = 'Package an Ionic project for the given plaform.'; - -IonicPackageTask.prototype = new IonicTask(); - -IonicPackageTask.prototype._printUsage = function() { - process.stderr.write('\nUsage: ionic package mode(debug|release) platform [more platforms,...]\n'); -} - -IonicPackageTask.prototype.run = function(ionic) { - if(argv._.length < 3) { - IonicPackageTask.prototype._printUsage(); - ionic.fail('No platforms or build mode specified, exiting.'); - } - - var mode = argv._[1].toLowerCase(); - if(mode != 'debug' && mode != 'release') { - IonicPackageTask.prototype._printUsage(); - ionic.fail('Package build mode must be debug or release, exiting.'); - } - - var platforms = argv._.slice(2); - - if(platforms.length < 1) { - ionic.fail('No platforms specified, exiting.'); - } - - var upload = new IonicUploadTask(); - upload.run(ionic, function() { - - var login = new IonicLoginTask(); - login.get(ionic, function(jar) { - - var project = ionic.loadConfig(); - - for(var i = 0; i < platforms.length; i++) { - var platform = platforms[i]; - - prompt.override = argv; - prompt.start(); - var properties = {}; - - // Just prompt for some build properties - switch (platform) { - case 'android': - // Android debug doesn't require anything - if(mode == 'release') { - properties = { - android_keystore: { - description: 'Android Keystore File (.keystore)', - message: 'Relative path to your release keystore file (eg. release.keystore)', - required: true - }, - android_keystore_alias: { - description: 'Keystore Alias', - message: 'Alias of the Keystore', - required: true - }, - android_keystore_password: { - description: 'Keystore Password', - message: 'Password of the Keystore', - hidden: true, - required: true - }, - android_key_password: { - description: 'Key Password (optional)', - message: 'Password for Key (usually same as Keystore Password and if left blank will use it)', - hidden: true, - } - }; - } - - break; - case 'ios': - properties = { - ios_certificate: { - description: 'iOS Certificate File (.p12)', - message: 'Relative path to your certificate file (eg. cert.p12)', - required: true - }, - ios_certificate_password: { - description: 'Certificate Password', - message: 'Password of the Certificate', - hidden: true, - required: true - }, - ios_profile: { - description: 'iOS Mobile Provisioning Profile (.mobileprofile)', - message: 'Relative path to your Mobile Provisioning Profile (eg. my.mobileprofile)', - required: true - }, - }; - break; - default: - process.stderr.write('\nUnknown platform: "'+platform+'"\nSupported platforms currently are "ios" and "android"\n\n'); - continue; - } - - // Should we just not add this since it's optional? - // properties.package_name = { - // description: 'Package Name (eg. com.mycompany.app) optional', - // message: 'Package Name (eg. com.mycompany.app) optional', - // }; - - // Don't prompt for properties we already have in the config - for (var property in properties) { - if(project[property] && project[project] == '') { - delete properties[property]; - } - } - - prompt.get({properties: properties}, function (err, result) { - if(err) { - ionic.fail('Error packaging: ' + err); - } - - // Overwrite any empty properties with prompt responses - for (var property in properties) { - if(project[property] && project[project] == '') { - project[property] = result[property]; - } - } - ionic.saveConfig(project); - - var form = new FormData(); - form.append('email', project.email); - form.append('name', project.name); - form.append('platform', platform); - form.append('build_mode', mode); - form.append('csrfmiddlewaretoken', jar.cookies[0].value); - - // Add the platform specific project properties to the post - for (var property in project) { - if(property.indexOf(platform) == 0) { - form.append(property, project[project]); - } - } - - // Gimmie dem sweet sweet files - switch(platform) { - case 'android': - if(mode == 'release') { - var keystoreFilePath = path.resolve(project.android_keystore); - if(fs.existsSync(keystoreFilePath)) { - form.append('android_keystore_file', fs.createReadStream(keystoreFilePath), {filename: 'www.zip'}); - } else { - delete project.android_keystore; - ionic.saveConfig(project); - process.stderr.write('\nCan\'t find file: "'+keystoreFilePath+'"\nSkipping build..."\n\n'); - return; - } - } - break; - case 'ios': - var certificateFilePath = path.resolve(project.ios_certificate); - if(fs.existsSync(certificateFilePath)) { - form.append('ios_certificate_file', fs.createReadStream(certificateFilePath)); - } else { - delete project.ios_certificate; - ionic.saveConfig(project); - process.stderr.write('\nCan\'t find file: "'+certificateFilePath+'"\nSkipping build..."\n\n'); - return; - } - var profileFilePath = path.resolve(project.ios_profile); - if(fs.existsSync(profileFilePath)) { - form.append('ios_profile_file', fs.createReadStream(profileFilePath)); - } else { - delete project.ios_profile; - ionic.saveConfig(project); - process.stderr.write('\nCan\'t find file: "'+profileFilePath+'"\nSkipping build..."\n\n'); - return; - } - break; - default: - console.trace(); - process.stderr.write('\nUnknown platform: "'+platform+'"\nWe should never get here"\n\n'); - break; - } - - var url = ionic.IONIC_DASH+'export/'+project.app_id; - var params = parseUrl(url); - - form.submit({ - host: params.host, - path: params.path, - headers: form.getHeaders({ - cookie: jar.cookies.map(function (c) { - return c.name + "=" + encodeURIComponent(c.value) - }).join("; ") - }) - }, function(err, response) { - - response.setEncoding('utf8'); - response.on("data", function(data) { - var json = JSON.parse(data); - if(json.errors) { - for (var j = 0; j < json.errors.length; j++) { - process.stderr.write(json.errors[j]); - } - } - }); - - if(err) { - ionic.fail("Error packaging: " + err); - } - }); - }); - } - }); - }); -}; - -exports.IonicPackageTask = IonicPackageTask; +var fs = require('fs'), + path = require('path'), + parseUrl = require('url').parse, + argv = require('optimist').argv, + prompt = require('prompt'), + FormData = require('form-data'), + IonicTask = require('./task').IonicTask, + IonicUploadTask = require('./upload').IonicUploadTask, + IonicLoginTask = require('./login').IonicLoginTask; + +var IonicPackageTask = function() { +} + +IonicPackageTask.HELP_LINE = 'Package an Ionic project for the given plaform.'; + +IonicPackageTask.prototype = new IonicTask(); + +IonicPackageTask.prototype._printUsage = function() { + process.stderr.write('\nUsage: ionic package mode(debug|release) platform [more platforms,...]\n'); +} + +IonicPackageTask.prototype.run = function(ionic) { + if(argv._.length < 3) { + IonicPackageTask.prototype._printUsage(); + ionic.fail('No platforms or build mode specified, exiting.'); + } + + var mode = argv._[1].toLowerCase(); + if(mode != 'debug' && mode != 'release') { + IonicPackageTask.prototype._printUsage(); + ionic.fail('Package build mode must be debug or release, exiting.'); + } + + var platforms = argv._.slice(2); + + if(platforms.length < 1) { + ionic.fail('No platforms specified, exiting.'); + } + + var upload = new IonicUploadTask(); + upload.run(ionic, function() { + + var login = new IonicLoginTask(); + login.get(ionic, function(jar) { + + var project = ionic.loadConfig(); + + for(var i = 0; i < platforms.length; i++) { + var platform = platforms[i]; + + prompt.override = argv; + prompt.start(); + var properties = {}; + + // Just prompt for some build properties + switch (platform) { + case 'android': + // Android debug doesn't require anything + if(mode == 'release') { + properties = { + android_keystore: { + description: 'Android Keystore File (.keystore)', + message: 'Relative path to your release keystore file (eg. release.keystore)', + required: true + }, + android_keystore_alias: { + description: 'Keystore Alias', + message: 'Alias of the Keystore', + required: true + }, + android_keystore_password: { + description: 'Keystore Password', + message: 'Password of the Keystore', + hidden: true, + required: true + }, + android_key_password: { + description: 'Key Password (optional)', + message: 'Password for Key (usually same as Keystore Password and if left blank will use it)', + hidden: true, + } + }; + } + + break; + case 'ios': + properties = { + ios_certificate: { + description: 'iOS Certificate File (.p12)', + message: 'Relative path to your certificate file (eg. cert.p12)', + required: true + }, + ios_certificate_password: { + description: 'Certificate Password', + message: 'Password of the Certificate', + hidden: true, + required: true + }, + ios_profile: { + description: 'iOS Mobile Provisioning Profile (.mobileprovision)', + message: 'Relative path to your Mobile Provisioning Profile (eg. my.mobileprovision)', + required: true + }, + }; + break; + default: + process.stderr.write('\nUnknown platform: "'+platform+'"\nSupported platforms currently are "ios" and "android"\n\n'); + continue; + } + + // Should we just not add this since it's optional? + // properties.package_name = { + // description: 'Package Name (eg. com.mycompany.app) optional', + // message: 'Package Name (eg. com.mycompany.app) optional', + // }; + + // Don't prompt for properties we already have in the config + for (var property in properties) { + if(project[property]) { + delete properties[property]; + } + } + + prompt.get({properties: properties}, function (err, result) { + if(err) { + ionic.fail('Error packaging: ' + err); + } + + // Overwrite any empty properties with prompt responses + for (var property in properties) { + if(result[property] && !project[property]) { + project[property] = result[property]; + } + } + ionic.saveConfig(project); + + console.log('Packaging '+platform+' app...'); + + var form = new FormData(); + form.append('email', project.email); + form.append('name', project.name); + form.append('platform', platform); + form.append('build_mode', mode); + form.append('csrfmiddlewaretoken', jar.cookies[0].value); + + + // Add the platform specific project properties to the post + for (var property in project) { + // console.log(property); + // console.log(project[property]); + if(property.indexOf(platform) == 0) { + form.append(property, project[property]); + } + } + + // Gimmie dem sweet sweet files + switch(platform) { + case 'android': + if(mode == 'release') { + var keystoreFilePath = path.resolve(project.android_keystore); + if(fs.existsSync(keystoreFilePath)) { + form.append('android_keystore_file', fs.createReadStream(keystoreFilePath), {filename: 'www.zip'}); + } else { + delete project.android_keystore; + ionic.saveConfig(project); + process.stderr.write('\nCan\'t find file: "'+keystoreFilePath+'"\nSkipping build..."\n\n'); + return; + } + } + break; + case 'ios': + var certificateFilePath = path.resolve(project.ios_certificate); + if(fs.existsSync(certificateFilePath)) { + form.append('ios_certificate_file', fs.createReadStream(certificateFilePath)); + } else { + delete project.ios_certificate; + ionic.saveConfig(project); + process.stderr.write('\nCan\'t find file: "'+certificateFilePath+'"\nSkipping build..."\n\n'); + return; + } + var profileFilePath = path.resolve(project.ios_profile); + if(fs.existsSync(profileFilePath)) { + form.append('ios_profile_file', fs.createReadStream(profileFilePath)); + } else { + delete project.ios_profile; + ionic.saveConfig(project); + process.stderr.write('\nCan\'t find file: "'+profileFilePath+'"\nSkipping build..."\n\n'); + return; + } + break; + default: + console.trace(); + process.stderr.write('\nUnknown platform: "'+platform+'"\nWe should never get here"\n\n'); + break; + } + + var url = ionic.IONIC_DASH+'export/'+project.app_id; + var params = parseUrl(url); + + form.submit({ + host: params.host, + path: params.path, + headers: form.getHeaders({ + cookie: jar.cookies.map(function (c) { + return c.name + "=" + encodeURIComponent(c.value) + }).join("; ") + }) + }, function(err, response) { + + response.setEncoding('utf8'); + response.on("data", function(data) { + var json = JSON.parse(data); + if(json.errors) { + for (var j = 0; j < json.errors.length; j++) { + process.stderr.write(json.errors[j]); + } + } + }); + + if(err) { + ionic.fail("Error packaging: " + err); + } + console.log('Done'); + }); + }); + } + }); + }); +}; + +exports.IonicPackageTask = IonicPackageTask; diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index e65f01fe0f..31e963f8c7 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -20,6 +20,8 @@ IonicUploadTask.prototype.run = function(ionic, callback) { var login = new IonicLoginTask(); login.get(ionic, function(jar) { + console.log('Zipping...'); + var zip = fs.createWriteStream('www.zip'); var archive = archiver('zip'); @@ -36,6 +38,9 @@ IonicUploadTask.prototype.run = function(ionic, callback) { }); zip.on('close', function() { + console.log('Done'); + console.log('Uploading...'); + var form = new FormData(); form.append('csrfmiddlewaretoken', jar.cookies[0].value); form.append('app_file', fs.createReadStream(path.resolve('www.zip')), {filename: 'www.zip', contentType: 'application/zip'}); @@ -57,6 +62,9 @@ IonicUploadTask.prototype.run = function(ionic, callback) { if(err) { ionic.fail("Error uploading: " + err); } + + console.log('Done'); + if(response.statusCode == 302) { var redirectPath = parseUrl(response.headers.location); project.app_id = redirectPath.pathname.match(/^\/projects\/(\w+)\/?$/)[1]; From dd99e7c2abf1f6604c592df757292e9786581a77 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Mon, 17 Feb 2014 11:51:53 -0600 Subject: [PATCH 0065/1100] Add signup link --- lib/ionic/login.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/ionic/login.js b/lib/ionic/login.js index c631b6c353..21ee0e4ccf 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -37,6 +37,8 @@ IonicLoginTask.prototype.run = function(ionic, callback) { schema.shift(); } + console.log('Signup here! http://apps.ionicframework.com/signup'); + prompt.override = argv; prompt.start(); From 540ceb2072bf713677e94f1fcc996f3ffdd50a3e Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Mon, 17 Feb 2014 16:07:12 -0600 Subject: [PATCH 0066/1100] Merge config wip --- lib/ionic.js | 38 +----------------- lib/ionic/config.js | 94 +++++++++++++++++++++++++++++++------------- lib/ionic/login.js | 8 ++-- lib/ionic/package.js | 3 +- lib/ionic/start.js | 7 ++-- lib/ionic/stats.js | 5 +-- lib/ionic/upload.js | 3 +- 7 files changed, 81 insertions(+), 77 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 1107c92990..e5a80ae121 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -84,22 +84,7 @@ Ionic = function() {}; Ionic.prototype = { IONIC_DASH: 'http://apps.ionicframework.com/', IONIC_COOKIES: 'ionic.cookies', - IONIC_CONFIG: 'ionic.config', - IONIC_CONF: '.ionic', - IONIC_API: 'api/v1/', - CONFIG_DEFAULT: { - name: '', - email: '', - app_id: '', - package_name: '', - ios_certificate: '', - ios_certificate_password: '', - ios_profile: '', - android_keystore: '', - android_keystore_alias: '', - android_keystore_password: '', - android_key_password: '' - }, + IONIC_API: 'api/v1/', _tryBuildingTask: function() { if(argv._.length == 0) { return false; @@ -163,28 +148,7 @@ Ionic.prototype = { fail: function(msg) { process.stderr.write(msg + '\n'); process.exit(1); - }, - - loadConfig: function() { - if(!fs.existsSync(this.IONIC_CONFIG)) { - this.fail('Could not find ' + this.IONIC_CONFIG + '!'+ - ' Please run this command your root ionic project directory with that file.'); - } - return JSON.parse(fs.readFileSync(this.IONIC_CONFIG)); - }, - - saveConfig: function(project, targetPath) { - if(!project) { - console.trace(); - this.fail('This should never happen!'); - } - - var err = fs.writeFileSync((targetPath?targetPath+'/':'')+this.IONIC_CONFIG, JSON.stringify(project, null, 2)); - if(err) { - process.stderr.write('Error writing ' + (targetPath?targetPath+'/':'')+ ionic.IONIC_CONFIG + ': ' + err + '\n'); - } } - }; diff --git a/lib/ionic/config.js b/lib/ionic/config.js index 2b9e1b6c67..dafc63e9c7 100644 --- a/lib/ionic/config.js +++ b/lib/ionic/config.js @@ -5,46 +5,86 @@ var fs = require('fs'), var home = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE; module.exports = { - CONF_DIR: '.ionic', - CONF_FILE: '.ionic/info.json', - getConfig: function() { - if(fs.existsSync(path.join(home, this.CONF_FILE))) { - var data = JSON.parse(fs.readFileSync(path.join(home, this.CONF_FILE))); - this.data = data; + CONFIG_FILE: '.ionic/ionic.config', + PROJECT_FILE: 'ionic.project', + PROJECT_DEFAULT: { + name: '', + email: '', + app_id: '', + package_name: '', + ios_certificate: '', + ios_certificate_password: '', + ios_profile: '', + android_keystore: '', + android_keystore_alias: '', + android_keystore_password: '', + android_key_password: '' + }, + loadConfig: function() { + this.file = this.CONFIG_FILE; + if(fs.existsSync(path.join(home,this.file))) { + this.data = JSON.parse(fs.readFileSync(path.join(home, this.file))); } else { - this.create(); + this.data = {}; } return this; }, - get: function(k) { - return this.data[k]; - }, - set: function(k, v) { - if(!this.data) { - this.create(); + loadProject: function() { + this.file = this.PROJECT_FILE; + if(fs.existsSync(this.file)) { + this.data = JSON.parse(fs.readFileSync(this.file)) + } else { + console.error('Could not find ' + this.file + '!'+ + ' Please run this command in your root ionic project directory with that file.'); } - this.data[k] = v; - this.save(); + return this; }, - save: function() { - if(!this.data) { return; } - + createProject: function(name) { + this.file = this.PROJECT_FILE; + this.data = this.PROJECT_DEFAULT; + this.data.name = name; + return this; + }, + saveConfig: function() { + if(!this.data) { + return; + } try { - var dirPath = path.join(home, this.CONF_DIR); - var p = path.join(home, this.CONF_FILE); + var dirPath = path.join(home, path.dirname(this.CONFIG_FILE)); + var p = path.join(home, this.CONFIG_FILE); if(!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath); } - if(!fs.exists(p)) { - fs.writeFileSync(p, JSON.stringify(this.data)); - } + fs.writeFileSync(p, JSON.stringify(this.data, null, 2)); } catch(e) { console.error('Unable to save settings file:', e); } }, - create: function() { - this.data = {}; - this.save(); - } + saveProject: function(targetPath) { + if(!this.data) { + console.trace(); + console.error('This should never happen!'); + } + try { + fs.writeFileSync((targetPath?targetPath+'/':'')+this.PROJECT_FILE, JSON.stringify(this.data, null, 2)); + } catch(e) { + console.error('Unable to save settings file:', e); + } + }, + get: function(k) { + return this.data[k]; + }, + set: function(k, v) { + if(!this.data) { + this.data = {}; + } + this.data[k] = v; + + if(this.file == this.PROJECT_FILE) { + this.saveProject(); + } else { + this.saveConfig(); + } + } }; diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 21ee0e4ccf..245d996e9e 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -2,6 +2,7 @@ var fs = require('fs'), request = require('request'), argv = require('optimist').argv, prompt = require('prompt'), + IonicConfig = require('./config'), IonicTask = require('./task').IonicTask; var IonicLoginTask = function() { @@ -24,7 +25,7 @@ IonicLoginTask.prototype.run = function(ionic, callback) { required: true }]; - var project = ionic.loadConfig(); + var project = IonicConfig.loadProject(); // Grab the email for login if(argv._.length >= 2 && schema[0].pattern.test(argv._[1])) { @@ -53,9 +54,8 @@ IonicLoginTask.prototype.run = function(ionic, callback) { } self.password = result.password; - if(!project.email || project.email == '') { - project.email = self.email; - ionic.saveConfig(project); + if(!project.get('email')) { + project.set('email'); } var jar = request.jar(); diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 113dcb67ff..5a27ad71de 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -4,6 +4,7 @@ var fs = require('fs'), argv = require('optimist').argv, prompt = require('prompt'), FormData = require('form-data'), + IonicConfig = require('./config'), IonicTask = require('./task').IonicTask, IonicUploadTask = require('./upload').IonicUploadTask, IonicLoginTask = require('./login').IonicLoginTask; @@ -43,7 +44,7 @@ IonicPackageTask.prototype.run = function(ionic) { var login = new IonicLoginTask(); login.get(ionic, function(jar) { - var project = ionic.loadConfig(); + var project = IonicConfig.loadProject(); for(var i = 0; i < platforms.length; i++) { var platform = platforms[i]; diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 349cafebce..ac118768ec 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -6,6 +6,7 @@ var fs = require('fs'), shelljs = require('shelljs/global'), unzip = require('unzip'), argv = require('optimist').argv, + IonicConfig = require('./config'), IonicTask = require('./task').IonicTask; IonicStats = require('./stats').IonicStats; @@ -101,10 +102,8 @@ IonicStartTask.prototype._fetchAndWriteSeed = function() { IonicStartTask.prototype._writeConfig = function(ionic) { console.log('Writing '+this.targetPath+'/ionic.config'); - var project = ionic.CONFIG_DEFAULT; - project.name = this.appName; - - ionic.saveConfig(project, this.targetPath); + var project = IonicConfig.createProject(this.appName); + project.saveProject(this.targetPath); }; IonicStartTask.prototype._checkTargetPath = function() { diff --git a/lib/ionic/stats.js b/lib/ionic/stats.js index bddc6bba87..ed5313f429 100644 --- a/lib/ionic/stats.js +++ b/lib/ionic/stats.js @@ -1,4 +1,4 @@ -var MixpanelAPI, crypto, Buffer, http, querystring, util, path, fs, ionic, os, IonicConfig; +var MixpanelAPI, crypto, Buffer, http, querystring, util, path, fs, os, IonicConfig; var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __slice = Array.prototype.slice; @@ -11,7 +11,6 @@ util = require('util'); es = require('event-stream'); fs = require('fs'); os = require('os'); -ionic = require('../ionic'); IonicConfig = require('./config'); /* @@ -515,7 +514,7 @@ module.exports = { var mixpanel = create_client('69f7271aa8f3d43f2e1b6baf698159b7'); -var ionicConfig = IonicConfig.getConfig(); +var ionicConfig = IonicConfig.loadConfig(); exports.IonicStats = { diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index 31e963f8c7..c06eda379f 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -4,6 +4,7 @@ var fs = require('fs'), archiver = require('archiver'), argv = require('optimist').argv, FormData = require('form-data'), + IonicConfig = require('./config'), IonicTask = require('./task').IonicTask, IonicLoginTask = require('./login').IonicLoginTask; @@ -15,7 +16,7 @@ IonicUploadTask.HELP_LINE = 'Upload an Ionic project.'; IonicUploadTask.prototype = new IonicTask(); IonicUploadTask.prototype.run = function(ionic, callback) { - var project = ionic.loadConfig(); + var project = IonicConfig.loadProject(); var login = new IonicLoginTask(); login.get(ionic, function(jar) { From f91d53db31f4cdc717d9b909c34091e05bce0be2 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Mon, 17 Feb 2014 17:21:44 -0600 Subject: [PATCH 0067/1100] Just move them to two different files --- lib/ionic/config.js | 51 +++--------------------------------- lib/ionic/login.js | 10 ++++---- lib/ionic/package.js | 38 +++++++++++++-------------- lib/ionic/project.js | 61 ++++++++++++++++++++++++++++++++++++++++++++ lib/ionic/start.js | 7 ++--- lib/ionic/stats.js | 2 +- lib/ionic/upload.js | 10 ++++---- 7 files changed, 98 insertions(+), 81 deletions(-) create mode 100644 lib/ionic/project.js diff --git a/lib/ionic/config.js b/lib/ionic/config.js index dafc63e9c7..541240d073 100644 --- a/lib/ionic/config.js +++ b/lib/ionic/config.js @@ -6,21 +6,7 @@ var home = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE; module.exports = { CONFIG_FILE: '.ionic/ionic.config', - PROJECT_FILE: 'ionic.project', - PROJECT_DEFAULT: { - name: '', - email: '', - app_id: '', - package_name: '', - ios_certificate: '', - ios_certificate_password: '', - ios_profile: '', - android_keystore: '', - android_keystore_alias: '', - android_keystore_password: '', - android_key_password: '' - }, - loadConfig: function() { + load: function() { this.file = this.CONFIG_FILE; if(fs.existsSync(path.join(home,this.file))) { this.data = JSON.parse(fs.readFileSync(path.join(home, this.file))); @@ -29,23 +15,7 @@ module.exports = { } return this; }, - loadProject: function() { - this.file = this.PROJECT_FILE; - if(fs.existsSync(this.file)) { - this.data = JSON.parse(fs.readFileSync(this.file)) - } else { - console.error('Could not find ' + this.file + '!'+ - ' Please run this command in your root ionic project directory with that file.'); - } - return this; - }, - createProject: function(name) { - this.file = this.PROJECT_FILE; - this.data = this.PROJECT_DEFAULT; - this.data.name = name; - return this; - }, - saveConfig: function() { + save: function() { if(!this.data) { return; } @@ -61,17 +31,6 @@ module.exports = { console.error('Unable to save settings file:', e); } }, - saveProject: function(targetPath) { - if(!this.data) { - console.trace(); - console.error('This should never happen!'); - } - try { - fs.writeFileSync((targetPath?targetPath+'/':'')+this.PROJECT_FILE, JSON.stringify(this.data, null, 2)); - } catch(e) { - console.error('Unable to save settings file:', e); - } - }, get: function(k) { return this.data[k]; }, @@ -81,10 +40,6 @@ module.exports = { } this.data[k] = v; - if(this.file == this.PROJECT_FILE) { - this.saveProject(); - } else { - this.saveConfig(); - } + this.saveConfig(); } }; diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 245d996e9e..5567796ba4 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -2,7 +2,7 @@ var fs = require('fs'), request = require('request'), argv = require('optimist').argv, prompt = require('prompt'), - IonicConfig = require('./config'), + IonicProject = require('./project'), IonicTask = require('./task').IonicTask; var IonicLoginTask = function() { @@ -25,7 +25,7 @@ IonicLoginTask.prototype.run = function(ionic, callback) { required: true }]; - var project = IonicConfig.loadProject(); + var project = IonicProject.load(); // Grab the email for login if(argv._.length >= 2 && schema[0].pattern.test(argv._[1])) { @@ -33,12 +33,12 @@ IonicLoginTask.prototype.run = function(ionic, callback) { schema.shift(); } // Assume project email is login email if it exists - else if(project.email && project.email != '') { - this.email = project.email; + else if(project.get('email')) { + this.email = project.get('email'); schema.shift(); } - console.log('Signup here! http://apps.ionicframework.com/signup'); + console.log('Signup here! '+ionic.IONIC_DASH+'signup'); prompt.override = argv; diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 5a27ad71de..a8837fd976 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -4,7 +4,7 @@ var fs = require('fs'), argv = require('optimist').argv, prompt = require('prompt'), FormData = require('form-data'), - IonicConfig = require('./config'), + IonicProject = require('./project'), IonicTask = require('./task').IonicTask, IonicUploadTask = require('./upload').IonicUploadTask, IonicLoginTask = require('./login').IonicLoginTask; @@ -44,7 +44,7 @@ IonicPackageTask.prototype.run = function(ionic) { var login = new IonicLoginTask(); login.get(ionic, function(jar) { - var project = IonicConfig.loadProject(); + var project = IonicProject.load(); for(var i = 0; i < platforms.length; i++) { var platform = platforms[i]; @@ -117,7 +117,7 @@ IonicPackageTask.prototype.run = function(ionic) { // Don't prompt for properties we already have in the config for (var property in properties) { - if(project[property]) { + if(project.get(property)) { delete properties[property]; } } @@ -129,17 +129,17 @@ IonicPackageTask.prototype.run = function(ionic) { // Overwrite any empty properties with prompt responses for (var property in properties) { - if(result[property] && !project[property]) { - project[property] = result[property]; + if(result[property] && !project.get(property)) { + project.set(property, result[property]); } } - ionic.saveConfig(project); + project.save(); console.log('Packaging '+platform+' app...'); var form = new FormData(); - form.append('email', project.email); - form.append('name', project.name); + form.append('email', project.get('email')); + form.append('name', project.get('name')); form.append('platform', platform); form.append('build_mode', mode); form.append('csrfmiddlewaretoken', jar.cookies[0].value); @@ -150,7 +150,7 @@ IonicPackageTask.prototype.run = function(ionic) { // console.log(property); // console.log(project[property]); if(property.indexOf(platform) == 0) { - form.append(property, project[property]); + form.append(property, project.get(property)); } } @@ -158,33 +158,33 @@ IonicPackageTask.prototype.run = function(ionic) { switch(platform) { case 'android': if(mode == 'release') { - var keystoreFilePath = path.resolve(project.android_keystore); + var keystoreFilePath = path.resolve(project.get('android_keystore')); if(fs.existsSync(keystoreFilePath)) { form.append('android_keystore_file', fs.createReadStream(keystoreFilePath), {filename: 'www.zip'}); } else { - delete project.android_keystore; - ionic.saveConfig(project); + project.remove('android_keystore'); + project.save(); process.stderr.write('\nCan\'t find file: "'+keystoreFilePath+'"\nSkipping build..."\n\n'); return; } } break; case 'ios': - var certificateFilePath = path.resolve(project.ios_certificate); + var certificateFilePath = path.resolve(project.get('ios_certificate')); if(fs.existsSync(certificateFilePath)) { form.append('ios_certificate_file', fs.createReadStream(certificateFilePath)); } else { - delete project.ios_certificate; - ionic.saveConfig(project); + project.remove('ios_certificate'); + project.save(); process.stderr.write('\nCan\'t find file: "'+certificateFilePath+'"\nSkipping build..."\n\n'); return; } - var profileFilePath = path.resolve(project.ios_profile); + var profileFilePath = path.resolve(project.get('ios_profile')); if(fs.existsSync(profileFilePath)) { form.append('ios_profile_file', fs.createReadStream(profileFilePath)); } else { - delete project.ios_profile; - ionic.saveConfig(project); + project.remove('ios_profile'); + project.save(); process.stderr.write('\nCan\'t find file: "'+profileFilePath+'"\nSkipping build..."\n\n'); return; } @@ -195,7 +195,7 @@ IonicPackageTask.prototype.run = function(ionic) { break; } - var url = ionic.IONIC_DASH+'export/'+project.app_id; + var url = ionic.IONIC_DASH+'export/'+project.get('app_id'); var params = parseUrl(url); form.submit({ diff --git a/lib/ionic/project.js b/lib/ionic/project.js new file mode 100644 index 0000000000..e04027d52e --- /dev/null +++ b/lib/ionic/project.js @@ -0,0 +1,61 @@ +var fs = require('fs'), + path = require('path'), + ionic = require('../ionic'); + +module.exports = { + PROJECT_FILE: 'ionic.project', + PROJECT_DEFAULT: { + name: '', + email: '', + app_id: '', + package_name: '', + ios_certificate: '', + ios_certificate_password: '', + ios_profile: '', + android_keystore: '', + android_keystore_alias: '', + android_keystore_password: '', + android_key_password: '' + }, + load: function() { + this.file = this.PROJECT_FILE; + if(fs.existsSync(this.file)) { + this.data = JSON.parse(fs.readFileSync(this.file)) + } else { + console.error('Could not find ' + this.file + '!'+ + ' Please run this command in your root ionic project directory with that file.'); + } + return this; + }, + create: function(name) { + this.file = this.PROJECT_FILE; + this.data = this.PROJECT_DEFAULT; + return this; + }, + save: function(targetPath) { + if(!this.data) { + console.trace(); + console.error('This should never happen!'); + } + try { + fs.writeFileSync((targetPath?targetPath+'/':'')+this.PROJECT_FILE, JSON.stringify(this.data, null, 2)); + } catch(e) { + console.error('Unable to save settings file:', e); + } + }, + get: function(k) { + return this.data[k]; + }, + set: function(k, v) { + if(!this.data) { + this.data = PROJECT_DEFAULT; + } + this.data[k] = v; + }, + remove: function(k) { + if(!this.data) { + this.data = PROJECT_DEFAULT; + } + this.data[k] = ''; + } +}; diff --git a/lib/ionic/start.js b/lib/ionic/start.js index ac118768ec..ed6667d65a 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -6,7 +6,7 @@ var fs = require('fs'), shelljs = require('shelljs/global'), unzip = require('unzip'), argv = require('optimist').argv, - IonicConfig = require('./config'), + IonicProject = require('./project'), IonicTask = require('./task').IonicTask; IonicStats = require('./stats').IonicStats; @@ -102,8 +102,9 @@ IonicStartTask.prototype._fetchAndWriteSeed = function() { IonicStartTask.prototype._writeConfig = function(ionic) { console.log('Writing '+this.targetPath+'/ionic.config'); - var project = IonicConfig.createProject(this.appName); - project.saveProject(this.targetPath); + var project = IonicProject.create(); + project.set('name', this.appName); + project.save(this.targetPath); }; IonicStartTask.prototype._checkTargetPath = function() { diff --git a/lib/ionic/stats.js b/lib/ionic/stats.js index ed5313f429..b4823afbee 100644 --- a/lib/ionic/stats.js +++ b/lib/ionic/stats.js @@ -514,7 +514,7 @@ module.exports = { var mixpanel = create_client('69f7271aa8f3d43f2e1b6baf698159b7'); -var ionicConfig = IonicConfig.loadConfig(); +var ionicConfig = IonicConfig.load(); exports.IonicStats = { diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index c06eda379f..05148f3a79 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -4,7 +4,7 @@ var fs = require('fs'), archiver = require('archiver'), argv = require('optimist').argv, FormData = require('form-data'), - IonicConfig = require('./config'), + IonicProject = require('./project'), IonicTask = require('./task').IonicTask, IonicLoginTask = require('./login').IonicLoginTask; @@ -16,7 +16,7 @@ IonicUploadTask.HELP_LINE = 'Upload an Ionic project.'; IonicUploadTask.prototype = new IonicTask(); IonicUploadTask.prototype.run = function(ionic, callback) { - var project = IonicConfig.loadProject(); + var project = IonicProject.load(); var login = new IonicLoginTask(); login.get(ionic, function(jar) { @@ -47,7 +47,7 @@ IonicUploadTask.prototype.run = function(ionic, callback) { form.append('app_file', fs.createReadStream(path.resolve('www.zip')), {filename: 'www.zip', contentType: 'application/zip'}); // form.append('app_file', zip.toBuffer(), {filename: 'www.zip', contentType: 'application/zip'}); - var url = ionic.IONIC_DASH+'projects/'+(project.app_id?project.app_id:''); + var url = ionic.IONIC_DASH+'projects/'+project.get('app_id'); var params = parseUrl(url); form.submit({ @@ -68,8 +68,8 @@ IonicUploadTask.prototype.run = function(ionic, callback) { if(response.statusCode == 302) { var redirectPath = parseUrl(response.headers.location); - project.app_id = redirectPath.pathname.match(/^\/projects\/(\w+)\/?$/)[1]; - ionic.saveConfig(project); + project.set('app_id', redirectPath.pathname.match(/^\/projects\/(\w+)\/?$/)[1]); + project.save(); if (callback) { callback(); From 751497cb69996acbff9a324c92e829740cdf9785 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Fri, 21 Feb 2014 16:39:02 -0600 Subject: [PATCH 0068/1100] Add name for Timmy --- lib/ionic.js | 2 +- lib/ionic/upload.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/ionic.js b/lib/ionic.js index e5a80ae121..5aa09ba996 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -82,7 +82,7 @@ var TASKS = [ Ionic = function() {}; Ionic.prototype = { - IONIC_DASH: 'http://apps.ionicframework.com/', + IONIC_DASH: 'http://virt/', IONIC_COOKIES: 'ionic.cookies', IONIC_API: 'api/v1/', _tryBuildingTask: function() { diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index 05148f3a79..0c292ffa9f 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -43,6 +43,7 @@ IonicUploadTask.prototype.run = function(ionic, callback) { console.log('Uploading...'); var form = new FormData(); + form.append('name', project.get('name')); form.append('csrfmiddlewaretoken', jar.cookies[0].value); form.append('app_file', fs.createReadStream(path.resolve('www.zip')), {filename: 'www.zip', contentType: 'application/zip'}); // form.append('app_file', zip.toBuffer(), {filename: 'www.zip', contentType: 'application/zip'}); @@ -68,6 +69,7 @@ IonicUploadTask.prototype.run = function(ionic, callback) { if(response.statusCode == 302) { var redirectPath = parseUrl(response.headers.location); + console.log(redirectPath); project.set('app_id', redirectPath.pathname.match(/^\/projects\/(\w+)\/?$/)[1]); project.save(); From 69148a72ef3a595f95b4ef3dd509ae90e1bc61cf Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Fri, 21 Feb 2014 16:42:36 -0600 Subject: [PATCH 0069/1100] Fail when it can't find the project file --- lib/ionic/project.js | 6 +++--- lib/ionic/upload.js | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/ionic/project.js b/lib/ionic/project.js index e04027d52e..e437226a73 100644 --- a/lib/ionic/project.js +++ b/lib/ionic/project.js @@ -18,11 +18,11 @@ module.exports = { android_key_password: '' }, load: function() { - this.file = this.PROJECT_FILE; - if(fs.existsSync(this.file)) { + this.file = this.PROJECT_FILE; + if(fs.existsSync(this.file)) { this.data = JSON.parse(fs.readFileSync(this.file)) } else { - console.error('Could not find ' + this.file + '!'+ + new Ionic().fail('Could not find ' + this.file + '!'+ ' Please run this command in your root ionic project directory with that file.'); } return this; diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index 0c292ffa9f..196d98992d 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -69,7 +69,6 @@ IonicUploadTask.prototype.run = function(ionic, callback) { if(response.statusCode == 302) { var redirectPath = parseUrl(response.headers.location); - console.log(redirectPath); project.set('app_id', redirectPath.pathname.match(/^\/projects\/(\w+)\/?$/)[1]); project.save(); From 6b23b40ef40594aff1ef0099ba8de7d06e77c2af Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Wed, 26 Feb 2014 17:20:28 -0600 Subject: [PATCH 0070/1100] Grab and upload plugins --- lib/ionic/package.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/ionic/package.js b/lib/ionic/package.js index a8837fd976..660b471acf 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -3,6 +3,7 @@ var fs = require('fs'), parseUrl = require('url').parse, argv = require('optimist').argv, prompt = require('prompt'), + shelljs = require('shelljs/global'), FormData = require('form-data'), IonicProject = require('./project'), IonicTask = require('./task').IonicTask, @@ -144,6 +145,19 @@ IonicPackageTask.prototype.run = function(ionic) { form.append('build_mode', mode); form.append('csrfmiddlewaretoken', jar.cookies[0].value); + console.log('Grabbing plugins...'); + exec('cordova plugins', function(code, output) { + if(code != 0 ) { + process.stderr.write('Unable to read cordova plugin list. Please see console for more info.\n'); + } else { + output = output.replace(/'/g, '"'); + var plugins = JSON.parse(output); + for (var i = 0; i < plugins.length; i++) { + form.append('plugin_'+i, plugins[i]); + }; + } + }); + // Add the platform specific project properties to the post for (var property in project) { From 5609783ba407cfdfe4b78dc9ad166553dcbfa47e Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Fri, 28 Feb 2014 12:19:00 -0600 Subject: [PATCH 0071/1100] Whoops do the plugin grab sychronously --- lib/ionic.js | 2 +- lib/ionic/package.js | 30 ++++++++++++++---------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 5aa09ba996..e5a80ae121 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -82,7 +82,7 @@ var TASKS = [ Ionic = function() {}; Ionic.prototype = { - IONIC_DASH: 'http://virt/', + IONIC_DASH: 'http://apps.ionicframework.com/', IONIC_COOKIES: 'ionic.cookies', IONIC_API: 'api/v1/', _tryBuildingTask: function() { diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 660b471acf..a695dc48bc 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -47,6 +47,16 @@ IonicPackageTask.prototype.run = function(ionic) { var project = IonicProject.load(); + console.log('Grabbing plugins...'); + var plugins = []; + var pluginExec = exec('cordova plugins'); + if(pluginExec.code != 0 ) { + process.stderr.write('Unable to read cordova plugin list. Please see console for more info.\n'); + } else { + pluginExec.output = pluginExec.output.replace(/'/g, '"'); + plugins = JSON.parse(pluginExec.output); + } + for(var i = 0; i < platforms.length; i++) { var platform = platforms[i]; @@ -145,24 +155,12 @@ IonicPackageTask.prototype.run = function(ionic) { form.append('build_mode', mode); form.append('csrfmiddlewaretoken', jar.cookies[0].value); - console.log('Grabbing plugins...'); - exec('cordova plugins', function(code, output) { - if(code != 0 ) { - process.stderr.write('Unable to read cordova plugin list. Please see console for more info.\n'); - } else { - output = output.replace(/'/g, '"'); - var plugins = JSON.parse(output); - for (var i = 0; i < plugins.length; i++) { - form.append('plugin_'+i, plugins[i]); - }; - } - }); - + for (var i = 0; i < plugins.length; i++) { + form.append('plugin_'+i, plugins[i]); + }; // Add the platform specific project properties to the post for (var property in project) { - // console.log(property); - // console.log(project[property]); if(property.indexOf(platform) == 0) { form.append(property, project.get(property)); } @@ -236,7 +234,7 @@ IonicPackageTask.prototype.run = function(ionic) { ionic.fail("Error packaging: " + err); } console.log('Done'); - }); + }); }); } }); From b0438e30e32c0bc165812dfa951f4c3c26af06df Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Mon, 3 Mar 2014 12:35:21 -0600 Subject: [PATCH 0072/1100] Why wasn't this ever committed? --- lib/ionic/config.js | 2 +- lib/ionic/package.js | 8 ++++---- lib/ionic/project.js | 6 +++++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/ionic/config.js b/lib/ionic/config.js index 541240d073..b1c2e648e6 100644 --- a/lib/ionic/config.js +++ b/lib/ionic/config.js @@ -40,6 +40,6 @@ module.exports = { } this.data[k] = v; - this.saveConfig(); + this.save(); } }; diff --git a/lib/ionic/package.js b/lib/ionic/package.js index a695dc48bc..f555ef577f 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -39,8 +39,8 @@ IonicPackageTask.prototype.run = function(ionic) { ionic.fail('No platforms specified, exiting.'); } - var upload = new IonicUploadTask(); - upload.run(ionic, function() { + //var upload = new IonicUploadTask(); + //upload.run(ionic, function() { var login = new IonicLoginTask(); login.get(ionic, function(jar) { @@ -160,7 +160,7 @@ IonicPackageTask.prototype.run = function(ionic) { }; // Add the platform specific project properties to the post - for (var property in project) { + for (var property in project.get()) { if(property.indexOf(platform) == 0) { form.append(property, project.get(property)); } @@ -238,7 +238,7 @@ IonicPackageTask.prototype.run = function(ionic) { }); } }); - }); + //}); }; exports.IonicPackageTask = IonicPackageTask; diff --git a/lib/ionic/project.js b/lib/ionic/project.js index e437226a73..c227284b10 100644 --- a/lib/ionic/project.js +++ b/lib/ionic/project.js @@ -44,7 +44,11 @@ module.exports = { } }, get: function(k) { - return this.data[k]; + if(k) { + return this.data[k]; + } else { + return this.data; + } }, set: function(k, v) { if(!this.data) { From 1f27d375f2834028dfe3152c06f1ce0e61494737 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Mon, 3 Mar 2014 12:41:09 -0600 Subject: [PATCH 0073/1100] Tim's wish is my command --- lib/ionic/login.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 5567796ba4..5064e38eab 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -39,6 +39,7 @@ IonicLoginTask.prototype.run = function(ionic, callback) { } console.log('Signup here! '+ionic.IONIC_DASH+'signup'); + console.log('Login Info:'); prompt.override = argv; From 001d4d43042ea2ae291febf22435ad5dcf9f5a31 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Mon, 3 Mar 2014 16:20:51 -0600 Subject: [PATCH 0074/1100] Should upload when packaging --- lib/ionic/package.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ionic/package.js b/lib/ionic/package.js index f555ef577f..bf66b47b42 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -39,8 +39,8 @@ IonicPackageTask.prototype.run = function(ionic) { ionic.fail('No platforms specified, exiting.'); } - //var upload = new IonicUploadTask(); - //upload.run(ionic, function() { + var upload = new IonicUploadTask(); + upload.run(ionic, function() { var login = new IonicLoginTask(); login.get(ionic, function(jar) { @@ -238,7 +238,7 @@ IonicPackageTask.prototype.run = function(ionic) { }); } }); - //}); + }); }; exports.IonicPackageTask = IonicPackageTask; From b8af9dd2b5c7de9c631b05b36bd5850afd1d1e8f Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Mon, 10 Mar 2014 14:37:00 -0500 Subject: [PATCH 0075/1100] Ignore add keyword for platform --- lib/ionic/platform.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/ionic/platform.js b/lib/ionic/platform.js index d8a1a76d99..671c76ff8a 100644 --- a/lib/ionic/platform.js +++ b/lib/ionic/platform.js @@ -33,10 +33,14 @@ IonicPlatformTask.prototype.run = function(ionic) { ionic.fail('No platforms specified, exiting.'); } + if(platforms[0].toLowerCase() == 'add') { + platforms.shift(); + } + IonicStats.t('platform', { 'platform': platforms.join(',') }); for(var i = 0; i < platforms.length; i++) { - var platform = platforms[i]; + var platform = platforms[i].toLowerCase(); console.log('Adding platform', platform); if(exec("cordova platform add " + platform).code !== 0) { process.stderr.write('Unable to add platform ' + platform + '. Please see console for more info.\n'); From 477e79456fd5088388f1c820f9abbcc940413156 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Fri, 21 Mar 2014 14:48:35 -0500 Subject: [PATCH 0076/1100] Added support for starter project naming --- lib/ionic.js | 2 +- lib/ionic/config.js | 2 +- lib/ionic/login.js | 11 ++++++----- lib/ionic/start.js | 29 ++++++++++++++++++++--------- lib/ionic/upload.js | 3 +++ 5 files changed, 31 insertions(+), 16 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 5aa09ba996..e5a80ae121 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -82,7 +82,7 @@ var TASKS = [ Ionic = function() {}; Ionic.prototype = { - IONIC_DASH: 'http://virt/', + IONIC_DASH: 'http://apps.ionicframework.com/', IONIC_COOKIES: 'ionic.cookies', IONIC_API: 'api/v1/', _tryBuildingTask: function() { diff --git a/lib/ionic/config.js b/lib/ionic/config.js index 541240d073..b1c2e648e6 100644 --- a/lib/ionic/config.js +++ b/lib/ionic/config.js @@ -40,6 +40,6 @@ module.exports = { } this.data[k] = v; - this.saveConfig(); + this.save(); } }; diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 5567796ba4..41fd38e57b 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -17,7 +17,7 @@ IonicLoginTask.prototype.run = function(ionic, callback) { var schema = [{ name: 'email', pattern: /^[A-z0-9!#$%&'*+\/=?^_{|}~-]+(?:\.[A-z0-9!#$%&'*+\/=?^_{|}~-]+)*@(?:[A-z0-9](?:[A-z0-9-]*[A-z0-9])?\.)+[A-z0-9](?:[A-z0-9-]*[A-z0-9])?$/, - message: 'Email for Ionic Studio login', + message: 'Email for Ionic Apps login', required: true }, { name: 'password', @@ -38,12 +38,13 @@ IonicLoginTask.prototype.run = function(ionic, callback) { schema.shift(); } - console.log('Signup here! '+ionic.IONIC_DASH+'signup'); + console.log('To continue, please login to your Ionic Apps account (Create a free account here: ' + ionic.IONIC_DASH + 'signup)'); prompt.override = argv; prompt.start(); + prompt.get(schema, function (err, result) { if(err) { ionic.fail('Error logging in: ' + err); @@ -60,7 +61,7 @@ IonicLoginTask.prototype.run = function(ionic, callback) { var jar = request.jar(); request({ - url: ionic.IONIC_DASH+'login', + url: ionic.IONIC_DASH + 'login', jar: jar }, function(err, response, body) { @@ -70,7 +71,7 @@ IonicLoginTask.prototype.run = function(ionic, callback) { request({ method: 'POST', - url: ionic.IONIC_DASH+'login', + url: ionic.IONIC_DASH + 'login', jar: jar, form: { username: self.email, @@ -84,7 +85,7 @@ IonicLoginTask.prototype.run = function(ionic, callback) { } // Should be a 304 redirect status code if correct if(response.statusCode == 200) { - ionic.fail('Email or Password incorrect. Please visit '+ionic.IONIC_DASH+' for help. :)') + ionic.fail('Email or Password incorrect. Please visit '+ ionic.IONIC_DASH +' for help.') } var err = fs.writeFileSync(ionic.IONIC_COOKIES, JSON.stringify(jar, null, 2)); diff --git a/lib/ionic/start.js b/lib/ionic/start.js index ed6667d65a..09b615ff8a 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -43,6 +43,8 @@ IonicStartTask.prototype.run = function(ionic) { // Grab the target path from the command line, or use this as the app name var targetPathName = argv._[2] || this.appName; + var starterProject = argv._[3] || 'tabs'; + this.targetPath = path.resolve(this.appName); // Make sure to create this, or ask them if they want to override it @@ -51,23 +53,30 @@ IonicStartTask.prototype.run = function(ionic) { process.exit(1); } - console.log('Creating Ionic app in folder', this.targetPath); + console.log('Creating Ionic app in folder', this.targetPath, 'based on', starterProject, 'project'); fs.mkdirSync(this.targetPath); - this._fetchAndWriteSeed(); + this._fetchAndWriteSeed(starterProject); this._writeConfig(ionic); }; -IonicStartTask.prototype._fetchAndWriteSeed = function() { +IonicStartTask.prototype._getStarterUrl = function(repo) { + return 'https://github.com/driftyco/' + repo + '/archive/master.zip' ; +}; + +IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { var self = this; - var templateUrl = 'https://github.com/driftyco/ionic-angular-cordova-seed/archive/master.zip' ; + var repoName = 'ionic-starter-' + projectType; + var repoFolderName = repoName + '-master'; + var templateUrl = this._getStarterUrl(repoName); + console.log('Downloading starter template from', templateUrl); var tmpFolder = os.tmpdir(); - var tempZipFilePath = path.join(tmpFolder, 'ionic-angular-cordova-seed' + new Date().getTime() + '.zip'); + var tempZipFilePath = path.join(tmpFolder, repoName + new Date().getTime() + '.zip'); var tempZipFileStream = fs.createWriteStream(tempZipFilePath) var unzipRepo = function(fileName) { @@ -76,8 +85,8 @@ IonicStartTask.prototype._fetchAndWriteSeed = function() { var writeStream = unzip.Extract({ path: self.targetPath }); writeStream.on('close', function() { //fs.renameSync(self.targetPath + '/' + 'ionic-angular-cordova-seed-master', self.targetPath + '/app'); - cp('-R', self.targetPath + '/' + 'ionic-angular-cordova-seed-master/.', self.targetPath); - rm('-rf', self.targetPath + '/' + 'ionic-angular-cordova-seed-master/'); + cp('-R', self.targetPath + '/' + repoFolderName + '/.', self.targetPath); + rm('-rf', self.targetPath + '/' + repoFolderName + '/'); console.log('Project created!'); cd(self.targetPath); @@ -93,6 +102,10 @@ IonicStartTask.prototype._fetchAndWriteSeed = function() { }; request({ url: templateUrl, encoding: null }, function(err, res, body) { + if(res.statusCode !== 200) { + console.log('Unable to fetch project: HTTP', res.statusCode); + return; + } tempZipFileStream.write(body); tempZipFileStream.close(); unzipRepo(tempZipFilePath); @@ -100,8 +113,6 @@ IonicStartTask.prototype._fetchAndWriteSeed = function() { }; IonicStartTask.prototype._writeConfig = function(ionic) { - console.log('Writing '+this.targetPath+'/ionic.config'); - var project = IonicProject.create(); project.set('name', this.appName); project.save(this.targetPath); diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index 196d98992d..7378de53a1 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -6,6 +6,7 @@ var fs = require('fs'), FormData = require('form-data'), IonicProject = require('./project'), IonicTask = require('./task').IonicTask, + IonicStats = require('./stats').IonicStats, IonicLoginTask = require('./login').IonicLoginTask; var IonicUploadTask = function() { @@ -23,6 +24,8 @@ IonicUploadTask.prototype.run = function(ionic, callback) { console.log('Zipping...'); + IonicStats.t('upload', {}); + var zip = fs.createWriteStream('www.zip'); var archive = archiver('zip'); From 41cf47dfd3e289c5cf808b1bfec90d577cf4a021 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 24 Mar 2014 12:42:25 -0500 Subject: [PATCH 0077/1100] Updated ascii art and added ability to specify starter project --- lib/ionic.js | 14 ++++++++++++-- lib/ionic/start.js | 11 ++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index e5a80ae121..41a59c0ab9 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -125,8 +125,17 @@ Ionic.prototype = { }, _printIonic: function() { - process.stdout.write('\n __ __ \n'); - process.stdout.write('| / \\ |\\ | | / `\n' + '| \\__/ | \\| | \\__,\n\n'); + var w = function(s) { + process.stdout.write(s); + }; + + w(' _ _ \n'); + w(' (_) (_) \n'); + w(' _ ___ _ __ _ ___ \n'); + w(' | |/ _ \\| \'_ \\| |/ __|\n'); + w(' | | (_) | | | | | (__ \n'); + w(' |_|\\___/|_| |_|_|\\___|\n'); + }, _loadTaskRunner: function(which) { @@ -146,6 +155,7 @@ Ionic.prototype = { }, fail: function(msg) { + process.stderr.write('Error, exiting:'); process.stderr.write(msg + '\n'); process.exit(1); } diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 09b615ff8a..128cc98d9e 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -32,6 +32,10 @@ IonicStartTask.HELP_LINE = 'Start a new Ionic project with the given name.'; IonicStartTask.prototype = new IonicTask(); +IonicStartTask.prototype._printUsage = function() { + process.stderr.write('\nUsage: ionic start appName [starterProject(blank,tabs,sidemenu)]\n'); +}; + IonicStartTask.prototype.run = function(ionic) { if(argv._.length < 2) { ionic.fail('No app name specified, exiting.'); @@ -41,9 +45,9 @@ IonicStartTask.prototype.run = function(ionic) { this.appName = argv._[1]; // Grab the target path from the command line, or use this as the app name - var targetPathName = argv._[2] || this.appName; + var targetPathName = this.appName; - var starterProject = argv._[3] || 'tabs'; + var starterProject = argv._[2] || 'tabs'; this.targetPath = path.resolve(this.appName); @@ -103,7 +107,8 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { request({ url: templateUrl, encoding: null }, function(err, res, body) { if(res.statusCode !== 200) { - console.log('Unable to fetch project: HTTP', res.statusCode); + console.error('Error: Unable to fetch project: HTTP', res.statusCode); + console.error('Valid project types are blank, tabs, or sidemenu (or see more on our starter page: http://ionicframework.com/getting-started/)'); return; } tempZipFileStream.write(body); From 035114ae6dede65aff3fb0475eab877cccc08f95 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 24 Mar 2014 16:40:33 -0500 Subject: [PATCH 0078/1100] New starter project sourcing --- bin/ionic | 3 +- lib/ionic.js | 68 ++++++++++++++++++++++++++--- lib/ionic/start.js | 105 +++++++++++++++++++++++++++++++-------------- package.json | 8 ++-- 4 files changed, 140 insertions(+), 44 deletions(-) diff --git a/bin/ionic b/bin/ionic index 171649cb21..3b444affe9 100755 --- a/bin/ionic +++ b/bin/ionic @@ -7,5 +7,4 @@ process.title = 'ionic'; var Ionic = require('../lib/ionic').Ionic; -var ionic = new Ionic(); -ionic.run(); +Ionic.run(); diff --git a/lib/ionic.js b/lib/ionic.js index 41a59c0ab9..e2d3e406a8 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -21,7 +21,29 @@ var IonicStartTask = require('./ionic/start').IonicStartTask, IonicBuildTask = require('./ionic/build').IonicBuildTask, IonicLoginTask = require('./ionic/login').IonicLoginTask, IonicUploadTask = require('./ionic/upload').IonicUploadTask, - IonicPackageTask = require('./ionic/package').IonicPackageTask; + IonicPackageTask = require('./ionic/package').IonicPackageTask, + path = require('path'), + request = require('request'), + os = require('os'), + unzip = require('unzip'), + colors = require('colors'), + Q = require('q'); + + +colors.setTheme({ + silly: 'rainbow', + input: 'grey', + small: 'grey', + verbose: 'cyan', + prompt: 'grey', + info: 'white', + data: 'grey', + help: 'cyan', + warn: 'yellow', + debug: 'blue', + error: 'red' +}); + var fs = require('fs'), argv = require('optimist').argv; @@ -79,9 +101,7 @@ var TASKS = [ } ]; -Ionic = function() {}; - -Ionic.prototype = { +Ionic = { IONIC_DASH: 'http://apps.ionicframework.com/', IONIC_COOKIES: 'ionic.cookies', IONIC_API: 'api/v1/', @@ -148,7 +168,7 @@ Ionic.prototype = { return this._printGenericUsage(); } - console.log('Running', task.title, 'task...') + console.log('Running', task.title.info.bold, 'task...') var taskObj = new task.task(); taskObj.run(this); @@ -158,8 +178,44 @@ Ionic.prototype = { process.stderr.write('Error, exiting:'); process.stderr.write(msg + '\n'); process.exit(1); - } + }, + + /** + * Fetch a repo from GitHub, unzip it to a specific folder. + */ + fetchRepo: function(targetPath, repoName, repoUrl) { + var q = Q.defer(); + + // The folder name the project will be downloaded and extracted to + var repoFolderName = repoName + '-master'; + console.log('Fetching repo:', repoUrl); + + var tmpFolder = os.tmpdir(); + var tempZipFilePath = path.join(tmpFolder, repoName + new Date().getTime() + '.zip'); + var tempZipFileStream = fs.createWriteStream(tempZipFilePath) + var unzipRepo = function(fileName) { + var readStream = fs.createReadStream(fileName); + + var writeStream = unzip.Extract({ path: targetPath }); + writeStream.on('close', function() { + q.resolve(repoFolderName); + }); + readStream.pipe(writeStream); + }; + + request({ url: repoUrl, encoding: null }, function(err, res, body) { + if(res.statusCode !== 200) { + q.reject(res); + return; + } + tempZipFileStream.write(body); + tempZipFileStream.close(); + unzipRepo(tempZipFilePath); + }); + + return q.promise; + } }; exports.Ionic = Ionic; diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 128cc98d9e..a07f8b359b 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -6,6 +6,9 @@ var fs = require('fs'), shelljs = require('shelljs/global'), unzip = require('unzip'), argv = require('optimist').argv, + colors = require('colors'), + Q = require('q'), + IonicProject = require('../ionic'), IonicProject = require('./project'), IonicTask = require('./task').IonicTask; IonicStats = require('./stats').IonicStats; @@ -28,6 +31,9 @@ fs.mkdirParent = function(dirPath, mode, callback) { var IonicStartTask = function() { } +// The URL for the cordova wrapper project +IonicStartTask.WRAPPER_REPO_NAME = 'ionic-cordova-wrapper'; + IonicStartTask.HELP_LINE = 'Start a new Ionic project with the given name.'; IonicStartTask.prototype = new IonicTask(); @@ -53,11 +59,11 @@ IonicStartTask.prototype.run = function(ionic) { // Make sure to create this, or ask them if they want to override it if(this._checkTargetPath() === false) { - process.stderr.write('Exiting.\n'); + process.stderr.write('\nExiting.\n'); process.exit(1); } - console.log('Creating Ionic app in folder', this.targetPath, 'based on', starterProject, 'project'); + console.log('Creating Ionic app in folder', this.targetPath, 'based on', starterProject.info.bold, 'project'); fs.mkdirSync(this.targetPath); @@ -70,51 +76,84 @@ IonicStartTask.prototype._getStarterUrl = function(repo) { return 'https://github.com/driftyco/' + repo + '/archive/master.zip' ; }; +IonicStartTask.prototype._fetchWrapper = function() { + var q = Q.defer(); + var self = this; + + var repoName = IonicStartTask.WRAPPER_REPO_NAME; + var repoUrl = 'https://github.com/driftyco/' + IonicStartTask.WRAPPER_REPO_NAME + '/archive/master.zip'; + console.log('Fetching', 'Cordova'.info.bold, 'wrapper'); + + Ionic.fetchRepo(self.targetPath, repoName, repoUrl).then(function(repoFolderName) { + cp('-R', self.targetPath + '/' + repoFolderName + '/.', self.targetPath); + rm('-rf', self.targetPath + '/' + repoFolderName + '/'); + cd(self.targetPath); + + q.resolve(self.targetPath); + }, function(err) { + q.reject(err); + }); + + return q.promise; +}; + IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { var self = this; - var repoName = 'ionic-starter-' + projectType; - var repoFolderName = repoName + '-master'; - var templateUrl = this._getStarterUrl(repoName); + // First, grab the wrapper project. - console.log('Downloading starter template from', templateUrl); + var wrapperPromise = this._fetchWrapper(); - var tmpFolder = os.tmpdir(); - var tempZipFilePath = path.join(tmpFolder, repoName + new Date().getTime() + '.zip'); - var tempZipFileStream = fs.createWriteStream(tempZipFilePath) + wrapperPromise.then(function(targetPath) { + // Get the starter project repo name: + var repoName = 'ionic-starter-' + projectType; - var unzipRepo = function(fileName) { - var readStream = fs.createReadStream(fileName); + // The folder name the project will be downloaded and extracted to + var repoFolderName = repoName + '-master'; - var writeStream = unzip.Extract({ path: self.targetPath }); - writeStream.on('close', function() { - //fs.renameSync(self.targetPath + '/' + 'ionic-angular-cordova-seed-master', self.targetPath + '/app'); - cp('-R', self.targetPath + '/' + repoFolderName + '/.', self.targetPath); - rm('-rf', self.targetPath + '/' + repoFolderName + '/'); - console.log('Project created!'); + // Get the URL for the starter project repo: + var repoUrl = self._getStarterUrl(repoName); - cd(self.targetPath); - console.log('Initializing cordova project.'); + Ionic.fetchRepo(self.targetPath, repoName, repoUrl).then(function(repoFolderName) { + // Move the content of this repo into the www folder + cp('-R', targetPath + '/' + repoFolderName + '/.', 'www'); + + // Copy the root config file to the www folder + cp(targetPath + '/config.xml', 'www'); + + // Clean up start template folder + rm('-rf', targetPath + '/' + repoFolderName + '/'); + + console.log('Initializing cordova project.'.info.bold); if(!exec('cordova plugin add org.apache.cordova.device') || !exec('cordova plugin add org.apache.cordova.console') || !exec('cordova plugin add org.apache.cordova.statusbar')) { - process.stderr.write('Unable to install one or more cordova plugins.\n'); + process.stderr.write('Unable to install one or more cordova plugins.\n'.error.bold); } + self._printQuickHelp(); + IonicStats.t('start', {}); + }, function(err) { + console.error('Error: Unable to fetch project: HTTP'.error.bold, res.statusCode); + console.error('Valid project types are blank, tabs, or sidemenu (or see more on our starter page: http://ionicframework.com/getting-started/)'.error.bold); + Ionic.fail(''); }); - readStream.pipe(writeStream); - }; - - request({ url: templateUrl, encoding: null }, function(err, res, body) { - if(res.statusCode !== 200) { - console.error('Error: Unable to fetch project: HTTP', res.statusCode); - console.error('Valid project types are blank, tabs, or sidemenu (or see more on our starter page: http://ionicframework.com/getting-started/)'); - return; - } - tempZipFileStream.write(body); - tempZipFileStream.close(); - unzipRepo(tempZipFilePath); + + }, function(err) { + Ionic.fail('Unable to grab wrapper project:'.error.bold, err); }); + +}; + +IonicStartTask.prototype._printQuickHelp = function() { + console.log('\nYour Ionic project is ready to go!'.green.bold); + console.log('\nSome quick tips:'); + console.log(' * Make sure to add a platform (ios or Android):', 'ionic platform add android [ios]'.info.bold); + console.log(' Note: iOS development requires OS X currently'.small); + console.log('\n * To build your project, run', 'ionic build [platform]'.info.bold); + console.log('\n * To emulate your project in a simulator or emulator, run', 'ionic emulate [platform]'.info.bold); + console.log('\n * To run your project on a device, run', 'ionic run [platform]'.info.bold); + console.log('\n\nFor more help, visit the Ionic docs:', 'http://ionicframework.com/docs'.info.bold); }; IonicStartTask.prototype._writeConfig = function(ionic) { @@ -125,7 +164,7 @@ IonicStartTask.prototype._writeConfig = function(ionic) { IonicStartTask.prototype._checkTargetPath = function() { if(fs.existsSync(this.targetPath)) { - process.stderr.write('The directory ' + this.targetPath + ' already exists, please remove it if you would like to create a new ionic project there.\n'); + process.stderr.write('The directory '.error.bold + this.targetPath + ' already exists, please remove it if you would like to create a new ionic project there.\n'.error.bold); return false; } return true; diff --git a/package.json b/package.json index f6598e9c05..1ba7e6295f 100644 --- a/package.json +++ b/package.json @@ -26,14 +26,16 @@ "license": "MIT", "dependencies": { "archiver": "0.5.1", - "cordova": "~3.2.0", - "event-stream": "3.0.x", + "cordova": "~3.2.0", + "event-stream": "3.0.x", "form-data": "~0.1.0", "ncp": "0.4.2", "optimist": "0.6.0", "prompt": "0.2.12", "request": "2.27.0", "shelljs": "0.2.6", - "unzip": "0.1.9" + "unzip": "0.1.9", + "q": "^1.0.1", + "colors": "^0.6.2" } } From 79c4842421c77fecae36c5e1f7e602ff4f6f7a6a Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 24 Mar 2014 16:53:24 -0500 Subject: [PATCH 0079/1100] Updated package --- package.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/package.json b/package.json index 1ba7e6295f..d276025bc6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "0.9.16", + "version": "1.0.0-beta", "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -19,14 +19,10 @@ "type": "git", "url": "https://github.com/driftyco/ionic-cli.git" }, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, "author": "Max Lynch and Peter Collins ", "license": "MIT", "dependencies": { "archiver": "0.5.1", - "cordova": "~3.2.0", "event-stream": "3.0.x", "form-data": "~0.1.0", "ncp": "0.4.2", From c33048227d34aeaf0741e0615326e67eb786531c Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 24 Mar 2014 16:55:03 -0500 Subject: [PATCH 0080/1100] Added note --- lib/ionic/start.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index a07f8b359b..3e8019be36 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -150,6 +150,7 @@ IonicStartTask.prototype._printQuickHelp = function() { console.log('\nSome quick tips:'); console.log(' * Make sure to add a platform (ios or Android):', 'ionic platform add android [ios]'.info.bold); console.log(' Note: iOS development requires OS X currently'.small); + console.log('\n * cd into your project directory before running any of the following commands'.info.bold); console.log('\n * To build your project, run', 'ionic build [platform]'.info.bold); console.log('\n * To emulate your project in a simulator or emulator, run', 'ionic emulate [platform]'.info.bold); console.log('\n * To run your project on a device, run', 'ionic run [platform]'.info.bold); From 9c4b9eeee815e61e7cede5f6953b44b9c1a9f4ab Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 24 Mar 2014 16:58:17 -0500 Subject: [PATCH 0081/1100] Added note --- lib/ionic/start.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 3e8019be36..1cc218e738 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -130,7 +130,7 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { process.stderr.write('Unable to install one or more cordova plugins.\n'.error.bold); } - self._printQuickHelp(); + self._printQuickHelp(targetPath); IonicStats.t('start', {}); }, function(err) { @@ -145,12 +145,12 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { }; -IonicStartTask.prototype._printQuickHelp = function() { +IonicStartTask.prototype._printQuickHelp = function(projectDirectory) { console.log('\nYour Ionic project is ready to go!'.green.bold); console.log('\nSome quick tips:'); + console.log('\n * cd into your project directory before running any of the following commands', ('(cd ' + projectDirectory + ')').info.bold); console.log(' * Make sure to add a platform (ios or Android):', 'ionic platform add android [ios]'.info.bold); console.log(' Note: iOS development requires OS X currently'.small); - console.log('\n * cd into your project directory before running any of the following commands'.info.bold); console.log('\n * To build your project, run', 'ionic build [platform]'.info.bold); console.log('\n * To emulate your project in a simulator or emulator, run', 'ionic emulate [platform]'.info.bold); console.log('\n * To run your project on a device, run', 'ionic run [platform]'.info.bold); From 915fab6957e9a329010038ef7706dabdb8787256 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 24 Mar 2014 16:59:47 -0500 Subject: [PATCH 0082/1100] Added note --- lib/ionic/start.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 1cc218e738..2651f8b484 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -130,7 +130,7 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { process.stderr.write('Unable to install one or more cordova plugins.\n'.error.bold); } - self._printQuickHelp(targetPath); + self._printQuickHelp(self.targetPath); IonicStats.t('start', {}); }, function(err) { From 1baed22745eb99fa8cafcea1c18491836bcb0082 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 24 Mar 2014 17:01:00 -0500 Subject: [PATCH 0083/1100] Added note --- lib/ionic/start.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 2651f8b484..4c70aa52e4 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -130,7 +130,7 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { process.stderr.write('Unable to install one or more cordova plugins.\n'.error.bold); } - self._printQuickHelp(self.targetPath); + self._printQuickHelp(self.appName); IonicStats.t('start', {}); }, function(err) { From d9e40ff0f77937df0739ff55469b4c60cf025555 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 24 Mar 2014 17:16:18 -0500 Subject: [PATCH 0084/1100] Updated --- lib/ionic/start.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 4c70aa52e4..2a0942f7bf 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -149,7 +149,7 @@ IonicStartTask.prototype._printQuickHelp = function(projectDirectory) { console.log('\nYour Ionic project is ready to go!'.green.bold); console.log('\nSome quick tips:'); console.log('\n * cd into your project directory before running any of the following commands', ('(cd ' + projectDirectory + ')').info.bold); - console.log(' * Make sure to add a platform (ios or Android):', 'ionic platform add android [ios]'.info.bold); + console.log('\n * Make sure to add a platform (ios or Android):', 'ionic platform add android [ios]'.info.bold); console.log(' Note: iOS development requires OS X currently'.small); console.log('\n * To build your project, run', 'ionic build [platform]'.info.bold); console.log('\n * To emulate your project in a simulator or emulator, run', 'ionic emulate [platform]'.info.bold); From a297872e673aa8d479b390806d548d7178df86dd Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Tue, 25 Mar 2014 00:45:18 -0500 Subject: [PATCH 0085/1100] Moved to base project --- lib/ionic.js | 4 ++-- lib/ionic/start.js | 37 ++++++++++++++++++++++++++----------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index e2d3e406a8..f8d4ee9481 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -175,8 +175,8 @@ Ionic = { }, fail: function(msg) { - process.stderr.write('Error, exiting:'); - process.stderr.write(msg + '\n'); + process.stderr.write('ERROR: '); + process.stderr.write(msg.error.bold + '\nExiting.\n'); process.exit(1); }, diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 2a0942f7bf..6d79baaeb9 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -32,7 +32,7 @@ var IonicStartTask = function() { } // The URL for the cordova wrapper project -IonicStartTask.WRAPPER_REPO_NAME = 'ionic-cordova-wrapper'; +IonicStartTask.WRAPPER_REPO_NAME = 'ionic-app-base'; IonicStartTask.HELP_LINE = 'Start a new Ionic project with the given name.'; @@ -104,7 +104,11 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { var wrapperPromise = this._fetchWrapper(); - wrapperPromise.then(function(targetPath) { + wrapperPromise.then(function() { + exec('npm install'); + exec('gulp bootstrap --sass'); + }) + .then(function() { // Get the starter project repo name: var repoName = 'ionic-starter-' + projectType; @@ -115,22 +119,33 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { var repoUrl = self._getStarterUrl(repoName); Ionic.fetchRepo(self.targetPath, repoName, repoUrl).then(function(repoFolderName) { + var pluginQ = Q.defer(); + // Move the content of this repo into the www folder - cp('-R', targetPath + '/' + repoFolderName + '/.', 'www'); + cp('-Rf', self.targetPath + '/' + repoFolderName + '/.', 'www'); // Copy the root config file to the www folder - cp(targetPath + '/config.xml', 'www'); + cp(self.targetPath + '/config.xml', 'www'); // Clean up start template folder - rm('-rf', targetPath + '/' + repoFolderName + '/'); + rm('-rf', self.targetPath + '/' + repoFolderName + '/'); console.log('Initializing cordova project.'.info.bold); - if(!exec('cordova plugin add org.apache.cordova.device') || !exec('cordova plugin add org.apache.cordova.console') || - !exec('cordova plugin add org.apache.cordova.statusbar')) { - process.stderr.write('Unable to install one or more cordova plugins.\n'.error.bold); - } - - self._printQuickHelp(self.appName); + exec('cordova plugin add org.apache.cordova.device && ' + + 'cordova plugin add org.apache.cordova.console && ' + + 'cordova plugin add org.apache.cordova.statusbar', function(err, stdout, stderr) { + if(err) { + pluginQ.reject(stderr); + } else { + pluginQ.resolve(stdout); + } + }) + + pluginQ.promise.then(function() { + self._printQuickHelp(self.appName); + }, function(err) { + Ionic.fail('Unable to add plugins. Perhaps your version of Cordova is too old. Try updating (npm install -g cordova), removing this project folder, and trying again.'); + }); IonicStats.t('start', {}); }, function(err) { From c75e86049451701ad24ac827ef2ef33fa7bf57fa Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Tue, 25 Mar 2014 00:59:30 -0500 Subject: [PATCH 0086/1100] Tweaked install order, fixed error handler --- lib/ionic/start.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 6d79baaeb9..f9d708ed17 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -105,10 +105,6 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { var wrapperPromise = this._fetchWrapper(); wrapperPromise.then(function() { - exec('npm install'); - exec('gulp bootstrap --sass'); - }) - .then(function() { // Get the starter project repo name: var repoName = 'ionic-starter-' + projectType; @@ -121,6 +117,9 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { Ionic.fetchRepo(self.targetPath, repoName, repoUrl).then(function(repoFolderName) { var pluginQ = Q.defer(); + exec('npm install'); + exec('gulp bootstrap --sass'); + // Move the content of this repo into the www folder cp('-Rf', self.targetPath + '/' + repoFolderName + '/.', 'www'); @@ -148,8 +147,8 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { }); IonicStats.t('start', {}); - }, function(err) { - console.error('Error: Unable to fetch project: HTTP'.error.bold, res.statusCode); + }).catch(function(err) { + console.error('Error: Unable to fetch project: HTTP:'.error.bold, err.statusCode); console.error('Valid project types are blank, tabs, or sidemenu (or see more on our starter page: http://ionicframework.com/getting-started/)'.error.bold); Ionic.fail(''); }); From 34e506b2054da92ba6094061de03604af9695a79 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Tue, 25 Mar 2014 10:07:33 -0500 Subject: [PATCH 0087/1100] Run init instead of bootstrap --- lib/ionic/start.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index f9d708ed17..f2e35398c6 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -117,8 +117,9 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { Ionic.fetchRepo(self.targetPath, repoName, repoUrl).then(function(repoFolderName) { var pluginQ = Q.defer(); + // Install dependencies for the starter project exec('npm install'); - exec('gulp bootstrap --sass'); + exec('gulp init --sass'); // Move the content of this repo into the www folder cp('-Rf', self.targetPath + '/' + repoFolderName + '/.', 'www'); From 48af5213f38d647eb6c3a155ee954fdfe8978c90 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Tue, 25 Mar 2014 15:56:55 -0500 Subject: [PATCH 0088/1100] Delay release of login stuff --- lib/ionic.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/ionic.js b/lib/ionic.js index f8d4ee9481..f8dbeccfc2 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -79,6 +79,7 @@ var TASKS = [ usage: 'platform', task: IonicPlatformTask }, + /* { title: 'login', name: 'login', @@ -99,6 +100,7 @@ var TASKS = [ usage: 'mode platform', task: IonicPackageTask } + */ ]; Ionic = { From ea41deb4e3daa2f7890ab7dcae071da7666fb588 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Tue, 25 Mar 2014 22:47:24 -0500 Subject: [PATCH 0089/1100] Fixed upload task --- lib/ionic.js | 5 ++--- lib/ionic/login.js | 6 +++--- lib/ionic/package.js | 14 +++++++------- lib/ionic/project.js | 6 +++--- lib/ionic/upload.js | 23 ++++++++++++++++++----- 5 files changed, 33 insertions(+), 21 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index f8dbeccfc2..50e25c698b 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -79,7 +79,6 @@ var TASKS = [ usage: 'platform', task: IonicPlatformTask }, - /* { title: 'login', name: 'login', @@ -100,11 +99,11 @@ var TASKS = [ usage: 'mode platform', task: IonicPackageTask } - */ ]; Ionic = { - IONIC_DASH: 'http://apps.ionicframework.com/', + //IONIC_DASH: 'http://apps.ionicframework.com/', + IONIC_DASH: 'http://127.0.0.1:8000/', IONIC_COOKIES: 'ionic.cookies', IONIC_API: 'api/v1/', _tryBuildingTask: function() { diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 41fd38e57b..1b11ea70b0 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -8,7 +8,7 @@ var fs = require('fs'), var IonicLoginTask = function() { } -IonicLoginTask.HELP_LINE = 'Login to Ionic Studio'; +IonicLoginTask.HELP_LINE = 'Login to the Ionic Platform'; IonicLoginTask.prototype = new IonicTask(); @@ -17,7 +17,7 @@ IonicLoginTask.prototype.run = function(ionic, callback) { var schema = [{ name: 'email', pattern: /^[A-z0-9!#$%&'*+\/=?^_{|}~-]+(?:\.[A-z0-9!#$%&'*+\/=?^_{|}~-]+)*@(?:[A-z0-9](?:[A-z0-9-]*[A-z0-9])?\.)+[A-z0-9](?:[A-z0-9-]*[A-z0-9])?$/, - message: 'Email for Ionic Apps login', + message: 'Email for your Ionic Platform login', required: true }, { name: 'password', @@ -38,7 +38,7 @@ IonicLoginTask.prototype.run = function(ionic, callback) { schema.shift(); } - console.log('To continue, please login to your Ionic Apps account (Create a free account here: ' + ionic.IONIC_DASH + 'signup)'); + console.log('To continue, please login to your Ionic Platform account (Create a free account here: ' + ionic.IONIC_DASH + 'signup)'); prompt.override = argv; diff --git a/lib/ionic/package.js b/lib/ionic/package.js index bf66b47b42..0a958a33be 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -13,7 +13,7 @@ var fs = require('fs'), var IonicPackageTask = function() { } -IonicPackageTask.HELP_LINE = 'Package an Ionic project for the given plaform.'; +IonicPackageTask.HELP_LINE = 'Package an Ionic project using the Ionic Platform Build service (requires login)'; IonicPackageTask.prototype = new IonicTask(); @@ -146,7 +146,7 @@ IonicPackageTask.prototype.run = function(ionic) { } project.save(); - console.log('Packaging '+platform+' app...'); + console.log('Packaging ' + platform + ' app...'); var form = new FormData(); form.append('email', project.get('email')); @@ -156,7 +156,7 @@ IonicPackageTask.prototype.run = function(ionic) { form.append('csrfmiddlewaretoken', jar.cookies[0].value); for (var i = 0; i < plugins.length; i++) { - form.append('plugin_'+i, plugins[i]); + form.append('plugin_' + i, plugins[i]); }; // Add the platform specific project properties to the post @@ -176,7 +176,7 @@ IonicPackageTask.prototype.run = function(ionic) { } else { project.remove('android_keystore'); project.save(); - process.stderr.write('\nCan\'t find file: "'+keystoreFilePath+'"\nSkipping build..."\n\n'); + process.stderr.write('\nCan\'t find file: "' + keystoreFilePath + '"\nSkipping build..."\n\n'); return; } } @@ -188,7 +188,7 @@ IonicPackageTask.prototype.run = function(ionic) { } else { project.remove('ios_certificate'); project.save(); - process.stderr.write('\nCan\'t find file: "'+certificateFilePath+'"\nSkipping build..."\n\n'); + process.stderr.write('\nCan\'t find file: "' + certificateFilePath + '"\nSkipping build..."\n\n'); return; } var profileFilePath = path.resolve(project.get('ios_profile')); @@ -197,13 +197,13 @@ IonicPackageTask.prototype.run = function(ionic) { } else { project.remove('ios_profile'); project.save(); - process.stderr.write('\nCan\'t find file: "'+profileFilePath+'"\nSkipping build..."\n\n'); + process.stderr.write('\nCan\'t find file: "' + profileFilePath + '"\nSkipping build..."\n\n'); return; } break; default: console.trace(); - process.stderr.write('\nUnknown platform: "'+platform+'"\nWe should never get here"\n\n'); + process.stderr.write('\nUnknown platform: "' + platform + '"\nWe should never get here"\n\n'); break; } diff --git a/lib/ionic/project.js b/lib/ionic/project.js index c227284b10..36a6f246bb 100644 --- a/lib/ionic/project.js +++ b/lib/ionic/project.js @@ -22,8 +22,8 @@ module.exports = { if(fs.existsSync(this.file)) { this.data = JSON.parse(fs.readFileSync(this.file)) } else { - new Ionic().fail('Could not find ' + this.file + '!'+ - ' Please run this command in your root ionic project directory with that file.'); + Ionic.fail('Could not find your ' + this.file + ' file!'+ + ' Please run this command in the root of your ionic project.'); } return this; }, @@ -38,7 +38,7 @@ module.exports = { console.error('This should never happen!'); } try { - fs.writeFileSync((targetPath?targetPath+'/':'')+this.PROJECT_FILE, JSON.stringify(this.data, null, 2)); + fs.writeFileSync((targetPath?targetPath + '/':'') + this.PROJECT_FILE, JSON.stringify(this.data, null, 2)); } catch(e) { console.error('Unable to save settings file:', e); } diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index 7378de53a1..a170318251 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -7,17 +7,19 @@ var fs = require('fs'), IonicProject = require('./project'), IonicTask = require('./task').IonicTask, IonicStats = require('./stats').IonicStats, - IonicLoginTask = require('./login').IonicLoginTask; + IonicLoginTask = require('./login').IonicLoginTask, + Q = require('q'); var IonicUploadTask = function() { } -IonicUploadTask.HELP_LINE = 'Upload an Ionic project.'; +IonicUploadTask.HELP_LINE = 'Upload an Ionic project to the Ionic Platform (requires login)'; IonicUploadTask.prototype = new IonicTask(); IonicUploadTask.prototype.run = function(ionic, callback) { var project = IonicProject.load(); + var q = Q.defer(); var login = new IonicLoginTask(); login.get(ionic, function(jar) { @@ -51,7 +53,7 @@ IonicUploadTask.prototype.run = function(ionic, callback) { form.append('app_file', fs.createReadStream(path.resolve('www.zip')), {filename: 'www.zip', contentType: 'application/zip'}); // form.append('app_file', zip.toBuffer(), {filename: 'www.zip', contentType: 'application/zip'}); - var url = ionic.IONIC_DASH+'projects/'+project.get('app_id'); + var url = ionic.IONIC_DASH + 'projects/' + project.get('app_id'); var params = parseUrl(url); form.submit({ @@ -67,9 +69,9 @@ IonicUploadTask.prototype.run = function(ionic, callback) { if(err) { ionic.fail("Error uploading: " + err); } - - console.log('Done'); + console.log('Done uploading'.info.bold); + if(response.statusCode == 302) { var redirectPath = parseUrl(response.headers.location); project.set('app_id', redirectPath.pathname.match(/^\/projects\/(\w+)\/?$/)[1]); @@ -78,10 +80,21 @@ IonicUploadTask.prototype.run = function(ionic, callback) { if (callback) { callback(); }; + } else { + callback && callback(); } + + q.resolve(callback); }); }); }); + + q.promise.then(function(callback) { + // Exit if there was no callback + if(!callback) { + process.exit(0); + } + }); }; exports.IonicUploadTask = IonicUploadTask; From 735a2fe870a2663810e200694af3c13faa14bff5 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Tue, 25 Mar 2014 23:04:54 -0500 Subject: [PATCH 0090/1100] Spacing around quotes --- lib/ionic/package.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 0a958a33be..8213893389 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -207,7 +207,7 @@ IonicPackageTask.prototype.run = function(ionic) { break; } - var url = ionic.IONIC_DASH+'export/'+project.get('app_id'); + var url = ionic.IONIC_DASH + 'export/' + project.get('app_id'); var params = parseUrl(url); form.submit({ From cbd212092f1398ef82994e11b6dafdc98afbcb97 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Tue, 25 Mar 2014 23:07:10 -0500 Subject: [PATCH 0091/1100] Removing some stuff --- lib/ionic.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/ionic.js b/lib/ionic.js index 50e25c698b..b234f32f0e 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -79,6 +79,7 @@ var TASKS = [ usage: 'platform', task: IonicPlatformTask }, + /* { title: 'login', name: 'login', @@ -99,6 +100,7 @@ var TASKS = [ usage: 'mode platform', task: IonicPackageTask } + */ ]; Ionic = { From 2c3f9c5c4980a7ca7b299ee038bcf9a59c9b6e98 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Wed, 26 Mar 2014 14:56:33 -0500 Subject: [PATCH 0092/1100] Initial private settings for projects --- lib/ionic/package.js | 32 +++++++++++---------- lib/ionic/private.js | 67 ++++++++++++++++++++++++++++++++++++++++++++ lib/ionic/project.js | 7 ----- lib/ionic/upload.js | 2 +- 4 files changed, 85 insertions(+), 23 deletions(-) create mode 100644 lib/ionic/private.js diff --git a/lib/ionic/package.js b/lib/ionic/package.js index bf66b47b42..b227526a31 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -6,6 +6,7 @@ var fs = require('fs'), shelljs = require('shelljs/global'), FormData = require('form-data'), IonicProject = require('./project'), + IonicProjectPrivate = require('./private'), IonicTask = require('./task').IonicTask, IonicUploadTask = require('./upload').IonicUploadTask, IonicLoginTask = require('./login').IonicLoginTask; @@ -46,6 +47,7 @@ IonicPackageTask.prototype.run = function(ionic) { login.get(ionic, function(jar) { var project = IonicProject.load(); + var projectPrivate = IonicProjectPrivate.load(project.get('app_id')); console.log('Grabbing plugins...'); var plugins = []; @@ -128,7 +130,7 @@ IonicPackageTask.prototype.run = function(ionic) { // Don't prompt for properties we already have in the config for (var property in properties) { - if(project.get(property)) { + if(projectPrivate.get(property)) { delete properties[property]; } } @@ -140,11 +142,11 @@ IonicPackageTask.prototype.run = function(ionic) { // Overwrite any empty properties with prompt responses for (var property in properties) { - if(result[property] && !project.get(property)) { - project.set(property, result[property]); + if(result[property] && !projectPrivate.get(property)) { + projectPrivate.set(property, result[property]); } } - project.save(); + projectPrivate.save(project.get('app_id')); console.log('Packaging '+platform+' app...'); @@ -160,9 +162,9 @@ IonicPackageTask.prototype.run = function(ionic) { }; // Add the platform specific project properties to the post - for (var property in project.get()) { + for (var property in projectPrivate.get()) { if(property.indexOf(platform) == 0) { - form.append(property, project.get(property)); + form.append(property, projectPrivate.get(property)); } } @@ -170,33 +172,33 @@ IonicPackageTask.prototype.run = function(ionic) { switch(platform) { case 'android': if(mode == 'release') { - var keystoreFilePath = path.resolve(project.get('android_keystore')); + var keystoreFilePath = path.resolve(projectPrivate.get('android_keystore')); if(fs.existsSync(keystoreFilePath)) { form.append('android_keystore_file', fs.createReadStream(keystoreFilePath), {filename: 'www.zip'}); } else { - project.remove('android_keystore'); - project.save(); + projectPrivate.remove('android_keystore'); + projectPrivate.save(project.get('app_id')); process.stderr.write('\nCan\'t find file: "'+keystoreFilePath+'"\nSkipping build..."\n\n'); return; } } break; case 'ios': - var certificateFilePath = path.resolve(project.get('ios_certificate')); + var certificateFilePath = path.resolve(projectPrivate.get('ios_certificate')); if(fs.existsSync(certificateFilePath)) { form.append('ios_certificate_file', fs.createReadStream(certificateFilePath)); } else { - project.remove('ios_certificate'); - project.save(); + projectPrivate.remove('ios_certificate'); + projectPrivate.save(project.get('app_id')); process.stderr.write('\nCan\'t find file: "'+certificateFilePath+'"\nSkipping build..."\n\n'); return; } - var profileFilePath = path.resolve(project.get('ios_profile')); + var profileFilePath = path.resolve(projectPrivate.get('ios_profile')); if(fs.existsSync(profileFilePath)) { form.append('ios_profile_file', fs.createReadStream(profileFilePath)); } else { - project.remove('ios_profile'); - project.save(); + projectPrivate.remove('ios_profile'); + projectPrivate.save(project.get('app_id')); process.stderr.write('\nCan\'t find file: "'+profileFilePath+'"\nSkipping build..."\n\n'); return; } diff --git a/lib/ionic/private.js b/lib/ionic/private.js new file mode 100644 index 0000000000..63a3377a64 --- /dev/null +++ b/lib/ionic/private.js @@ -0,0 +1,67 @@ +var fs = require('fs'), + path = require('path'), + ionic = require('../ionic'); + +var home = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE; + +module.exports = { + PRIVATE_PATH: '.ionic/', + PRIVATE_EXT: '.project', + PRIVATE_DEFAULT: { + ios_certificate: '', + ios_certificate_password: '', + ios_profile: '', + android_keystore: '', + android_keystore_alias: '', + android_keystore_password: '', + android_key_password: '' + }, + load: function(app_id) { + if(app_id) { + this.file = this.PRIVATE_PATH+app_id+this.PRIVATE_EXT; + if(fs.existsSync(path.join(home,this.file))) { + this.data = JSON.parse(fs.readFileSync(path.join(home, this.file))); + } else { + this.data = {}; + } + } + return this; + }, + create: function(name) { + this.data = this.PRIVATE_DEFAULT; + return this; + }, + save: function(app_id) { + if(!this.data) { + console.trace(); + console.error('This should never happen!'); + } + if(app_id) { + this.file = this.PRIVATE_PATH+app_id+this.PRIVATE_EXT; + try { + fs.writeFileSync(path.join(home,this.file), JSON.stringify(this.data, null, 2)); + } catch(e) { + console.error('Unable to save private settings file:', e); + } + } + }, + get: function(k) { + if(k) { + return this.data[k]; + } else { + return this.data; + } + }, + set: function(k, v) { + if(!this.data) { + this.data = PRIVATE_DEFAULT; + } + this.data[k] = v; + }, + remove: function(k) { + if(!this.data) { + this.data = PRIVATE_DEFAULT; + } + this.data[k] = ''; + } +}; diff --git a/lib/ionic/project.js b/lib/ionic/project.js index c227284b10..180757375c 100644 --- a/lib/ionic/project.js +++ b/lib/ionic/project.js @@ -9,13 +9,6 @@ module.exports = { email: '', app_id: '', package_name: '', - ios_certificate: '', - ios_certificate_password: '', - ios_profile: '', - android_keystore: '', - android_keystore_alias: '', - android_keystore_password: '', - android_key_password: '' }, load: function() { this.file = this.PROJECT_FILE; diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index 196d98992d..997de2bbe4 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -72,7 +72,7 @@ IonicUploadTask.prototype.run = function(ionic, callback) { project.set('app_id', redirectPath.pathname.match(/^\/projects\/(\w+)\/?$/)[1]); project.save(); - if (callback) { + if(callback) { callback(); }; } From 7b2cda1c5bdf49282d6a299b231bbf0358c4dad5 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Wed, 26 Mar 2014 15:42:54 -0500 Subject: [PATCH 0093/1100] Finish privatizing passwords --- lib/ionic/package.js | 4 ++-- lib/ionic/private.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/ionic/package.js b/lib/ionic/package.js index b227526a31..10acd4e397 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -41,7 +41,7 @@ IonicPackageTask.prototype.run = function(ionic) { } var upload = new IonicUploadTask(); - upload.run(ionic, function() { + //upload.run(ionic, function() { var login = new IonicLoginTask(); login.get(ionic, function(jar) { @@ -240,7 +240,7 @@ IonicPackageTask.prototype.run = function(ionic) { }); } }); - }); + //}); }; exports.IonicPackageTask = IonicPackageTask; diff --git a/lib/ionic/private.js b/lib/ionic/private.js index 63a3377a64..e58d810c4f 100644 --- a/lib/ionic/private.js +++ b/lib/ionic/private.js @@ -22,12 +22,12 @@ module.exports = { if(fs.existsSync(path.join(home,this.file))) { this.data = JSON.parse(fs.readFileSync(path.join(home, this.file))); } else { - this.data = {}; + this.data = this.PRIVATE_DEFAULT; } } return this; }, - create: function(name) { + create: function() { this.data = this.PRIVATE_DEFAULT; return this; }, From ce6aea76023eeda0b85fb6bd286fc670770753ed Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Thu, 27 Mar 2014 18:53:32 -0500 Subject: [PATCH 0094/1100] Removed dep on gulp and bower --- lib/ionic.js | 32 ++++++++++++++++++++- lib/ionic/start.js | 69 +++++++++++++++++++++++++--------------------- package.json | 4 +-- 3 files changed, 70 insertions(+), 35 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index b234f32f0e..2578e279a8 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -27,6 +27,7 @@ var IonicStartTask = require('./ionic/start').IonicStartTask, os = require('os'), unzip = require('unzip'), colors = require('colors'), + spawn = require('child_process').spawn, Q = require('q'); @@ -183,6 +184,35 @@ Ionic = { process.exit(1); }, + spawnPromise: function(cmd, args, onStdOut, onStdErr) { + var q = Q.defer(); + console.log('\nRUNNING:'.info.bold, cmd, args.join(' ')); + try { + var child = spawn(cmd, args); + } catch(e) { + } + child.stdout.setEncoding('utf8'); + child.stderr.setEncoding('utf8'); + child.stdout.on('data', function(data) { + process.stdout.write(data); + onStdOut && onStdOut(data); + }); + child.stderr.on('data', function(data) { + process.stderr.write(data); + onStdErr && onStdErr(data); + }); + child.on('error', function(err) { + q.reject(err); + }); + child.on('exit', function(code) { + if(code === 0) { + q.resolve(); + } else { + q.reject(code); + } + }); + return q.promise; + }, /** * Fetch a repo from GitHub, unzip it to a specific folder. */ @@ -191,7 +221,7 @@ Ionic = { // The folder name the project will be downloaded and extracted to var repoFolderName = repoName + '-master'; - console.log('Fetching repo:', repoUrl); + console.log('\nDOWNLOADING:'.info.bold, repoUrl); var tmpFolder = os.tmpdir(); var tempZipFilePath = path.join(tmpFolder, repoName + new Date().getTime() + '.zip'); diff --git a/lib/ionic/start.js b/lib/ionic/start.js index f2e35398c6..b15b700769 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -7,6 +7,7 @@ var fs = require('fs'), unzip = require('unzip'), argv = require('optimist').argv, colors = require('colors'), + spawn = require('child_process').spawn, Q = require('q'), IonicProject = require('../ionic'), IonicProject = require('./project'), @@ -82,7 +83,6 @@ IonicStartTask.prototype._fetchWrapper = function() { var repoName = IonicStartTask.WRAPPER_REPO_NAME; var repoUrl = 'https://github.com/driftyco/' + IonicStartTask.WRAPPER_REPO_NAME + '/archive/master.zip'; - console.log('Fetching', 'Cordova'.info.bold, 'wrapper'); Ionic.fetchRepo(self.targetPath, repoName, repoUrl).then(function(repoFolderName) { cp('-R', self.targetPath + '/' + repoFolderName + '/.', self.targetPath); @@ -115,41 +115,46 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { var repoUrl = self._getStarterUrl(repoName); Ionic.fetchRepo(self.targetPath, repoName, repoUrl).then(function(repoFolderName) { + var npmQ = Q.defer(); + var gulpQ = Q.defer(); var pluginQ = Q.defer(); - // Install dependencies for the starter project - exec('npm install'); - exec('gulp init --sass'); - - // Move the content of this repo into the www folder - cp('-Rf', self.targetPath + '/' + repoFolderName + '/.', 'www'); - - // Copy the root config file to the www folder - cp(self.targetPath + '/config.xml', 'www'); - - // Clean up start template folder - rm('-rf', self.targetPath + '/' + repoFolderName + '/'); - - console.log('Initializing cordova project.'.info.bold); - exec('cordova plugin add org.apache.cordova.device && ' + - 'cordova plugin add org.apache.cordova.console && ' + - 'cordova plugin add org.apache.cordova.statusbar', function(err, stdout, stderr) { - if(err) { - pluginQ.reject(stderr); - } else { - pluginQ.resolve(stdout); - } - }) - - pluginQ.promise.then(function() { - self._printQuickHelp(self.appName); - }, function(err) { - Ionic.fail('Unable to add plugins. Perhaps your version of Cordova is too old. Try updating (npm install -g cordova), removing this project folder, and trying again.'); - }); - IonicStats.t('start', {}); + //Ionic.spawnPromise('npm', ['install', '--quiet']).then(function() { + // Move the content of this repo into the www folder + cp('-Rf', self.targetPath + '/' + repoFolderName + '/.', 'www'); + + // Copy the root config file to the www folder + cp(self.targetPath + '/config.xml', 'www'); + + // Clean up start template folder + rm('-rf', self.targetPath + '/' + repoFolderName + '/'); + + console.log('Initializing cordova project.'.info.bold); + exec('cordova plugin add org.apache.cordova.device && ' + + 'cordova plugin add org.apache.cordova.console && ' + + 'cordova plugin add org.apache.cordova.statusbar', function(err, stdout, stderr) { + if(err) { + pluginQ.reject(stderr); + } else { + pluginQ.resolve(stdout); + } + }) + + pluginQ.promise.then(function() { + self._printQuickHelp(self.appName); + }, function(err) { + Ionic.fail('Unable to add plugins. Perhaps your version of Cordova is too old. Try updating (npm install -g cordova), removing this project folder, and trying again.'); + }); + + IonicStats.t('start', {}); + /* + }).fail(function(err, stderr) {; + Ionic.fail('Unable to install Ionic dependencies. Please make sure you\'ve installed cordova, and gulp globally by running:\nsudo npm install -g cordova gulp. Error:\n'.error.bold + err + '\n' + stderr); + }); + */ }).catch(function(err) { - console.error('Error: Unable to fetch project: HTTP:'.error.bold, err.statusCode); + console.error('Error: Unable to fetch project: HTTP:'.error.bold, err.statusCode, err); console.error('Valid project types are blank, tabs, or sidemenu (or see more on our starter page: http://ionicframework.com/getting-started/)'.error.bold); Ionic.fail(''); }); diff --git a/package.json b/package.json index d276025bc6..a09d323b0a 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "request": "2.27.0", "shelljs": "0.2.6", "unzip": "0.1.9", - "q": "^1.0.1", - "colors": "^0.6.2" + "colors": "^0.6.2", + "q": "^1.0.1" } } From 556374c1e7c1256adab642dd8cbf2cb00536365a Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Thu, 27 Mar 2014 18:59:04 -0500 Subject: [PATCH 0095/1100] vNum Update --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a09d323b0a..0d780a255e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.0.0-beta", + "version": "1.0.0-beta.1", "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 0c683eca716fa6bc9a34de3ab0c656f5f8e28efe Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Thu, 27 Mar 2014 18:59:51 -0500 Subject: [PATCH 0096/1100] idk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0d780a255e..902e99e824 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.0.0-beta.1", + "version": "1.0.1-beta", "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 15933b01a3e27f969afa89269416a2f570b8fc09 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Wed, 2 Apr 2014 11:44:34 -0500 Subject: [PATCH 0097/1100] Just lowercase all starter projects by default --- lib/ionic/start.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index b15b700769..1a74914dfa 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -68,7 +68,7 @@ IonicStartTask.prototype.run = function(ionic) { fs.mkdirSync(this.targetPath); - this._fetchAndWriteSeed(starterProject); + this._fetchAndWriteSeed(starterProject.toLowerCase()); this._writeConfig(ionic); }; From 269804a6542243613daef18455c8d5293d528d85 Mon Sep 17 00:00:00 2001 From: Peter C Date: Fri, 18 Apr 2014 14:34:29 -0500 Subject: [PATCH 0098/1100] Don't copy config.xml into www anymore People were getting confused on new versions of cordova with config.xml files appearing in the www folder and the project parent directory. --- lib/ionic/start.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 1a74914dfa..17fd663948 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -125,7 +125,7 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { cp('-Rf', self.targetPath + '/' + repoFolderName + '/.', 'www'); // Copy the root config file to the www folder - cp(self.targetPath + '/config.xml', 'www'); + // cp(self.targetPath + '/config.xml', 'www'); // Clean up start template folder rm('-rf', self.targetPath + '/' + repoFolderName + '/'); From c218c3f4f15347dfdf5b53cb89dc7283daf75b49 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Thu, 24 Apr 2014 15:55:22 -0700 Subject: [PATCH 0099/1100] Removed statusbar plugin by default --- lib/ionic/start.js | 4 ++-- package.json | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 17fd663948..ed46b18347 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -132,8 +132,8 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { console.log('Initializing cordova project.'.info.bold); exec('cordova plugin add org.apache.cordova.device && ' + - 'cordova plugin add org.apache.cordova.console && ' + - 'cordova plugin add org.apache.cordova.statusbar', function(err, stdout, stderr) { + 'cordova plugin add org.apache.cordova.console' + , function(err, stdout, stderr) { if(err) { pluginQ.reject(stderr); } else { diff --git a/package.json b/package.json index 902e99e824..5f87238aca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.0.1-beta", + "version": "1.0.3-beta", "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -32,6 +32,7 @@ "shelljs": "0.2.6", "unzip": "0.1.9", "colors": "^0.6.2", - "q": "^1.0.1" + "q": "^1.0.1", + "npm": "^1.4.6" } } From 278d0051e65886e31c0d9d1b03cd768d8148f12c Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 28 Apr 2014 12:33:40 -0500 Subject: [PATCH 0100/1100] Proxy support --- lib/ionic.js | 5 ++++- lib/ionic/project.js | 3 +++ lib/ionic/start.js | 12 ++++++------ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 2578e279a8..91bea82f99 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -22,6 +22,7 @@ var IonicStartTask = require('./ionic/start').IonicStartTask, IonicLoginTask = require('./ionic/login').IonicLoginTask, IonicUploadTask = require('./ionic/upload').IonicUploadTask, IonicPackageTask = require('./ionic/package').IonicPackageTask, + IonicProject = require('./ionic/project'), path = require('path'), request = require('request'), os = require('os'), @@ -219,6 +220,8 @@ Ionic = { fetchRepo: function(targetPath, repoName, repoUrl) { var q = Q.defer(); + var proxy = process.env.PROXY || null; + // The folder name the project will be downloaded and extracted to var repoFolderName = repoName + '-master'; console.log('\nDOWNLOADING:'.info.bold, repoUrl); @@ -237,7 +240,7 @@ Ionic = { readStream.pipe(writeStream); }; - request({ url: repoUrl, encoding: null }, function(err, res, body) { + request({ url: repoUrl, encoding: null, proxy: proxy }, function(err, res, body) { if(res.statusCode !== 200) { q.reject(res); return; diff --git a/lib/ionic/project.js b/lib/ionic/project.js index 0494f94ef1..8390466d82 100644 --- a/lib/ionic/project.js +++ b/lib/ionic/project.js @@ -37,6 +37,9 @@ module.exports = { } }, get: function(k) { + if(!this.data) { + return null; + } if(k) { return this.data[k]; } else { diff --git a/lib/ionic/start.js b/lib/ionic/start.js index ed46b18347..7b9d7c79a8 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -9,7 +9,6 @@ var fs = require('fs'), colors = require('colors'), spawn = require('child_process').spawn, Q = require('q'), - IonicProject = require('../ionic'), IonicProject = require('./project'), IonicTask = require('./task').IonicTask; IonicStats = require('./stats').IonicStats; @@ -168,12 +167,13 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { IonicStartTask.prototype._printQuickHelp = function(projectDirectory) { console.log('\nYour Ionic project is ready to go!'.green.bold); console.log('\nSome quick tips:'); - console.log('\n * cd into your project directory before running any of the following commands', ('(cd ' + projectDirectory + ')').info.bold); - console.log('\n * Make sure to add a platform (ios or Android):', 'ionic platform add android [ios]'.info.bold); + console.log('\n * cd into your project:', ('$ cd ' + projectDirectory).info.bold); + console.log('\n * Add a platform (ios or Android):', 'ionic platform add ios [android]'.info.bold); console.log(' Note: iOS development requires OS X currently'.small); - console.log('\n * To build your project, run', 'ionic build [platform]'.info.bold); - console.log('\n * To emulate your project in a simulator or emulator, run', 'ionic emulate [platform]'.info.bold); - console.log('\n * To run your project on a device, run', 'ionic run [platform]'.info.bold); + console.log(' See the Android Platform Guid for full Android installation instructions: https://cordova.apache.org/docs/en/3.4.0/guide_platforms_android_index.md.html#Android%20Platform%20Guide'.small); + console.log('\n * Build your app:', 'ionic build [platform]'.info.bold); + console.log('\n * Simulate your app:', 'ionic emulate [platform]'.info.bold); + console.log('\n * Run your app on a device:', 'ionic run [platform]'.info.bold); console.log('\n\nFor more help, visit the Ionic docs:', 'http://ionicframework.com/docs'.info.bold); }; From 3ad53647fca5f64399de9d602107bcbae70bbd19 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 28 Apr 2014 12:33:57 -0500 Subject: [PATCH 0101/1100] Proxy support --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5f87238aca..c2d3b2ad24 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.0.3-beta", + "version": "1.0.4", "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 5e298d1fd40d1feaf302deff87c47b36b544e299 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Tue, 29 Apr 2014 09:51:50 -0500 Subject: [PATCH 0102/1100] Alpha features --- lib/ionic.js | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 91bea82f99..7dde3ff159 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -81,7 +81,6 @@ var TASKS = [ usage: 'platform', task: IonicPlatformTask }, - /* { title: 'login', name: 'login', @@ -94,7 +93,8 @@ var TASKS = [ alt: ['up'], usage: '', task: IonicUploadTask - }, + } + /* { title: 'package', name: 'package', diff --git a/package.json b/package.json index c2d3b2ad24..c6fab60902 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.0.4", + "version": "1.0.5", "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 995a0218b22bc22f311eb5454a2401a1971957b8 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Tue, 29 Apr 2014 11:54:06 -0500 Subject: [PATCH 0103/1100] Fixed URL --- lib/ionic.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 7dde3ff159..756365d872 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -106,8 +106,8 @@ var TASKS = [ ]; Ionic = { - //IONIC_DASH: 'http://apps.ionicframework.com/', - IONIC_DASH: 'http://127.0.0.1:8000/', + IONIC_DASH: 'http://apps.ionicframework.com/', + //IONIC_DASH: 'http://127.0.0.1:8000/', IONIC_COOKIES: 'ionic.cookies', IONIC_API: 'api/v1/', _tryBuildingTask: function() { From 5ff69acc57dc5268a962f91bc09da6567285ed4c Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Tue, 29 Apr 2014 11:54:27 -0500 Subject: [PATCH 0104/1100] v --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c6fab60902..123e1357e5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.0.5", + "version": "1.0.6", "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 08ab635933ad353ac39c3b4159abef23aebe2ee2 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Tue, 29 Apr 2014 12:26:07 -0500 Subject: [PATCH 0105/1100] Ionic serve --- lib/ionic.js | 13 ++++++++++--- lib/ionic/serve.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 6 ++++-- 3 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 lib/ionic/serve.js diff --git a/lib/ionic.js b/lib/ionic.js index 756365d872..bf56c3606a 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -22,6 +22,7 @@ var IonicStartTask = require('./ionic/start').IonicStartTask, IonicLoginTask = require('./ionic/login').IonicLoginTask, IonicUploadTask = require('./ionic/upload').IonicUploadTask, IonicPackageTask = require('./ionic/package').IonicPackageTask, + IonicServeTask = require('./ionic/serve'), IonicProject = require('./ionic/project'), path = require('path'), request = require('request'), @@ -51,6 +52,12 @@ var fs = require('fs'), argv = require('optimist').argv; var TASKS = [ + { + title: 'serve', + name: 'serve', + usage: '', + task: IonicServeTask + }, { title: 'start', name: 'start', @@ -93,7 +100,7 @@ var TASKS = [ alt: ['up'], usage: '', task: IonicUploadTask - } + }, /* { title: 'package', @@ -106,8 +113,8 @@ var TASKS = [ ]; Ionic = { - IONIC_DASH: 'http://apps.ionicframework.com/', - //IONIC_DASH: 'http://127.0.0.1:8000/', + //IONIC_DASH: 'http://apps.ionicframework.com/', + IONIC_DASH: 'http://127.0.0.1:8002/', IONIC_COOKIES: 'ionic.cookies', IONIC_API: 'api/v1/', _tryBuildingTask: function() { diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js new file mode 100644 index 0000000000..1c5768bcaa --- /dev/null +++ b/lib/ionic/serve.js @@ -0,0 +1,45 @@ +var fs = require('fs'), + argv = require('optimist').argv, + connect = require('connect'), + open = require('open'), + Q = require('q'), + IonicProject = require('./project'), + IonicTask = require('./task').IonicTask; + IonicStats = require('./stats').IonicStats; + +var IonicServeTask = function() {} + +IonicServeTask.HELP_LINE = 'Start a local development server for easy app development and testing'; + +IonicServeTask.prototype = new IonicTask(); + +IonicServeTask.prototype._printUsage = function() { + process.stderr.write('\nUsage: ionic serve [port] [options]\n'); + process.stderr.write('\nUse --nobrowser to disable auto browser launch\n'); +}; + +IonicServeTask.prototype.run = function(ionic) { + var project = IonicProject.load(); + + // Grab the name of the app + this.port = argv._[1] || '8100'; + + this.launchBrowser = typeof argv['nobrowser'] === 'undefined'; + + this._start(); +}; + +IonicServeTask.prototype._start = function() { + var self = this; + + console.log('Running local development server at', ('http://0.0.0.0:' + this.port).info.bold); + + setTimeout(function() { + if(self.launchBrowser) { + open('http://localhost:' + self.port); + } + }); + connect().use(connect.static('www')).listen(this.port); +}; + +module.exports = IonicServeTask; diff --git a/package.json b/package.json index 123e1357e5..cdc985d42d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.0.6", + "version": "1.0.7", "preferGlobal": true, "description": "A tool for creating and building Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -33,6 +33,8 @@ "unzip": "0.1.9", "colors": "^0.6.2", "q": "^1.0.1", - "npm": "^1.4.6" + "npm": "^1.4.6", + "connect": "^2.14.5", + "open": "0.0.5" } } From ee87fb0fce90f8ff8b31424aa82674e575bf0103 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Tue, 29 Apr 2014 14:36:25 -0500 Subject: [PATCH 0106/1100] Livereload --- lib/ionic.js | 15 +++++++++++ lib/ionic/serve.js | 67 +++++++++++++++++++++++++++++++++++++++------- package.json | 9 ++++--- 3 files changed, 78 insertions(+), 13 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index bf56c3606a..c4b7b2c61d 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -258,6 +258,21 @@ Ionic = { }); return q.promise; + }, + + getLocalIps: function() { + var interfaces = os.networkInterfaces(); + var addresses = []; + for (k in interfaces) { + for (k2 in interfaces[k]) { + var address = interfaces[k][k2]; + if (address.family == 'IPv4' && !address.internal) { + addresses.push(address.address) + } + } + } + + return addresses; } }; diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 1c5768bcaa..ac6f8129f8 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -2,6 +2,10 @@ var fs = require('fs'), argv = require('optimist').argv, connect = require('connect'), open = require('open'), + tinylr = require('tiny-lr-fork'), + lr = require('connect-livereload'), + vfs = require('vinyl-fs'), + request = require('request'), Q = require('q'), IonicProject = require('./project'), IonicTask = require('./task').IonicTask; @@ -14,7 +18,7 @@ IonicServeTask.HELP_LINE = 'Start a local development server for easy app develo IonicServeTask.prototype = new IonicTask(); IonicServeTask.prototype._printUsage = function() { - process.stderr.write('\nUsage: ionic serve [port] [options]\n'); + process.stderr.write('\nUsage: ionic serve [http-port] [livereload-port] [options]\n'); process.stderr.write('\nUse --nobrowser to disable auto browser launch\n'); }; @@ -22,24 +26,67 @@ IonicServeTask.prototype.run = function(ionic) { var project = IonicProject.load(); // Grab the name of the app - this.port = argv._[1] || '8100'; + this.port = argv._[1] || 8100; + this.liveReloadPort = argv._[2] || 35729; this.launchBrowser = typeof argv['nobrowser'] === 'undefined'; + this.runLivereload = typeof argv['nolivereload'] === 'undefined'; - this._start(); + this._start(ionic); }; -IonicServeTask.prototype._start = function() { - var self = this; +IonicServeTask.prototype._changed = function(path) { + // Cleanup the path a bit + var pwd = process.cwd() + path = path.replace(pwd + '/', ''); - console.log('Running local development server at', ('http://0.0.0.0:' + this.port).info.bold); + console.log('Changed', path); - setTimeout(function() { - if(self.launchBrowser) { - open('http://localhost:' + self.port); + var req = request.post('http://localhost:' + this.liveReloadPort + '/changed', { + path: '/changed', + method: 'POST', + body: JSON.stringify({ + files: [path] + }) + }, function(err, res, body) { + if(err) { + console.error('Unable to update live reload:', err); } }); - connect().use(connect.static('www')).listen(this.port); +}; + +IonicServeTask.prototype._start = function(ionic) { + var self = this; + + var app = connect(); + + if(this.runLivereload) { + vfs.watch('www/**/*', { + }, function(f) { + self._changed(f.path); + }); + + server = tinylr(); + server.listen(this.liveReloadPort, function(err) { + if(err) { + ionic.fail('Unable to start server:', err); + } else { + console.log('Running live reload server at', ('http://0.0.0.0:' + self.liveReloadPort).info.bold); + if(self.launchBrowser) { + open('http://localhost:' + self.port); + } + } + }); + + app.use(require('connect-livereload')({ + port: this.liveReloadPort + })) + } + + app.use(connect.static('www')) + .listen(this.port); + + console.log('Running dev server at', ('http://0.0.0.0:' + this.port).info.bold); }; module.exports = IonicServeTask; diff --git a/package.json b/package.json index cdc985d42d..c6652a11f4 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "ionic", - "version": "1.0.7", + "version": "1.0.8", "preferGlobal": true, - "description": "A tool for creating and building Ionic Framework mobile apps.", + "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", "bin": { "ionic": "bin/ionic" @@ -35,6 +35,9 @@ "q": "^1.0.1", "npm": "^1.4.6", "connect": "^2.14.5", - "open": "0.0.5" + "open": "0.0.5", + "tiny-lr-fork": "0.0.5", + "connect-livereload": "^0.4.0", + "vinyl-fs": "^0.1.4" } } From 60b53d8276f11f4ff6990aa28048fa64abd6ad7a Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Tue, 29 Apr 2014 14:43:06 -0500 Subject: [PATCH 0107/1100] Version and info --- lib/ionic.js | 8 +++++++- lib/ionic/start.js | 1 + package.json | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index c4b7b2c61d..ed0654e997 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -30,7 +30,8 @@ var IonicStartTask = require('./ionic/start').IonicStartTask, unzip = require('unzip'), colors = require('colors'), spawn = require('child_process').spawn, - Q = require('q'); + Q = require('q'), + settings = require('../package.json') colors.setTheme({ @@ -175,6 +176,11 @@ Ionic = { }, run: function() { + if(argv.version) { + console.log('Ionic CLI version ' + settings.version); + process.exit(0); + } + var task = this._tryBuildingTask(); if(!task) { return this._printGenericUsage(); diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 7b9d7c79a8..567de82265 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -174,6 +174,7 @@ IonicStartTask.prototype._printQuickHelp = function(projectDirectory) { console.log('\n * Build your app:', 'ionic build [platform]'.info.bold); console.log('\n * Simulate your app:', 'ionic emulate [platform]'.info.bold); console.log('\n * Run your app on a device:', 'ionic run [platform]'.info.bold); + console.log('\n * Develop in the browser with live reload:', 'ionic serve'.info.bold); console.log('\n\nFor more help, visit the Ionic docs:', 'http://ionicframework.com/docs'.info.bold); }; diff --git a/package.json b/package.json index c6652a11f4..03356c04db 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.0.8", + "version": "1.0.9", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From af86a7a1a1d0b82960297f8fca610470ca1ca372 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Sun, 11 May 2014 17:03:40 -0500 Subject: [PATCH 0108/1100] Fixed login and upload --- lib/ionic.js | 4 ++-- lib/ionic/login.js | 2 +- lib/ionic/upload.js | 31 ++++++++++++++++++++++++++----- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index ed0654e997..0304ab98d6 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -114,8 +114,8 @@ var TASKS = [ ]; Ionic = { - //IONIC_DASH: 'http://apps.ionicframework.com/', - IONIC_DASH: 'http://127.0.0.1:8002/', + IONIC_DASH: 'http://apps.ionicframework.com/', + //IONIC_DASH: 'http://localhost:8000/', IONIC_COOKIES: 'ionic.cookies', IONIC_API: 'api/v1/', _tryBuildingTask: function() { diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 1b11ea70b0..69faedac1d 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -38,7 +38,7 @@ IonicLoginTask.prototype.run = function(ionic, callback) { schema.shift(); } - console.log('To continue, please login to your Ionic Platform account (Create a free account here: ' + ionic.IONIC_DASH + 'signup)'); + console.log('To continue, please login to your Ionic account.\nDon\'t have one? Create a one here: ' + (ionic.IONIC_DASH + 'signup').info.bold + '\n'); prompt.override = argv; diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index c90a541b67..9af0f1a85e 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -45,7 +45,11 @@ IonicUploadTask.prototype.run = function(ionic, callback) { zip.on('close', function() { console.log('Done'); - console.log('Uploading...'); + if(project.get('app_id')) { + console.log('Uploading app (id: ' + project.get('app_id') + ')'); + } else { + console.log('Uploading app.'); + } var form = new FormData(); form.append('name', project.get('name')); @@ -57,7 +61,8 @@ IonicUploadTask.prototype.run = function(ionic, callback) { var params = parseUrl(url); form.submit({ - host: params.host, + hostname: params.hostname, + port: params.port, path: params.path, headers: form.getHeaders({ cookie: jar.cookies.map(function (c) { @@ -70,9 +75,14 @@ IonicUploadTask.prototype.run = function(ionic, callback) { ionic.fail("Error uploading: " + err); } - console.log('Done uploading'.info.bold); - if(response.statusCode == 302) { + if(response.headers.location.indexOf('login?next=') >= 0) { + console.error('Session expired. Please log in again and run this command again.'); + q.reject('not_logged_in'); + return; + } + console.log('Done!'.info.bold); + var redirectPath = parseUrl(response.headers.location); project.set('app_id', redirectPath.pathname.match(/^\/projects\/(\w+)\/?$/)[1]); project.save(); @@ -80,8 +90,17 @@ IonicUploadTask.prototype.run = function(ionic, callback) { if(callback) { callback(); }; - } else { + } else if(response.statusCode === 200) { + console.log('Got response', response.statusCode); callback && callback(); + } else { + if(response.headers['content-type'] == 'application/json') { + response.on('data', function(chunk) { + console.log(String(chunk)); + }); + } + console.error('Unable to upload. HTTP response:', response.statusCode); + q.reject(response); } q.resolve(callback); @@ -94,6 +113,8 @@ IonicUploadTask.prototype.run = function(ionic, callback) { if(!callback) { process.exit(0); } + }, function(err) { + process.exit(1); }); }; From d9944a752320d8a5ef386de151635333b9d5196d Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Sun, 11 May 2014 17:03:58 -0500 Subject: [PATCH 0109/1100] Version bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 03356c04db..cc685c4aaf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.0.9", + "version": "1.0.10", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 29199e5fb8f8ed21cc292d429fdc78d89f6e39f6 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Wed, 14 May 2014 10:05:41 -0500 Subject: [PATCH 0110/1100] Added keyboard plugin by default --- lib/ionic.js | 4 ++-- lib/ionic/start.js | 33 +++++++++++++++++++++++---------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 0304ab98d6..3fe3689bf5 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -114,8 +114,8 @@ var TASKS = [ ]; Ionic = { - IONIC_DASH: 'http://apps.ionicframework.com/', - //IONIC_DASH: 'http://localhost:8000/', + //IONIC_DASH: 'http://apps.ionicframework.com/', + IONIC_DASH: 'http://localhost:8000/', IONIC_COOKIES: 'ionic.cookies', IONIC_API: 'api/v1/', _tryBuildingTask: function() { diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 567de82265..e21c5fb5b7 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -96,6 +96,24 @@ IonicStartTask.prototype._fetchWrapper = function() { return q.promise; }; +IonicStartTask.prototype._installCordovaPlugins = function() { + var q = Q.defer(); + + console.log('Initializing cordova project.'.info.bold); + exec('cordova plugin add org.apache.cordova.device && ' + + 'cordova plugin add org.apache.cordova.console && ' + + 'cordova plugin add https://github.com/driftyco/ionic-plugins-keyboard' + , function(err, stdout, stderr) { + if(err) { + q.reject(stderr); + } else { + q.resolve(stdout); + } + }); + + return q.promise; +}; + IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { var self = this; @@ -129,16 +147,11 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { // Clean up start template folder rm('-rf', self.targetPath + '/' + repoFolderName + '/'); - console.log('Initializing cordova project.'.info.bold); - exec('cordova plugin add org.apache.cordova.device && ' + - 'cordova plugin add org.apache.cordova.console' - , function(err, stdout, stderr) { - if(err) { - pluginQ.reject(stderr); - } else { - pluginQ.resolve(stdout); - } - }) + self._installCordovaPlugins().then(function(output) { + pluginQ.resolve(stdout); + }, function(err) { + pluginQ.reject(err); + }); pluginQ.promise.then(function() { self._printQuickHelp(self.appName); From bd36f49b6ea6cc8bcb175badc4f183702f1a6c7d Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Wed, 14 May 2014 10:06:11 -0500 Subject: [PATCH 0111/1100] v bump --- lib/ionic.js | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 3fe3689bf5..0304ab98d6 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -114,8 +114,8 @@ var TASKS = [ ]; Ionic = { - //IONIC_DASH: 'http://apps.ionicframework.com/', - IONIC_DASH: 'http://localhost:8000/', + IONIC_DASH: 'http://apps.ionicframework.com/', + //IONIC_DASH: 'http://localhost:8000/', IONIC_COOKIES: 'ionic.cookies', IONIC_API: 'api/v1/', _tryBuildingTask: function() { diff --git a/package.json b/package.json index cc685c4aaf..413a0f89a6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.0.10", + "version": "1.0.11", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From fb5468087c0d874f0b558603fb7ebb9284e6c913 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Tue, 20 May 2014 13:12:12 -0500 Subject: [PATCH 0112/1100] USERPROFILE should take priority over HOMEPATH on Windows --- lib/ionic/config.js | 2 +- lib/ionic/private.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ionic/config.js b/lib/ionic/config.js index b1c2e648e6..14ede659a1 100644 --- a/lib/ionic/config.js +++ b/lib/ionic/config.js @@ -2,7 +2,7 @@ var fs = require('fs'), path = require('path'), ionic = require('../ionic'); -var home = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE; +var home = process.env.HOME || process.env.USERPROFILE || process.env.HOMEPATH; module.exports = { CONFIG_FILE: '.ionic/ionic.config', diff --git a/lib/ionic/private.js b/lib/ionic/private.js index e58d810c4f..68a83c7952 100644 --- a/lib/ionic/private.js +++ b/lib/ionic/private.js @@ -2,7 +2,7 @@ var fs = require('fs'), path = require('path'), ionic = require('../ionic'); -var home = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE; +var home = process.env.HOME || process.env.USERPROFILE || process.env.HOMEPATH; module.exports = { PRIVATE_PATH: '.ionic/', From 703205cda20c3f45bc04c0b9a54ae74ba18cac05 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Wed, 21 May 2014 15:34:55 -0500 Subject: [PATCH 0113/1100] Bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 413a0f89a6..731b282ce5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.0.11", + "version": "1.0.12", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From d408435ab227395bbd4a1837987cb9f7b44253d3 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Thu, 22 May 2014 14:50:46 -0500 Subject: [PATCH 0114/1100] Auto create project file --- lib/ionic/project.js | 12 ++++++++++++ package.json | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/ionic/project.js b/lib/ionic/project.js index 8390466d82..ebf6130b4d 100644 --- a/lib/ionic/project.js +++ b/lib/ionic/project.js @@ -15,14 +15,26 @@ module.exports = { if(fs.existsSync(this.file)) { this.data = JSON.parse(fs.readFileSync(this.file)) } else { + if(fs.existsSync('www')) { + var parts = path.resolve('./').split(path.sep); + var dirname = parts[parts.length-1]; + console.log('We have a www folder in the', dirname, 'project'); + this.create(dirname); + this.save('./'); + } + /* Ionic.fail('Could not find your ' + this.file + ' file!'+ ' Please run this command in the root of your ionic project.'); + */ } return this; }, create: function(name) { this.file = this.PROJECT_FILE; this.data = this.PROJECT_DEFAULT; + if(name) { + this.set('name', name); + } return this; }, save: function(targetPath) { diff --git a/package.json b/package.json index 731b282ce5..96a419daa4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.0.12", + "version": "1.0.13", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 8b306097952386c764f6a2da4feb93163e615b1e Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Thu, 22 May 2014 14:57:58 -0500 Subject: [PATCH 0115/1100] License tweak --- lib/ionic.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 0304ab98d6..a4f3ba0349 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -11,7 +11,7 @@ http://ionicframework.com/ A utility for starting and administering Ionic based mobile app projects. Licensed under the MIT license. See LICENSE For more. -Copyright 2013 Drifty (http://drifty.com/) +Copyright 2014 Drifty (http://drifty.com/) */ var IonicStartTask = require('./ionic/start').IonicStartTask, @@ -114,8 +114,8 @@ var TASKS = [ ]; Ionic = { - IONIC_DASH: 'http://apps.ionicframework.com/', - //IONIC_DASH: 'http://localhost:8000/', + //IONIC_DASH: 'http://apps.ionicframework.com/', + IONIC_DASH: 'http://localhost:8000/', IONIC_COOKIES: 'ionic.cookies', IONIC_API: 'api/v1/', _tryBuildingTask: function() { From d9781e425463bc844e0270d513a6fb520aa78650 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Thu, 22 May 2014 15:00:06 -0500 Subject: [PATCH 0116/1100] Paderp --- lib/ionic.js | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index a4f3ba0349..c95f4f6007 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -114,8 +114,8 @@ var TASKS = [ ]; Ionic = { - //IONIC_DASH: 'http://apps.ionicframework.com/', - IONIC_DASH: 'http://localhost:8000/', + IONIC_DASH: 'http://apps.ionicframework.com/', + //IONIC_DASH: 'http://localhost:8000/', IONIC_COOKIES: 'ionic.cookies', IONIC_API: 'api/v1/', _tryBuildingTask: function() { diff --git a/package.json b/package.json index 96a419daa4..c02d9aaa19 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.0.13", + "version": "1.0.14", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From b2d1cf9d2f094aac67b6e99e0fbc4605c72791b9 Mon Sep 17 00:00:00 2001 From: Mike Hartington Date: Wed, 28 May 2014 16:19:46 -0400 Subject: [PATCH 0117/1100] Added serve and template description --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 87439694e8..29122bc7b1 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,20 @@ $ sudo npm install -g ionic ## Starting an Ionic App ```bash -$ ionic start myApp +$ ionic start myApp [template] ``` +There are three choices of templates. + +* Side-Menu (sidemenu) +* Tabs (tabs) +* Blank (blank) + +## Testing in a Browser + +```bash +$ ionic serve +``` + ## Adding a platform target From 087a5d5a3bcd99fa82c3ea3830b95b6bc6464844 Mon Sep 17 00:00:00 2001 From: Ryan Schumacher Date: Wed, 11 Jun 2014 14:10:38 -0400 Subject: [PATCH 0118/1100] Update language of failure with live reload --- lib/ionic/serve.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index ac6f8129f8..e8614755a3 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -69,7 +69,7 @@ IonicServeTask.prototype._start = function(ionic) { server = tinylr(); server.listen(this.liveReloadPort, function(err) { if(err) { - ionic.fail('Unable to start server:', err); + ionic.fail('Unable to start live reload server:', err); } else { console.log('Running live reload server at', ('http://0.0.0.0:' + self.liveReloadPort).info.bold); if(self.launchBrowser) { From fd3ae5f90d389735177e481680a13da4fbf60a30 Mon Sep 17 00:00:00 2001 From: Perry Govier Date: Tue, 24 Jun 2014 10:43:34 -0500 Subject: [PATCH 0119/1100] fixing typo in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 29122bc7b1..ce6a934579 100644 --- a/README.md +++ b/README.md @@ -53,4 +53,4 @@ $ ionic run ios Ionic uses Cordova underneath, so you can also substitute Cordova commands to prepare/build/emulate/run, or to add additional plugins. -Note: we occasionally anonymous usage statistics to the Ionic team to make the tool better. +Note: we occasionally collect anonymous usage statistics to the Ionic team to make the tool better. From 361878fcf48506cf39e6caf2a81461abdae37f53 Mon Sep 17 00:00:00 2001 From: Perry Govier Date: Tue, 24 Jun 2014 10:44:53 -0500 Subject: [PATCH 0120/1100] when fixing a typo, it is often best to read the whole sentence --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ce6a934579..20031cea44 100644 --- a/README.md +++ b/README.md @@ -53,4 +53,4 @@ $ ionic run ios Ionic uses Cordova underneath, so you can also substitute Cordova commands to prepare/build/emulate/run, or to add additional plugins. -Note: we occasionally collect anonymous usage statistics to the Ionic team to make the tool better. +Note: we occasionally send anonymous usage statistics to the Ionic team to make the tool better. From 45297dcb85b9ec8ce56d4b3eb89a51e61dbd6c61 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 9 Jul 2014 11:23:33 -0500 Subject: [PATCH 0121/1100] ionic package --- lib/ionic.js | 69 +++-- lib/ionic/config.js | 10 +- lib/ionic/login.js | 70 ++--- lib/ionic/package.js | 670 ++++++++++++++++++++++++++++++------------- lib/ionic/private.js | 67 ----- lib/ionic/project.js | 20 +- lib/ionic/store.js | 55 ++++ lib/ionic/upload.js | 84 +++--- package.json | 35 ++- 9 files changed, 680 insertions(+), 400 deletions(-) delete mode 100644 lib/ionic/private.js create mode 100644 lib/ionic/store.js diff --git a/lib/ionic.js b/lib/ionic.js index c95f4f6007..6604d4a192 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -1,9 +1,9 @@ /* - _ _ -(_) (_) - _ ___ _ __ _ ___ + _ _ +(_) (_) + _ ___ _ __ _ ___ | |/ _ \| '_ \| |/ __| -| | (_) | | | | | (__ +| | (_) | | | | | (__ |_|\___/|_| |_|_|\___| http://ionicframework.com/ @@ -13,7 +13,7 @@ Licensed under the MIT license. See LICENSE For more. Copyright 2014 Drifty (http://drifty.com/) */ - + var IonicStartTask = require('./ionic/start').IonicStartTask, IonicPlatformTask = require('./ionic/platform').IonicPlatformTask, IonicRunTask = require('./ionic/run').IonicRunTask, @@ -31,25 +31,25 @@ var IonicStartTask = require('./ionic/start').IonicStartTask, colors = require('colors'), spawn = require('child_process').spawn, Q = require('q'), - settings = require('../package.json') + settings = require('../package.json'); colors.setTheme({ - silly: 'rainbow', - input: 'grey', - small: 'grey', - verbose: 'cyan', - prompt: 'grey', - info: 'white', - data: 'grey', - help: 'cyan', - warn: 'yellow', - debug: 'blue', - error: 'red' + silly: 'rainbow', + input: 'grey', + small: 'grey', + verbose: 'cyan', + prompt: 'grey', + info: 'white', + data: 'grey', + help: 'cyan', + warn: 'yellow', + debug: 'blue', + error: 'red' }); -var fs = require('fs'), +var fs = require('fs'), argv = require('optimist').argv; var TASKS = [ @@ -64,7 +64,7 @@ var TASKS = [ name: 'start', usage: 'appname', task: IonicStartTask - }, + }, { title: 'emulate', name: 'emulate', @@ -102,7 +102,6 @@ var TASKS = [ usage: '', task: IonicUploadTask }, - /* { title: 'package', name: 'package', @@ -110,16 +109,15 @@ var TASKS = [ usage: 'mode platform', task: IonicPackageTask } - */ ]; Ionic = { - IONIC_DASH: 'http://apps.ionicframework.com/', - //IONIC_DASH: 'http://localhost:8000/', - IONIC_COOKIES: 'ionic.cookies', - IONIC_API: 'api/v1/', + IONIC_DASH: 'http://apps.ionicframework.com', + //IONIC_DASH: 'http://localhost:8000', + IONIC_API: '/api/v1/', + PRIVATE_PATH: '.ionic', _tryBuildingTask: function() { - if(argv._.length == 0) { + if(argv._.length === 0) { return false; } var taskName = argv._[0]; @@ -138,7 +136,7 @@ Ionic = { var alt = t.alt[j]; if(alt === name) { return t; - } + } } } } @@ -186,23 +184,24 @@ Ionic = { return this._printGenericUsage(); } - console.log('Running', task.title.info.bold, 'task...') + console.log('\nRunning', task.title.info.bold, 'task...'); var taskObj = new task.task(); taskObj.run(this); }, fail: function(msg) { - process.stderr.write('ERROR: '); - process.stderr.write(msg.error.bold + '\nExiting.\n'); + process.stderr.write(msg.error.bold); + process.stderr.write('\n\n'); process.exit(1); }, spawnPromise: function(cmd, args, onStdOut, onStdErr) { var q = Q.defer(); + var child; console.log('\nRUNNING:'.info.bold, cmd, args.join(' ')); try { - var child = spawn(cmd, args); + child = spawn(cmd, args); } catch(e) { } child.stdout.setEncoding('utf8'); @@ -241,7 +240,7 @@ Ionic = { var tmpFolder = os.tmpdir(); var tempZipFilePath = path.join(tmpFolder, repoName + new Date().getTime() + '.zip'); - var tempZipFileStream = fs.createWriteStream(tempZipFilePath) + var tempZipFileStream = fs.createWriteStream(tempZipFilePath); var unzipRepo = function(fileName) { var readStream = fs.createReadStream(fileName); @@ -269,11 +268,11 @@ Ionic = { getLocalIps: function() { var interfaces = os.networkInterfaces(); var addresses = []; - for (k in interfaces) { - for (k2 in interfaces[k]) { + for (var k in interfaces) { + for (var k2 in interfaces[k]) { var address = interfaces[k][k2]; if (address.family == 'IPv4' && !address.internal) { - addresses.push(address.address) + addresses.push(address.address); } } } diff --git a/lib/ionic/config.js b/lib/ionic/config.js index 14ede659a1..f2474fba5f 100644 --- a/lib/ionic/config.js +++ b/lib/ionic/config.js @@ -8,7 +8,7 @@ module.exports = { CONFIG_FILE: '.ionic/ionic.config', load: function() { this.file = this.CONFIG_FILE; - if(fs.existsSync(path.join(home,this.file))) { + if(fs.existsSync(path.join(home,this.file))) { this.data = JSON.parse(fs.readFileSync(path.join(home, this.file))); } else { this.data = {}; @@ -16,7 +16,7 @@ module.exports = { return this; }, save: function() { - if(!this.data) { + if(!this.data) { return; } try { @@ -28,7 +28,7 @@ module.exports = { } fs.writeFileSync(p, JSON.stringify(this.data, null, 2)); } catch(e) { - console.error('Unable to save settings file:', e); + console.error( ('Unable to save settings file: ' + e).red ); } }, get: function(k) { @@ -39,7 +39,7 @@ module.exports = { this.data = {}; } this.data[k] = v; - + this.save(); - } + } }; diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 69faedac1d..0c2290789b 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -3,10 +3,10 @@ var fs = require('fs'), argv = require('optimist').argv, prompt = require('prompt'), IonicProject = require('./project'), + IonicStore = require('./store').IonicStore, IonicTask = require('./task').IonicTask; -var IonicLoginTask = function() { -} +var IonicLoginTask = function() {}; IonicLoginTask.HELP_LINE = 'Login to the Ionic Platform'; @@ -14,9 +14,10 @@ IonicLoginTask.prototype = new IonicTask(); IonicLoginTask.prototype.run = function(ionic, callback) { var self = this; + var schema = [{ name: 'email', - pattern: /^[A-z0-9!#$%&'*+\/=?^_{|}~-]+(?:\.[A-z0-9!#$%&'*+\/=?^_{|}~-]+)*@(?:[A-z0-9](?:[A-z0-9-]*[A-z0-9])?\.)+[A-z0-9](?:[A-z0-9-]*[A-z0-9])?$/, + pattern: /^[A-z0-9!#$%&'*+\/=?\^_{|}~\-]+(?:\.[A-z0-9!#$%&'*+\/=?\^_{|}~\-]+)*@(?:[A-z0-9](?:[A-z0-9\-]*[A-z0-9])?\.)+[A-z0-9](?:[A-z0-9\-]*[A-z0-9])?$/, message: 'Email for your Ionic Platform login', required: true }, { @@ -31,20 +32,15 @@ IonicLoginTask.prototype.run = function(ionic, callback) { if(argv._.length >= 2 && schema[0].pattern.test(argv._[1])) { this.email = argv._[1].toLowerCase(); schema.shift(); - } - // Assume project email is login email if it exists - else if(project.get('email')) { - this.email = project.get('email'); - schema.shift(); } - console.log('To continue, please login to your Ionic account.\nDon\'t have one? Create a one here: ' + (ionic.IONIC_DASH + 'signup').info.bold + '\n'); + console.log('\nTo continue, please login to your Ionic account.'.bold.green); + console.log('Don\'t have one? Create a one at: '.grey + (ionic.IONIC_DASH + '/signup').info.bold + '\n'); prompt.override = argv; prompt.start(); - prompt.get(schema, function (err, result) { if(err) { ionic.fail('Error logging in: ' + err); @@ -55,48 +51,44 @@ IonicLoginTask.prototype.run = function(ionic, callback) { } self.password = result.password; - if(!project.get('email')) { - project.set('email'); - } - var jar = request.jar(); request({ - url: ionic.IONIC_DASH + 'login', + url: ionic.IONIC_DASH + '/login', jar: jar - }, + }, function(err, response, body) { - if(err || jar.cookies.length == 0) { + if(err || jar.cookies.length === 0) { ionic.fail('Error logging in: ' + err); } request({ method: 'POST', - url: ionic.IONIC_DASH + 'login', - jar: jar, + url: ionic.IONIC_DASH + '/login', + jar: jar, form: { username: self.email, password: self.password, csrfmiddlewaretoken: jar.cookies[0].value } }, - function (err, response, body) { + function (err, response, body) { if(err) { ionic.fail('Error logging in: ' + err); } // Should be a 304 redirect status code if correct if(response.statusCode == 200) { - ionic.fail('Email or Password incorrect. Please visit '+ ionic.IONIC_DASH +' for help.') + ionic.fail('Email or Password incorrect. Please visit '+ ionic.IONIC_DASH +' for help.'); } - - var err = fs.writeFileSync(ionic.IONIC_COOKIES, JSON.stringify(jar, null, 2)); - if(err) { - ionic.fail('Error writing ' + ionic.IONIC_COOKIES + ': ' + err); - } - - console.log('Logged in! :)'); + + self.cookieData.set(ionic.IONIC_DASH, jar); + self.cookieData.save(); + + console.log('Logged in! :)'.green); if(callback) { callback(jar); + project.set('email', self.email); + project.save(); } }); }); @@ -104,22 +96,20 @@ IonicLoginTask.prototype.run = function(ionic, callback) { }; IonicLoginTask.prototype.get = function(ionic, callback) { - var self = this; - - if(fs.existsSync(ionic.IONIC_COOKIES)) { - var jar = JSON.parse(fs.readFileSync(ionic.IONIC_COOKIES)); - if(jar.cookies && jar.cookies.length > 0) { - for(i in jar.cookies) { - var cookie = jar.cookies[i]; - if(cookie.name == "sessionid" && new Date(cookie.expires) > new Date()) { - callback(jar); - return; - } + this.cookieData = new IonicStore('cookies'); + var jar = this.cookieData.get(ionic.IONIC_DASH); + + if(jar && jar.cookies && jar.cookies.length) { + for(var i in jar.cookies) { + var cookie = jar.cookies[i]; + if(cookie.name == "sessionid" && new Date(cookie.expires) > new Date()) { + callback(jar); + return; } } } this.run(ionic, callback); -} +}; exports.IonicLoginTask = IonicLoginTask; diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 68f9629cb9..9ec8f31a94 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -1,246 +1,530 @@ var fs = require('fs'), + http = require('http'), path = require('path'), parseUrl = require('url').parse, + spawn = require("child_process").spawn, argv = require('optimist').argv, prompt = require('prompt'), shelljs = require('shelljs/global'), - FormData = require('form-data'), + FormData = require('form-data'), IonicProject = require('./project'), - IonicProjectPrivate = require('./private'), + IonicStore = require('./store').IonicStore, IonicTask = require('./task').IonicTask, IonicUploadTask = require('./upload').IonicUploadTask, IonicLoginTask = require('./login').IonicLoginTask; -var IonicPackageTask = function() { -} +var IonicPackageTask = function() {}; -IonicPackageTask.HELP_LINE = 'Package an Ionic project using the Ionic Platform Build service (requires login)'; +IonicPackageTask.HELP_LINE = 'Package an app using the Ionic Platform Build service (requires login)'; IonicPackageTask.prototype = new IonicTask(); + IonicPackageTask.prototype._printUsage = function() { - process.stderr.write('\nUsage: ionic package mode(debug|release) platform [more platforms,...]\n'); -} + process.stderr.write('\nUsage:\nionic package mode[debug|release] platform[ios,android]\n'); +}; + IonicPackageTask.prototype.run = function(ionic) { + var self = this; + self.ionic = ionic; + + self.project = IonicProject.load(); + + if(argv['clear-signing'] || argv.c) { + self.clearSigning(); + return; + } + + self.loadProject(); + + var login = new IonicLoginTask(); + login.get(self.ionic, function(jar) { + self.jar = jar; + + self.loadAppSigning(function(){ + self.packagePlatforms(self.platforms); + }); + }); + +}; + + +IonicPackageTask.prototype.clearSigning = function() { + var self = this; + console.log('Clearing app signing and credential information...'.yellow); + + var appId = self.project.get('app_id'); + + if(!appId) { + self.ionic.fail('App Id is not known'); + } + + var login = new IonicLoginTask(); + login.get(self.ionic, function(jar) { + var options = { + url: self.ionic.IONIC_DASH + self.ionic.IONIC_API + 'app/' + appId + '/signing/clear', + headers: { + cookie: jar.cookies.map(function (c) { + return c.name + "=" + encodeURIComponent(c.value); + }).join("; ") + } + }; + + request(options, function(err, response, body) { + if(err) { + self.ionic.fail("Error clearing app signing: " + err); + } + console.log( ('App (' + appId + ') signing and credential information cleared\n').green ); + }); + }); +}; + + +IonicPackageTask.prototype.loadProject = function() { + console.log( ("Loading " + this.project.get('name')).bold.yellow ); + if(argv._.length < 3) { IonicPackageTask.prototype._printUsage(); - ionic.fail('No platforms or build mode specified, exiting.'); + this.ionic.fail('No platforms or build mode specified.'); } - var mode = argv._[1].toLowerCase(); - if(mode != 'debug' && mode != 'release') { + this.mode = argv._[1].toLowerCase(); + if(this.mode != 'debug' && this.mode != 'release') { IonicPackageTask.prototype._printUsage(); - ionic.fail('Package build mode must be debug or release, exiting.'); + this.ionic.fail('Package build mode must be "debug" or "release".'); } - var platforms = argv._.slice(2); + this.platforms = argv._.slice(2); - if(platforms.length < 1) { - ionic.fail('No platforms specified, exiting.'); + if(this.platforms.length < 1) { + this.ionic.fail('No platforms specified.'); } +}; - var upload = new IonicUploadTask(); - upload.run(ionic, function() { - var login = new IonicLoginTask(); - login.get(ionic, function(jar) { +IonicPackageTask.prototype.loadPlugins = function() { + var self = this; + var parseString = require('xml2js').parseString; + var cp = spawn("cordova", ['plugins']); - var project = IonicProject.load(); - var projectPrivate = IonicProjectPrivate.load(project.get('app_id')); + cp.stdout.on("data", function (c) { + self.plugins = []; - console.log('Grabbing plugins...'); - var plugins = []; - var pluginExec = exec('cordova plugins'); - if(pluginExec.code != 0 ) { - process.stderr.write('Unable to read cordova plugin list. Please see console for more info.\n'); - } else { - pluginExec.output = pluginExec.output.replace(/'/g, '"'); - plugins = JSON.parse(pluginExec.output); - } + var pluginNames = JSON.parse(c.toString().replace(/'/g, '"')); + + for(var x=0; x= 0 && d.status < 3) { + // still queued/building + attempt++; + + if(attempt > 60) { + ionic.fail("\nUnable to receive build status"); + } + + setTimeout(function(){ + pingBuildStatus(ionic, requestOptions, platform, attempt); + }, 5000); + + } else if(d.status == 4) { + console.log("Use 'ionic package --clear-signing' to clear app signing and credential data if needed.".bold.red); + ionic.fail("Build failed"); + } else if(d.status == 3) { + downloadBuildPackage(platform, d); + } else { + ionic.fail("\nError receiving build status"); + } + + } catch(e) { + ionic.fail("\nError pinging build status: " + e); + } + + }); +} + + +function downloadBuildPackage(platform, data) { + console.log( ('\n\n' + platform + ' build complete, downloading package...').bold.grey ); + + var dirPath = path.resolve('./packages'); + var filePath = path.resolve(dirPath + '/' + data.package_filename); + + if(!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath); + } + + var params = parseUrl(data.package_url); + + var file = fs.createWriteStream(filePath); + var request = http.get({hostname: params.hostname, path: params.path, port: params.port, protocol: 'http:'}, function(response) { + response.pipe(file); + file.on('finish', function() { + file.close(function(){ + console.log( ('Saved ' + platform + ' package: ' + filePath + '\n').bold.green ); + }); + }); + }).on('response', function(res){ + + var ProgressBar = require('progress'); + var bar = new ProgressBar('[:bar] :percent :etas', { + complete: '=', + incomplete: ' ', + width: 30, + total: parseInt(res.headers['content-length'], 10) + }); + + res.on('data', function (chunk) { + bar.tick(chunk.length); + }); + + res.on('end', function () { + console.log('\n'); + }); + + }).on('error', function(err) { + fs.unlink(filePath); + console.error( (err).red ); + }); +} + + +function fileExists(filePath) { + // check if a file exists with a relative path or absolute path + filePath = filePath.replace(/\\ /g, ' ').trim(); + return fs.existsSync(path.resolve(filePath)); +} + exports.IonicPackageTask = IonicPackageTask; diff --git a/lib/ionic/private.js b/lib/ionic/private.js deleted file mode 100644 index 68a83c7952..0000000000 --- a/lib/ionic/private.js +++ /dev/null @@ -1,67 +0,0 @@ -var fs = require('fs'), - path = require('path'), - ionic = require('../ionic'); - -var home = process.env.HOME || process.env.USERPROFILE || process.env.HOMEPATH; - -module.exports = { - PRIVATE_PATH: '.ionic/', - PRIVATE_EXT: '.project', - PRIVATE_DEFAULT: { - ios_certificate: '', - ios_certificate_password: '', - ios_profile: '', - android_keystore: '', - android_keystore_alias: '', - android_keystore_password: '', - android_key_password: '' - }, - load: function(app_id) { - if(app_id) { - this.file = this.PRIVATE_PATH+app_id+this.PRIVATE_EXT; - if(fs.existsSync(path.join(home,this.file))) { - this.data = JSON.parse(fs.readFileSync(path.join(home, this.file))); - } else { - this.data = this.PRIVATE_DEFAULT; - } - } - return this; - }, - create: function() { - this.data = this.PRIVATE_DEFAULT; - return this; - }, - save: function(app_id) { - if(!this.data) { - console.trace(); - console.error('This should never happen!'); - } - if(app_id) { - this.file = this.PRIVATE_PATH+app_id+this.PRIVATE_EXT; - try { - fs.writeFileSync(path.join(home,this.file), JSON.stringify(this.data, null, 2)); - } catch(e) { - console.error('Unable to save private settings file:', e); - } - } - }, - get: function(k) { - if(k) { - return this.data[k]; - } else { - return this.data; - } - }, - set: function(k, v) { - if(!this.data) { - this.data = PRIVATE_DEFAULT; - } - this.data[k] = v; - }, - remove: function(k) { - if(!this.data) { - this.data = PRIVATE_DEFAULT; - } - this.data[k] = ''; - } -}; diff --git a/lib/ionic/project.js b/lib/ionic/project.js index ebf6130b4d..5b58da3c72 100644 --- a/lib/ionic/project.js +++ b/lib/ionic/project.js @@ -8,24 +8,20 @@ module.exports = { name: '', email: '', app_id: '', - package_name: '', }, load: function() { - this.file = this.PROJECT_FILE; + this.file = this.PROJECT_FILE; + if(fs.existsSync(this.file)) { - this.data = JSON.parse(fs.readFileSync(this.file)) + this.data = JSON.parse(fs.readFileSync(this.file)); + } else { if(fs.existsSync('www')) { var parts = path.resolve('./').split(path.sep); var dirname = parts[parts.length-1]; - console.log('We have a www folder in the', dirname, 'project'); this.create(dirname); this.save('./'); } - /* - Ionic.fail('Could not find your ' + this.file + ' file!'+ - ' Please run this command in the root of your ionic project.'); - */ } return this; }, @@ -38,9 +34,9 @@ module.exports = { return this; }, save: function(targetPath) { - if(!this.data) { + if(!this.data) { console.trace(); - console.error('This should never happen!'); + console.error('This should never happen!'); } try { fs.writeFileSync((targetPath?targetPath + '/':'') + this.PROJECT_FILE, JSON.stringify(this.data, null, 2)); @@ -60,13 +56,13 @@ module.exports = { }, set: function(k, v) { if(!this.data) { - this.data = PROJECT_DEFAULT; + this.data = this.PROJECT_DEFAULT; } this.data[k] = v; }, remove: function(k) { if(!this.data) { - this.data = PROJECT_DEFAULT; + this.data = this.PROJECT_DEFAULT; } this.data[k] = ''; } diff --git a/lib/ionic/store.js b/lib/ionic/store.js new file mode 100644 index 0000000000..752864fe90 --- /dev/null +++ b/lib/ionic/store.js @@ -0,0 +1,55 @@ +var fs = require('fs'), + path = require('path'); + + +var IonicStore = function(fileName) { + this.data = {}; + + if(!fileName) return this; + + this.fileName = fileName; + if(fileName.indexOf('.') < 0) { + this.fileName += '.data' + } + + this.homeDir = process.env.HOME || process.env.USERPROFILE || process.env.HOMEPATH; + + this.privateDir = path.join(this.homeDir, '.ionic'); + + if(!fs.existsSync(this.privateDir)) { + fs.mkdirSync(this.privateDir); + } + + this.filePath = path.join(this.privateDir, this.fileName); + + try { + this.data = JSON.parse(fs.readFileSync(this.filePath)); + } catch(e) {} + + return this; +}; + +IonicStore.prototype.get = function(k) { + if(k) { + return this.data[k]; + } + return this.data; +}; + +IonicStore.prototype.set = function(k, v) { + this.data[k] = v; +}; + +IonicStore.prototype.remove = function(k) { + delete this.data[k]; +}; + +IonicStore.prototype.save = function() { + try { + fs.writeFileSync(this.filePath, JSON.stringify(this.data, null, 2)); + } catch(e) { + console.error('Unable to save ionic data:', this.filePath, e); + } +}; + +exports.IonicStore = IonicStore; diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index 9af0f1a85e..32bb5ead03 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -10,8 +10,7 @@ var fs = require('fs'), IonicLoginTask = require('./login').IonicLoginTask, Q = require('q'); -var IonicUploadTask = function() { -} +var IonicUploadTask = function() {}; IonicUploadTask.HELP_LINE = 'Upload an Ionic project to the Ionic Platform (requires login)'; @@ -23,12 +22,11 @@ IonicUploadTask.prototype.run = function(ionic, callback) { var login = new IonicLoginTask(); login.get(ionic, function(jar) { - - console.log('Zipping...'); + var TEMP_FILENAME = 'www.zip'; IonicStats.t('upload', {}); - var zip = fs.createWriteStream('www.zip'); + var zip = fs.createWriteStream(TEMP_FILENAME); var archive = archiver('zip'); archive.pipe(zip); @@ -44,64 +42,72 @@ IonicUploadTask.prototype.run = function(ionic, callback) { }); zip.on('close', function() { - console.log('Done'); if(project.get('app_id')) { - console.log('Uploading app (id: ' + project.get('app_id') + ')'); + console.log( ('\nUploading app (' + project.get('app_id') + ')...').bold.grey ); } else { - console.log('Uploading app.'); + console.log('\nUploading new app...'.bold.grey); } var form = new FormData(); form.append('name', project.get('name')); form.append('csrfmiddlewaretoken', jar.cookies[0].value); - form.append('app_file', fs.createReadStream(path.resolve('www.zip')), {filename: 'www.zip', contentType: 'application/zip'}); - // form.append('app_file', zip.toBuffer(), {filename: 'www.zip', contentType: 'application/zip'}); + form.append('app_file', fs.createReadStream(path.resolve(TEMP_FILENAME)), {filename: TEMP_FILENAME, contentType: 'application/zip'}); - var url = ionic.IONIC_DASH + 'projects/' + project.get('app_id'); + var url = ionic.IONIC_DASH + ionic.IONIC_API + 'app/upload/' + project.get('app_id'); var params = parseUrl(url); form.submit({ + protocol: params.protocol, hostname: params.hostname, port: params.port, path: params.path, headers: form.getHeaders({ cookie: jar.cookies.map(function (c) { - return c.name + "=" + encodeURIComponent(c.value) + return c.name + "=" + encodeURIComponent(c.value); }).join("; ") }) }, function(err, response) { - rm('-f', 'www.zip'); + + rm('-f', TEMP_FILENAME); if(err) { ionic.fail("Error uploading: " + err); } - if(response.statusCode == 302) { - if(response.headers.location.indexOf('login?next=') >= 0) { - console.error('Session expired. Please log in again and run this command again.'); - q.reject('not_logged_in'); - return; - } - console.log('Done!'.info.bold); - - var redirectPath = parseUrl(response.headers.location); - project.set('app_id', redirectPath.pathname.match(/^\/projects\/(\w+)\/?$/)[1]); - project.save(); - - if(callback) { - callback(); - }; - } else if(response.statusCode === 200) { - console.log('Got response', response.statusCode); - callback && callback(); - } else { - if(response.headers['content-type'] == 'application/json') { - response.on('data', function(chunk) { - console.log(String(chunk)); - }); + response.setEncoding('utf8'); + response.on("data", function(data) { + try { + var d = JSON.parse(data); + if(d.errors && d.errors.length) { + for (var j = 0; j < d.errors.length; j++) { + console.log( (d.errors[j]).bold.error ); + } + q.reject('upload_error'); + ionic.fail('Unable to upload app'); + } + + if(response.statusCode == 200) { + // Success + project.set('app_id', d.app_id); + project.save(); + console.log( ('Successfully uploaded (' + project.get('app_id') + ')\n').bold.grey ); + + if(callback) { + callback(); + } + + } else if(response.statusCode == 401) { + console.error( ('Session expired. Please log in again and run this command again.').red ); + q.reject('not_logged_in'); + + } else { + q.reject('upload_error'); + } + + } catch(parseEx) { + q.reject('upload_error'); + ionic.fail('Error upload response: ' + parseEx); } - console.error('Unable to upload. HTTP response:', response.statusCode); - q.reject(response); - } + }); q.resolve(callback); }); diff --git a/package.json b/package.json index c02d9aaa19..6c196a4b7f 100644 --- a/package.json +++ b/package.json @@ -19,25 +19,42 @@ "type": "git", "url": "https://github.com/driftyco/ionic-cli.git" }, - "author": "Max Lynch and Peter Collins ", + "contributors": [ + { + "name": "Max Lynch", + "email": "max@drifty.com", + "web": "https://twitter.com/maxlynch" + }, + { + "name": "Peter Collins", + "email": "peter@drifty.com", + "web": "https://twitter.com/SomethingNew2_0" + }, + { + "name": "Adam Bradley", + "web": "https://twitter.com/adamdbradley" + } + ], "license": "MIT", "dependencies": { "archiver": "0.5.1", + "colors": "^0.6.2", + "connect": "^2.14.5", + "connect-livereload": "^0.4.0", "event-stream": "3.0.x", "form-data": "~0.1.0", "ncp": "0.4.2", + "npm": "^1.4.6", + "open": "0.0.5", "optimist": "0.6.0", "prompt": "0.2.12", + "progress": "1.1.7", + "q": "^1.0.1", "request": "2.27.0", "shelljs": "0.2.6", - "unzip": "0.1.9", - "colors": "^0.6.2", - "q": "^1.0.1", - "npm": "^1.4.6", - "connect": "^2.14.5", - "open": "0.0.5", "tiny-lr-fork": "0.0.5", - "connect-livereload": "^0.4.0", - "vinyl-fs": "^0.1.4" + "vinyl-fs": "^0.1.4", + "unzip": "0.1.9", + "xml2js": "^0.4.4" } } From 2ec45308e648ceb879c7b7d27925d98c2d03714b Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Thu, 10 Jul 2014 09:45:15 -0500 Subject: [PATCH 0122/1100] Master --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6c196a4b7f..bbd034e4ed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.0.14", + "version": "1.0.15", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From f11a774a65b5d7728dc12a302cf1ed1f3e005491 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 11 Jul 2014 09:21:00 -0500 Subject: [PATCH 0123/1100] packaging an app --- README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 20031cea44..5b2be158dc 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ There are three choices of templates. * Side-Menu (sidemenu) * Tabs (tabs) * Blank (blank) - + ## Testing in a Browser ```bash @@ -49,8 +49,22 @@ $ ionic emulate ios ```bash $ ionic run ios + + +## Packaging an app (beta) + +Using Ionic's service, you can compile and package your project into an app-store ready app without +requiring native SDKs on your machine. + +```bash +$ ionic package debug android ``` +The third argument can be either `debug` or `release`, and the last argument can be either `android` or `ios`. + + +## Cordova Commands + Ionic uses Cordova underneath, so you can also substitute Cordova commands to prepare/build/emulate/run, or to add additional plugins. Note: we occasionally send anonymous usage statistics to the Ionic team to make the tool better. From 7d227350897d4fd49d06d92d79237baa05bcaa80 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 11 Jul 2014 09:26:41 -0500 Subject: [PATCH 0124/1100] fix broken md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5b2be158dc..158d3afd24 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ $ ionic emulate ios ```bash $ ionic run ios +``` ## Packaging an app (beta) From b58092fe6551e427cbfa6d0b73b4cf2668c5fd89 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 11 Jul 2014 13:15:09 -0500 Subject: [PATCH 0125/1100] load plugins through package.json files --- lib/ionic/package.js | 54 +++++++++++++++----------------------------- lib/ionic/upload.js | 7 +++++- package.json | 5 ++-- 3 files changed, 26 insertions(+), 40 deletions(-) diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 9ec8f31a94..17321fed5a 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -104,44 +104,20 @@ IonicPackageTask.prototype.loadProject = function() { IonicPackageTask.prototype.loadPlugins = function() { - var self = this; - var parseString = require('xml2js').parseString; - var cp = spawn("cordova", ['plugins']); - - cp.stdout.on("data", function (c) { - self.plugins = []; - - var pluginNames = JSON.parse(c.toString().replace(/'/g, '"')); + this.plugins = []; - for(var x=0; x Date: Sat, 12 Jul 2014 11:54:12 -0500 Subject: [PATCH 0126/1100] update config.xml from cmd args --- lib/ionic/package.js | 7 ++- lib/ionic/start.js | 127 ++++++++++++++++++++++++++++--------------- package.json | 5 +- 3 files changed, 91 insertions(+), 48 deletions(-) diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 17321fed5a..745f2be9c0 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -82,7 +82,8 @@ IonicPackageTask.prototype.clearSigning = function() { IonicPackageTask.prototype.loadProject = function() { - console.log( ("Loading " + this.project.get('name')).bold.yellow ); + var appName = this.project.get('name') || "app"; + console.log( ("Loading " + appName).bold.yellow ); if(argv._.length < 3) { IonicPackageTask.prototype._printUsage(); @@ -353,8 +354,10 @@ IonicPackageTask.prototype.submitPostRequest = function(form, platform) { if(d.errors && d.errors.length) { for (var j = 0; j < d.errors.length; j++) { - process.stderr.write(d.errors[j] + '\n'); + console.log( d.errors[j].bold.error ); } + console.log(''); + } else if(d.build_status_url) { process.stderr.write('.'); setTimeout(function(){ diff --git a/lib/ionic/start.js b/lib/ionic/start.js index e21c5fb5b7..2f10ba1771 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -9,6 +9,7 @@ var fs = require('fs'), colors = require('colors'), spawn = require('child_process').spawn, Q = require('q'), + xml2js = require('xml2js'), IonicProject = require('./project'), IonicTask = require('./task').IonicTask; IonicStats = require('./stats').IonicStats; @@ -28,8 +29,7 @@ fs.mkdirParent = function(dirPath, mode, callback) { }); }; -var IonicStartTask = function() { -} +var IonicStartTask = function() {}; // The URL for the cordova wrapper project IonicStartTask.WRAPPER_REPO_NAME = 'ionic-app-base'; @@ -39,23 +39,30 @@ IonicStartTask.HELP_LINE = 'Start a new Ionic project with the given name.'; IonicStartTask.prototype = new IonicTask(); IonicStartTask.prototype._printUsage = function() { - process.stderr.write('\nUsage: ionic start appName [starterProject(blank,tabs,sidemenu)]\n'); + process.stderr.write('\nUsage: ionic start myapp\n'); + process.stderr.write('\nCommand-line Flags/Options:\n\n') + process.stderr.write(' -t, --template ...... starter template to use (tabs, sidemenu, blank)\n'); + process.stderr.write(' -a, --app ................... your app\'s name (Use quotes around the name)\n'); + process.stderr.write(' -p, --package ........... package name, such as "com.mycompany.myapp"\n'); }; IonicStartTask.prototype.run = function(ionic) { if(argv._.length < 2) { - ionic.fail('No app name specified, exiting.'); + this._printUsage(); + ionic.fail('\nInvalid command'); } - // Grab the name of the app - this.appName = argv._[1]; + // Grab the app's relative directory name + this.appDirectory = argv._[1]; - // Grab the target path from the command line, or use this as the app name - var targetPathName = this.appName; + // Grab the name of the app from -a or --app. Defaults to appDirectory if none provided + this.appName = argv.a || argv.app || this.appDirectory; + this.packageName = argv.p || argv.package; - var starterProject = argv._[2] || 'tabs'; + // start project template can come from cmd line args -t, --template, or the 3rd arg, and defaults to tabs + var starterProject = argv.t || argv.template || argv._[2] || 'tabs'; - this.targetPath = path.resolve(this.appName); + this.targetPath = path.resolve(this.appDirectory); // Make sure to create this, or ask them if they want to override it if(this._checkTargetPath() === false) { @@ -68,7 +75,7 @@ IonicStartTask.prototype.run = function(ionic) { fs.mkdirSync(this.targetPath); this._fetchAndWriteSeed(starterProject.toLowerCase()); - this._writeConfig(ionic); + this._writeProjectFile(ionic); }; @@ -102,18 +109,55 @@ IonicStartTask.prototype._installCordovaPlugins = function() { console.log('Initializing cordova project.'.info.bold); exec('cordova plugin add org.apache.cordova.device && ' + 'cordova plugin add org.apache.cordova.console && ' + - 'cordova plugin add https://github.com/driftyco/ionic-plugins-keyboard' - , function(err, stdout, stderr) { - if(err) { - q.reject(stderr); - } else { - q.resolve(stdout); - } + 'cordova plugin add https://github.com/driftyco/ionic-plugins-keyboard', + function(err, stdout, stderr) { + if(err) { + q.reject(stderr); + } else { + q.resolve(stdout); + } }); return q.promise; }; +IonicStartTask.prototype._updateConfigXml = function() { + var self = this; + console.log('Update config.xml'.info.bold); + + try { + var configXmlPath = self.targetPath + '/config.xml'; + var configString = fs.readFileSync(configXmlPath, { encoding: 'utf8' }); + + var parseString = xml2js.parseString; + parseString(configString, function (err, jsonConfig) { + if(err) { + Ionic.fail('Error parsing config.xml: ' + err); + } + + if(!self.packageName) { + var packageName = self.appDirectory + Math.round((Math.random() * 899999) + 100000); + self.packageName = 'com.ionicframework.' + packageName.replace(/\./g, ''); + } + + jsonConfig.widget.$.id = self.packageName.replace(/ /g, '').replace(/-/g, '').replace(/_/g, '').toLowerCase().trim(); + jsonConfig.widget.name = [ self.appName ]; + + var xmlBuilder = new xml2js.Builder(); + configString = xmlBuilder.buildObject(jsonConfig); + + fs.writeFile(configXmlPath, configString, function(err) { + if(err) { + Ionic.fail('Error saving config.xml file: ' + err); + } + }); + }); + + } catch(e) { + Ionic.fail('Error updating config.xml file: ' + e); + } +}; + IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { var self = this; @@ -137,34 +181,29 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { var pluginQ = Q.defer(); // Install dependencies for the starter project - //Ionic.spawnPromise('npm', ['install', '--quiet']).then(function() { - // Move the content of this repo into the www folder - cp('-Rf', self.targetPath + '/' + repoFolderName + '/.', 'www'); + // Move the content of this repo into the www folder + cp('-Rf', self.targetPath + '/' + repoFolderName + '/.', 'www'); - // Copy the root config file to the www folder - // cp(self.targetPath + '/config.xml', 'www'); + // Clean up start template folder + rm('-rf', self.targetPath + '/' + repoFolderName + '/'); - // Clean up start template folder - rm('-rf', self.targetPath + '/' + repoFolderName + '/'); + // update the config.xml file from cmd line args + self._updateConfigXml(); - self._installCordovaPlugins().then(function(output) { - pluginQ.resolve(stdout); - }, function(err) { - pluginQ.reject(err); - }); - - pluginQ.promise.then(function() { - self._printQuickHelp(self.appName); - }, function(err) { - Ionic.fail('Unable to add plugins. Perhaps your version of Cordova is too old. Try updating (npm install -g cordova), removing this project folder, and trying again.'); - }); + self._installCordovaPlugins().then(function(output) { + pluginQ.resolve(stdout); + }, function(err) { + pluginQ.reject(err); + }); - IonicStats.t('start', {}); - /* - }).fail(function(err, stderr) {; - Ionic.fail('Unable to install Ionic dependencies. Please make sure you\'ve installed cordova, and gulp globally by running:\nsudo npm install -g cordova gulp. Error:\n'.error.bold + err + '\n' + stderr); + pluginQ.promise.then(function() { + self._printQuickHelp(self.appDirectory); + }, function(err) { + Ionic.fail('Unable to add plugins. Perhaps your version of Cordova is too old. Try updating (npm install -g cordova), removing this project folder, and trying again.'); }); - */ + + IonicStats.t('start', {}); + }).catch(function(err) { console.error('Error: Unable to fetch project: HTTP:'.error.bold, err.statusCode, err); console.error('Valid project types are blank, tabs, or sidemenu (or see more on our starter page: http://ionicframework.com/getting-started/)'.error.bold); @@ -191,13 +230,13 @@ IonicStartTask.prototype._printQuickHelp = function(projectDirectory) { console.log('\n\nFor more help, visit the Ionic docs:', 'http://ionicframework.com/docs'.info.bold); }; -IonicStartTask.prototype._writeConfig = function(ionic) { - var project = IonicProject.create(); +IonicStartTask.prototype._writeProjectFile = function(ionic) { + var project = IonicProject.create(); project.set('name', this.appName); project.save(this.targetPath); }; -IonicStartTask.prototype._checkTargetPath = function() { +IonicStartTask.prototype._checkTargetPath = function() { if(fs.existsSync(this.targetPath)) { process.stderr.write('The directory '.error.bold + this.targetPath + ' already exists, please remove it if you would like to create a new ionic project there.\n'.error.bold); return false; diff --git a/package.json b/package.json index 9845a893ef..e70bf44935 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.0.16", + "version": "1.0.17", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -53,7 +53,8 @@ "request": "2.27.0", "shelljs": "0.2.6", "tiny-lr-fork": "0.0.5", + "unzip": "0.1.9", "vinyl-fs": "^0.1.4", - "unzip": "0.1.9" + "xml2js": "^0.4.4" } } From 53a866ba01855d085210f34720a3581003f1318f Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sat, 12 Jul 2014 18:33:31 -0500 Subject: [PATCH 0127/1100] start cmd line options --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 158d3afd24..d18c0c0637 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,18 @@ $ sudo npm install -g ionic ```bash $ ionic start myApp [template] ``` -There are three choices of templates. + +There are three choices of templates: * Side-Menu (sidemenu) * Tabs (tabs) * Blank (blank) + +Command-line flags/options: + + -a, --app ................... your app's name (Use quotes around the name) + -p, --package ........... package name, such as "com.mycompany.myapp" + ## Testing in a Browser From 6fad60218f1f433953024e83d7d4bfcd693d19dd Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 18 Jul 2014 11:39:02 -0500 Subject: [PATCH 0128/1100] package improvements, added cmd line opts --- README.md | 2 +- lib/ionic.js | 215 +++++++++++++++++++++++++++----- lib/ionic/build.js | 14 +-- lib/ionic/emulate.js | 14 +-- lib/ionic/login.js | 183 +++++++++++++++------------ lib/ionic/package.js | 281 +++++++++++++++++++++++++----------------- lib/ionic/platform.js | 45 ++++--- lib/ionic/project.js | 3 +- lib/ionic/run.js | 14 +-- lib/ionic/serve.js | 46 ++++--- lib/ionic/start.js | 25 ++-- lib/ionic/store.js | 2 +- lib/ionic/upload.js | 8 +- package.json | 2 +- 14 files changed, 538 insertions(+), 316 deletions(-) diff --git a/README.md b/README.md index d18c0c0637..09bcb96306 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Ionic-Cli ========= -The Ionic Framework command line utility makes it easy to start, build, run, and emulate [Ionic](http://ionicframework.com/) apps. In the future, it will also have support for our mobile development services and tools that make Ionic even more powerful. +The Ionic Framework command line utility makes it easy to start, build, run, and emulate [Ionic](http://ionicframework.com/) apps. In the future, it will also have support for our mobile development services and tools that make Ionic even more powerful. Use `ionic --help` for detailed task information. ## Installing diff --git a/lib/ionic.js b/lib/ionic.js index 6604d4a192..96d419a3fd 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -53,61 +53,109 @@ var fs = require('fs'), argv = require('optimist').argv; var TASKS = [ + { + title: 'start', + name: 'start', + summary: 'Starts a new Ionic project in the specified PATH', + args: { + '': 'directory for the new project', + '[TEMPLATE]': 'Starter template to use (tabs, sidemenu, blank)' + }, + options: { + '--app-name|-a': 'Human readable app\'s name (Use quotes around the name)', + '--id|-i': 'Package name set as config' + }, + task: IonicStartTask + }, { title: 'serve', name: 'serve', - usage: '', + summary: 'Start a local development server for app dev and testing', + args: { + '[http-port]': '', + '[livereload-port]': '' + }, + options: { + '--nobrowser|-b': 'Disable auto browser launch', + '--nolivereload|-r': 'Do not start live reload' + }, task: IonicServeTask }, { - title: 'start', - name: 'start', - usage: 'appname', - task: IonicStartTask + title: 'platform', + name: 'platform', + summary: 'Add platform target for building an Ionic app', + args: { + '': '' + }, + task: IonicPlatformTask }, { title: 'emulate', name: 'emulate', - usage: 'emulate', + summary: 'Emulate an Ionic project on a simulator or emulator', + args: { + '': '' + }, task: IonicEmulateTask }, { title: 'run', name: 'run', - usage: 'run', + summary: 'Run an ionic project on a connected device', + args: { + '': '' + }, task: IonicRunTask }, { title: 'build', name: 'build', - usage: 'build', + summary: 'Locally build an ionic project for a given platform', + args: { + '': '' + }, task: IonicBuildTask }, { - title: 'platform', - name: 'platform', - usage: 'platform', - task: IonicPlatformTask - }, - { - title: 'login', - name: 'login', - usage: '[email]', - task: IonicLoginTask + title: 'package', + name: 'package', + alt: ['pack'], + summary: 'Package an app using the Ionic Build service', + args: { + '': '"ios" or "android"', + '': '"debug" or "release"' + }, + options: { + '--android-keystore-file|-k': 'Android keystore file', + '--android-keystore-alias|-a': 'Android keystore alias', + '--android-keystore-password|-w': 'Android keystore password', + '--android-key-password|-r': 'Android key password', + '--ios-certificate-file|-c': 'iOS certificate file', + '--ios-certificate-password|-d': 'iOS certificate password', + '--ios-profile-file|-f': 'iOS profile file', + '--output|-o': 'Path to save the packaged app', + '--clear-signing|-l': 'Clear out all signing data from Ionic server', + '--email|-e': 'Ionic account email', + '--password|-p': 'Ionic account password' + }, + task: IonicPackageTask }, { title: 'upload', name: 'upload', + summary: 'Upload an app to your Ionic account', + options: { + '--email|-e': 'Ionic account email', + '--password|-p': 'Ionic account password' + }, alt: ['up'], - usage: '', task: IonicUploadTask }, { - title: 'package', - name: 'package', - alt: ['pack'], - usage: 'mode platform', - task: IonicPackageTask + title: 'login', + name: 'login', + task: IonicLoginTask } ]; @@ -142,13 +190,101 @@ Ionic = { } }, - _printGenericUsage: function() { + printUsage: function(d) { + var w = function(s) { + process.stdout.write(s); + }; + + w('\n'); + + var rightColumn = 45; + var dots = ''; + var indent = ''; + var x, arg; + + var taskArgs = d.name; + + for(arg in d.args) { + taskArgs += ' ' + arg; + } + + w(taskArgs.green); + + while( (taskArgs + dots).length < rightColumn + 1) { + dots += '.'; + } + + w(' ' + dots.grey + ' '); + + w(d.summary); + + for(arg in d.args) { + if( !d.args[arg] ) continue; + var argLine = arg; + + indent = ''; + w('\n'); + while(indent.length < rightColumn) { + indent += ' '; + } + w( (indent + ' ' + argLine + ' = ' + d.args[arg]).grey ); + } + + indent = ''; + while(indent.length < d.name.length + 1) { + indent += ' '; + } + + for(var opt in d.options) { + w('\n'); + dots = ''; + + var optLine = indent + '[' + opt + '] '; + + w(optLine.yellow); + + while( (dots.length + optLine.length - 2) < rightColumn) { + dots += '.'; + } + w(dots.grey + ' '); + + w(d.options[opt].grey); + } + + w('\n'); + }, + + _printAvailableTasks: function() { + this._printIonic(); + process.stderr.write('\nUsage: ionic task args\n\n===============\n\n'); + process.stderr.write('Available tasks: '.bold); + process.stderr.write('(use --help or -h for more info)\n\n'); + + for(var i = 0; i < TASKS.length; i++) { + var task = TASKS[i]; + if(task.summary) { + var name = ' ' + task.name + ' '; + var dots = ''; + while((name + dots).length < 20) { + dots += '.'; + } + process.stderr.write(name.green + dots.grey + ' ' + task.summary + '\n'); + } + } + + process.stderr.write('\n'); + process.exit(1); + }, + + _printHelpLines: function() { this._printIonic(); - process.stderr.write('Usage: ionic task args\n\n===============\n\nAvailable tasks:\n\n'); + process.stderr.write('\n===================\n'); for(var i = 0; i < TASKS.length; i++) { var task = TASKS[i]; - process.stderr.write(' ' + task.name + ' - ' + task.task.HELP_LINE + '\n'); + if(task.summary) { + this.printUsage(task); + } } process.stderr.write('\n'); @@ -174,25 +310,38 @@ Ionic = { }, run: function() { - if(argv.version) { + if(argv.version || argv.v) { console.log('Ionic CLI version ' + settings.version); process.exit(0); } + if(argv.help || argv.h) { + return this._printHelpLines(); + } + var task = this._tryBuildingTask(); if(!task) { - return this._printGenericUsage(); + return this._printAvailableTasks(); } - console.log('\nRunning', task.title.info.bold, 'task...'); - var taskObj = new task.task(); taskObj.run(this); }, - fail: function(msg) { + fail: function(msg, taskHelp) { process.stderr.write(msg.error.bold); - process.stderr.write('\n\n'); + process.stderr.write('\n'); + if(taskHelp) { + for(var i = 0; i < TASKS.length; i++) { + var task = TASKS[i]; + if(task.name == taskHelp) { + this.printUsage(task); + process.stderr.write('\n'); + break; + } + } + } + process.stderr.write('\n'); process.exit(1); }, diff --git a/lib/ionic/build.js b/lib/ionic/build.js index 04e871ed02..60a91c6d8c 100644 --- a/lib/ionic/build.js +++ b/lib/ionic/build.js @@ -9,27 +9,19 @@ var fs = require('fs'), IonicTask = require('./task').IonicTask, IonicStats = require('./stats').IonicStats; -var IonicBuildTask = function() { -} - -IonicBuildTask.HELP_LINE = 'Locally build an ionic project for a given platform'; +var IonicBuildTask = function() {}; IonicBuildTask.prototype = new IonicTask(); -IonicBuildTask.prototype._printUsage = function() { - process.stderr.write('\nUsage: ionic build platform [more platforms,...]\n'); -} - IonicBuildTask.prototype.run = function(ionic) { if(argv._.length < 2) { - IonicBuildTask.prototype._printUsage(); - ionic.fail('No platforms specified, exiting.'); + ionic.fail('No platforms specified.', 'build'); } var platforms = argv._.slice(1); if(platforms.length < 1) { - ionic.fail('No platforms specified, exiting.'); + ionic.fail('No platforms specified.', 'build'); } IonicStats.t('build', { 'platform': platforms.join(',') }); diff --git a/lib/ionic/emulate.js b/lib/ionic/emulate.js index 0c1934b12e..ca30dffb5c 100644 --- a/lib/ionic/emulate.js +++ b/lib/ionic/emulate.js @@ -10,29 +10,21 @@ var fs = require('fs'), var argv = require('optimist').argv; -var IonicEmulateTask = function() { -} - -IonicEmulateTask.HELP_LINE = 'Emulate an ionic project on a simulator or emulator.'; +var IonicEmulateTask = function() {}; IonicEmulateTask.prototype = new IonicTask(); -IonicEmulateTask.prototype._printUsage = function() { - process.stderr.write('\nUsage: ionic emulate [platform]\n'); -} - IonicEmulateTask.prototype.run = function(ionic) { var patform; if(argv._.length < 2) { - IonicEmulateTask.prototype._printUsage(); - ionic.fail('No platforms specified, exiting.'); + ionic.fail('No platforms specified.', 'emulate'); } var platforms = argv._.slice(1); if(platforms.length < 1) { - ionic.fail('No platforms specified, exiting.'); + ionic.fail('No platforms specified.', 'emulate'); } IonicStats.t('emulate', { 'platform': platforms.join(',') }); diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 0c2290789b..7504c07ff7 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -8,108 +8,139 @@ var fs = require('fs'), var IonicLoginTask = function() {}; -IonicLoginTask.HELP_LINE = 'Login to the Ionic Platform'; - IonicLoginTask.prototype = new IonicTask(); -IonicLoginTask.prototype.run = function(ionic, callback) { - var self = this; +IonicLoginTask.prototype.get = function(ionic, callback) { - var schema = [{ - name: 'email', - pattern: /^[A-z0-9!#$%&'*+\/=?\^_{|}~\-]+(?:\.[A-z0-9!#$%&'*+\/=?\^_{|}~\-]+)*@(?:[A-z0-9](?:[A-z0-9\-]*[A-z0-9])?\.)+[A-z0-9](?:[A-z0-9\-]*[A-z0-9])?$/, - message: 'Email for your Ionic Platform login', - required: true - }, { - name: 'password', - hidden: true, - required: true - }]; - - var project = IonicProject.load(); - - // Grab the email for login - if(argv._.length >= 2 && schema[0].pattern.test(argv._[1])) { - this.email = argv._[1].toLowerCase(); - schema.shift(); + if(ionic.jar) { + // already in memory + callback(ionic.jar); + return; } - console.log('\nTo continue, please login to your Ionic account.'.bold.green); - console.log('Don\'t have one? Create a one at: '.grey + (ionic.IONIC_DASH + '/signup').info.bold + '\n'); + this.email = argv.email || argv.e || process.env.IONIC_EMAIL; + this.password = argv.password || argv.p || process.env.IONIC_PASSWORD; - prompt.override = argv; + if(!this.email && this.password) { + ionic.fail('--email or -e command line flag, or IONIC_EMAIL environment variable required'); + } + if(this.email && !this.password) { + ionic.fail('--password or -p command line flag, or IONIC_PASSWORD environment variable required'); + } - prompt.start(); + if(!this.email && !this.password) { + // did not include cmd line flags, check for existing cookies + this.cookieData = new IonicStore('cookies'); + var jar = this.cookieData.get(ionic.IONIC_DASH); - prompt.get(schema, function (err, result) { - if(err) { - ionic.fail('Error logging in: ' + err); + if(jar && jar.cookies && jar.cookies.length) { + for(var i in jar.cookies) { + var cookie = jar.cookies[i]; + if(cookie.name == "sessionid" && new Date(cookie.expires) > new Date()) { + ionic.jar = jar; + callback(jar); + return; + } + } } + } + + this.run(ionic, callback); +}; + +IonicLoginTask.prototype.run = function(ionic, callback) { + var self = this; + + if(!this.email && !this.password) { + + var schema = [{ + name: 'email', + pattern: /^[A-z0-9!#$%&'*+\/=?\^_{|}~\-]+(?:\.[A-z0-9!#$%&'*+\/=?\^_{|}~\-]+)*@(?:[A-z0-9](?:[A-z0-9\-]*[A-z0-9])?\.)+[A-z0-9](?:[A-z0-9\-]*[A-z0-9])?$/, + description: 'Email:', + required: true + }, { + name: 'password', + description: 'Password:', + hidden: true, + required: true + }]; + + // prompt for log + console.log('\nTo continue, please login to your Ionic account.'.bold.green); + console.log('Don\'t have one? Create a one at: '.grey + (ionic.IONIC_DASH + '/signup').info.bold + '\n'); + + prompt.override = argv; + prompt.message = ''; + prompt.delimiter = ''; + prompt.start(); + + prompt.get(schema, function (err, result) { + if(err) { + ionic.fail('Error logging in: ' + err); + } + + self.email = result.email; + self.password = result.password; + + self.requestLogIn(ionic, callback, true); + }); + + } else { + // cmd line flag were added, use those instead of a prompt + self.requestLogIn(ionic, callback, false); + } - if(!self.email) { - self.email = result.email.toLowerCase(); +}; + +IonicLoginTask.prototype.requestLogIn = function(ionic, callback, saveCookies) { + var self = this; + + var jar = request.jar(); + request({ + url: ionic.IONIC_DASH + '/login', + jar: jar + }, + function(err, response, body) { + if(err || jar.cookies.length === 0) { + ionic.fail('Error logging in: ' + err); } - self.password = result.password; - var jar = request.jar(); request({ + method: 'POST', url: ionic.IONIC_DASH + '/login', - jar: jar + jar: jar, + form: { + username: self.email.toString().toLowerCase(), + password: self.password, + csrfmiddlewaretoken: jar.cookies[0].value + } }, - function(err, response, body) { - if(err || jar.cookies.length === 0) { + function (err, response, body) { + if(err) { ionic.fail('Error logging in: ' + err); } - request({ - method: 'POST', - url: ionic.IONIC_DASH + '/login', - jar: jar, - form: { - username: self.email, - password: self.password, - csrfmiddlewaretoken: jar.cookies[0].value - } - }, - function (err, response, body) { - if(err) { - ionic.fail('Error logging in: ' + err); - } - // Should be a 304 redirect status code if correct - if(response.statusCode == 200) { - ionic.fail('Email or Password incorrect. Please visit '+ ionic.IONIC_DASH +' for help.'); - } + // Should be a 302 redirect status code if correct + if(response.statusCode != 302) { + ionic.fail('Email or Password incorrect. Please visit '+ ionic.IONIC_DASH +' for help.'); + } + if(saveCookies) { + // save cookies self.cookieData.set(ionic.IONIC_DASH, jar); self.cookieData.save(); + } - console.log('Logged in! :)'.green); - - if(callback) { - callback(jar); - project.set('email', self.email); - project.save(); - } - }); - }); - }); -}; + // save in memory + ionic.jar = jar; -IonicLoginTask.prototype.get = function(ionic, callback) { - this.cookieData = new IonicStore('cookies'); - var jar = this.cookieData.get(ionic.IONIC_DASH); + console.log('Logged in! :)'.green); - if(jar && jar.cookies && jar.cookies.length) { - for(var i in jar.cookies) { - var cookie = jar.cookies[i]; - if(cookie.name == "sessionid" && new Date(cookie.expires) > new Date()) { + if(callback) { callback(jar); - return; } - } - } - - this.run(ionic, callback); + }); + }); }; exports.IonicLoginTask = IonicLoginTask; diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 745f2be9c0..a94e065aa7 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -15,92 +15,58 @@ var fs = require('fs'), var IonicPackageTask = function() {}; -IonicPackageTask.HELP_LINE = 'Package an app using the Ionic Platform Build service (requires login)'; - IonicPackageTask.prototype = new IonicTask(); - -IonicPackageTask.prototype._printUsage = function() { - process.stderr.write('\nUsage:\nionic package mode[debug|release] platform[ios,android]\n'); -}; - - IonicPackageTask.prototype.run = function(ionic) { var self = this; self.ionic = ionic; self.project = IonicProject.load(); + self.inputFiles = {}; + self.inputValues = {}; + self.useCmdArgs = false; - if(argv['clear-signing'] || argv.c) { + if(argv['clear-signing'] || argv.l) { self.clearSigning(); return; } self.loadProject(); + this.getCmdLineOptions(); + var login = new IonicLoginTask(); login.get(self.ionic, function(jar) { self.jar = jar; self.loadAppSigning(function(){ - self.packagePlatforms(self.platforms); + self.initPlatforms(); }); }); }; -IonicPackageTask.prototype.clearSigning = function() { - var self = this; - console.log('Clearing app signing and credential information...'.yellow); - - var appId = self.project.get('app_id'); - - if(!appId) { - self.ionic.fail('App Id is not known'); - } - - var login = new IonicLoginTask(); - login.get(self.ionic, function(jar) { - var options = { - url: self.ionic.IONIC_DASH + self.ionic.IONIC_API + 'app/' + appId + '/signing/clear', - headers: { - cookie: jar.cookies.map(function (c) { - return c.name + "=" + encodeURIComponent(c.value); - }).join("; ") - } - }; - - request(options, function(err, response, body) { - if(err) { - self.ionic.fail("Error clearing app signing: " + err); - } - console.log( ('App (' + appId + ') signing and credential information cleared\n').green ); - }); - }); -}; - - IonicPackageTask.prototype.loadProject = function() { var appName = this.project.get('name') || "app"; - console.log( ("Loading " + appName).bold.yellow ); + console.log( ('Loading ' + appName + '...').bold.yellow ); if(argv._.length < 3) { - IonicPackageTask.prototype._printUsage(); - this.ionic.fail('No platforms or build mode specified.'); + this.ionic.fail('No platforms or build mode specified.', 'package'); } this.mode = argv._[1].toLowerCase(); if(this.mode != 'debug' && this.mode != 'release') { - IonicPackageTask.prototype._printUsage(); - this.ionic.fail('Package build mode must be "debug" or "release".'); + this.ionic.fail('Package build mode must be "debug" or "release".', 'package'); } this.platforms = argv._.slice(2); if(this.platforms.length < 1) { - this.ionic.fail('No platforms specified.'); + this.ionic.fail('No platforms specified.', 'package'); } + + this.loadPlugins(); }; @@ -122,9 +88,48 @@ IonicPackageTask.prototype.loadPlugins = function() { }; +IonicPackageTask.prototype.getCmdLineOptions = function() { + var self = this; + + function getCmdArgValue(propertyName, shortName) { + var value = argv[propertyName] || argv[shortName]; + if(value) { + self.inputValues[propertyName] = value; + self.useCmdArgs = true; + } + } + + function getCmdArgFile(propertyName, shortName) { + var value = argv[propertyName] || argv[shortName]; + if(value) { + if(!fileExists(value)) { + self.ionic.fail("Unable to find file: " + argv[propertyName]); + } + self.inputFiles[propertyName] = value; + self.useCmdArgs = true; + } + } + + getCmdArgValue('android-keystore-alias', 'a'); + getCmdArgValue('android-keystore-password', 'w'); + getCmdArgValue('android-key-password', 'r'); + getCmdArgValue('ios-certificate-password', 'd'); + + getCmdArgFile('android-keystore-file', 'k'); + getCmdArgFile('ios-certificate-file', 'c'); + getCmdArgFile('ios-profile-file', 'f'); +}; + + IonicPackageTask.prototype.loadAppSigning = function(callback) { var self = this; + if(self.useCmdArgs) { + // if they used cmd line args, don't bother checking + callback(); + return; + } + if(!self.project.get('app_id')) { // if its the first load we won't have an appId yet callback(); @@ -134,6 +139,7 @@ IonicPackageTask.prototype.loadAppSigning = function(callback) { var privateData = new IonicStore(self.project.get('app_id')); var cck = privateData.get('cck'); if(!cck) { + // if there's no client key don't bother callback(); return; } @@ -149,8 +155,11 @@ IonicPackageTask.prototype.loadAppSigning = function(callback) { }; request(options, function(err, response, body) { - if(!err && response.statusCode == 200) { + if(err) { + self.ionic.fail("Error loading app signing info: " + err); + } + if(response.statusCode == 200) { try { self.signing = JSON.parse(body); @@ -161,7 +170,6 @@ IonicPackageTask.prototype.loadAppSigning = function(callback) { } catch(e) { self.ionic.fail("Error parsing app signing: " + e); } - } callback(); @@ -169,92 +177,107 @@ IonicPackageTask.prototype.loadAppSigning = function(callback) { }; -IonicPackageTask.prototype.packagePlatforms = function(platforms) { +IonicPackageTask.prototype.initPlatforms = function() { var self = this; - prompt.override = argv; - prompt.start(); + if(self.useCmdArgs) { + self.packagePlatforms(); - var promptProperties = self.buildPromptProperties(platforms); + } else { + prompt.override = argv; + prompt.message = ''; + prompt.delimiter = ''; + prompt.start(); - prompt.get({properties: promptProperties}, function (err, promptResult) { - if(err) { - self.ionic.fail('Error packaging: ' + err); - } + var promptProperties = self.buildPromptProperties(); + + prompt.get({properties: promptProperties}, function (err, promptResult) { + if(err) { + self.ionic.fail('Error packaging: ' + err); + } - self.loadPlugins(); + for(var propertyName in promptResult) { + var promptValue = promptResult[propertyName]; + if( !promptValue ) continue; - var upload = new IonicUploadTask(); - upload.run(self.ionic, function() { - for(var x=0; x ...... starter template to use (tabs, sidemenu, blank)\n'); - process.stderr.write(' -a, --app ................... your app\'s name (Use quotes around the name)\n'); - process.stderr.write(' -p, --package ........... package name, such as "com.mycompany.myapp"\n'); -}; - IonicStartTask.prototype.run = function(ionic) { if(argv._.length < 2) { - this._printUsage(); - ionic.fail('\nInvalid command'); + ionic.fail('Invalid command', 'start'); } // Grab the app's relative directory name this.appDirectory = argv._[1]; // Grab the name of the app from -a or --app. Defaults to appDirectory if none provided - this.appName = argv.a || argv.app || this.appDirectory; - this.packageName = argv.p || argv.package; + this.appName = argv['app-name'] || argv.a; + if(!this.appName) { + var appNameSplit = this.appDirectory.split('/'); + appNameSplit = appNameSplit[ appNameSplit.length -1 ].split('\\'); + this.appName = appNameSplit[ appNameSplit.length -1 ]; + } + + this.packageName = argv.id || argv.i; // start project template can come from cmd line args -t, --template, or the 3rd arg, and defaults to tabs - var starterProject = argv.t || argv.template || argv._[2] || 'tabs'; + var starterProject = argv.template || argv.t || argv._[2] || 'tabs'; this.targetPath = path.resolve(this.appDirectory); diff --git a/lib/ionic/store.js b/lib/ionic/store.js index 752864fe90..0743efc0c8 100644 --- a/lib/ionic/store.js +++ b/lib/ionic/store.js @@ -9,7 +9,7 @@ var IonicStore = function(fileName) { this.fileName = fileName; if(fileName.indexOf('.') < 0) { - this.fileName += '.data' + this.fileName += '.data'; } this.homeDir = process.env.HOME || process.env.USERPROFILE || process.env.HOMEPATH; diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index 4a262354e7..08ac6ee9e3 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -12,8 +12,6 @@ var fs = require('fs'), var IonicUploadTask = function() {}; -IonicUploadTask.HELP_LINE = 'Upload an Ionic project to the Ionic Platform (requires login)'; - IonicUploadTask.prototype = new IonicTask(); IonicUploadTask.prototype.run = function(ionic, callback) { @@ -42,11 +40,7 @@ IonicUploadTask.prototype.run = function(ionic, callback) { }); zip.on('close', function() { - if(project.get('app_id')) { - console.log( ('\nUploading app (' + project.get('app_id') + ')...').bold.grey ); - } else { - console.log('\nUploading new app...'.bold.grey); - } + console.log('\nUploading app...'.bold.grey); var form = new FormData(); form.append('name', project.get('name')); diff --git a/package.json b/package.json index e70bf44935..b403524295 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.0.17", + "version": "1.1.0", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From ac98c44f816d20cb64d6513cb53160ed64eb4a7b Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 18 Jul 2014 11:46:44 -0500 Subject: [PATCH 0129/1100] version # in printout --- lib/ionic.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic.js b/lib/ionic.js index 96d419a3fd..0911f82f18 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -301,7 +301,7 @@ Ionic = { w(' _ ___ _ __ _ ___ \n'); w(' | |/ _ \\| \'_ \\| |/ __|\n'); w(' | | (_) | | | | | (__ \n'); - w(' |_|\\___/|_| |_|_|\\___|\n'); + w(' |_|\\___/|_| |_|_|\\___| v'+ settings.version + '\n'); }, From 3e0e96da3453b9bad846ebd62fd8c5090aa89917 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 18 Jul 2014 11:50:50 -0500 Subject: [PATCH 0130/1100] 1.1.0-beta1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b403524295..95065829ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.0", + "version": "1.1.0-beta1", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 8a7b792ed62337efde3c46d78e5fada7bf0f84be Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 18 Jul 2014 12:07:11 -0500 Subject: [PATCH 0131/1100] fix order of package args --- lib/ionic.js | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 0911f82f18..6d2cc29d29 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -123,8 +123,8 @@ var TASKS = [ alt: ['pack'], summary: 'Package an app using the Ionic Build service', args: { - '': '"ios" or "android"', - '': '"debug" or "release"' + '': '"debug" or "release"', + '': '"ios" or "android"' }, options: { '--android-keystore-file|-k': 'Android keystore file', diff --git a/package.json b/package.json index 95065829ec..61e7c55546 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.0-beta1", + "version": "1.1.0-beta2", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 6084491436872d79554ad4c8da3d0d7ff76d61c2 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 18 Jul 2014 12:34:20 -0500 Subject: [PATCH 0132/1100] fix cookieData --- lib/ionic.js | 2 +- lib/ionic/login.js | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 6d2cc29d29..5b10a38354 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -62,7 +62,7 @@ var TASKS = [ '[TEMPLATE]': 'Starter template to use (tabs, sidemenu, blank)' }, options: { - '--app-name|-a': 'Human readable app\'s name (Use quotes around the name)', + '--app-name|-a': 'Human readable name for the app (Use quotes around the name)', '--id|-i': 'Package name set as config' }, task: IonicStartTask diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 7504c07ff7..a2d3983eef 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -11,6 +11,7 @@ var IonicLoginTask = function() {}; IonicLoginTask.prototype = new IonicTask(); IonicLoginTask.prototype.get = function(ionic, callback) { + this.cookieData = new IonicStore('cookies'); if(ionic.jar) { // already in memory @@ -30,7 +31,6 @@ IonicLoginTask.prototype.get = function(ionic, callback) { if(!this.email && !this.password) { // did not include cmd line flags, check for existing cookies - this.cookieData = new IonicStore('cookies'); var jar = this.cookieData.get(ionic.IONIC_DASH); if(jar && jar.cookies && jar.cookies.length) { diff --git a/package.json b/package.json index 61e7c55546..bd6dfbe450 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.0-beta2", + "version": "1.1.0-beta3", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 9b0b5caf0029de865111045ca08e8ed17f897f8a Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 18 Jul 2014 12:42:34 -0500 Subject: [PATCH 0133/1100] fix cookieData store save --- lib/ionic/login.js | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/ionic/login.js b/lib/ionic/login.js index a2d3983eef..43deb50b83 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -127,6 +127,9 @@ IonicLoginTask.prototype.requestLogIn = function(ionic, callback, saveCookies) { if(saveCookies) { // save cookies + if(!self.cookieData) { + self.cookieData = new IonicStore('cookies'); + } self.cookieData.set(ionic.IONIC_DASH, jar); self.cookieData.save(); } diff --git a/package.json b/package.json index bd6dfbe450..ac6e047701 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.0-beta3", + "version": "1.1.0-beta4", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From db4fe047b780147e880f2f10a211199b556c5085 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 18 Jul 2014 13:32:42 -0500 Subject: [PATCH 0134/1100] make sure there's a www directory --- lib/ionic/serve.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index d7a0e0d08d..6d52b8b08c 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -1,4 +1,5 @@ var fs = require('fs'), + path = require('path'), argv = require('optimist').argv, connect = require('connect'), open = require('open'), @@ -28,18 +29,18 @@ IonicServeTask.prototype.run = function(ionic) { this._start(ionic); }; -IonicServeTask.prototype._changed = function(path) { +IonicServeTask.prototype._changed = function(filePath) { // Cleanup the path a bit var pwd = process.cwd(); - path = path.replace(pwd + '/', ''); + filePath = filePath.replace(pwd + '/', ''); - console.log('Changed', path); + console.log( (' changed: ' + filePath).green ); var req = request.post('http://localhost:' + this.liveReloadPort + '/changed', { path: '/changed', method: 'POST', body: JSON.stringify({ - files: [path] + files: [filePath] }) }, function(err, res, body) { if(err) { @@ -52,6 +53,10 @@ IonicServeTask.prototype._start = function(ionic) { var self = this; var app = connect(); + if (!fs.existsSync( path.resolve('www') )) { + ionic.fail('"www" directory cannot be found. Make sure the working directory is an Ionic project.'); + } + if(this.runLivereload) { vfs.watch('www/**/*', { }, function(f) { From bec0c92b7d59ce14d64dd42a7c8d42eaee52044b Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 18 Jul 2014 13:45:10 -0500 Subject: [PATCH 0135/1100] do not require email, package arg order doesn't matter --- lib/ionic/package.js | 38 ++++++++++++++++++++++++-------------- package.json | 2 +- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/lib/ionic/package.js b/lib/ionic/package.js index a94e065aa7..7ff3e2e30e 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -55,12 +55,19 @@ IonicPackageTask.prototype.loadProject = function() { this.ionic.fail('No platforms or build mode specified.', 'package'); } - this.mode = argv._[1].toLowerCase(); + this.mode = argv._[2].toLowerCase(); if(this.mode != 'debug' && this.mode != 'release') { - this.ionic.fail('Package build mode must be "debug" or "release".', 'package'); + // ionic package debug android + this.mode = argv._[1].toLowerCase(); + this.platforms = argv._.slice(2); + } else { + // ionic package android debug + this.platforms = [ argv._[1].toLowerCase() ]; } - this.platforms = argv._.slice(2); + if(this.mode != 'debug' && this.mode != 'release') { + this.ionic.fail('Package build mode must be "debug" or "release".', 'package'); + } if(this.platforms.length < 1) { this.ionic.fail('No platforms specified.', 'package'); @@ -73,17 +80,21 @@ IonicPackageTask.prototype.loadProject = function() { IonicPackageTask.prototype.loadPlugins = function() { this.plugins = []; - var pluginsPath = path.resolve('./plugins/'); - var pluginDirs = fs.readdirSync(pluginsPath); + try { + var pluginsPath = path.resolve('./plugins/'); + var pluginDirs = fs.readdirSync(pluginsPath); - for(var x=0; x Date: Fri, 18 Jul 2014 14:03:40 -0500 Subject: [PATCH 0136/1100] update keywords --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 29088a502f..b057fac894 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,10 @@ "keywords": [ "ionic", "ionic framework", + "ionicframework", "mobile", - "html5", + "app", + "hybrid", "cordova", "phonegap" ], From cdcdafe28013523d8e39d346e182d88adf436123 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 18 Jul 2014 15:11:04 -0500 Subject: [PATCH 0137/1100] no-email flag, ~ resolve path --- lib/ionic.js | 3 ++- lib/ionic/package.js | 35 ++++++++++++++++++++++++++--------- package.json | 2 +- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 5b10a38354..3200524889 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -63,7 +63,7 @@ var TASKS = [ }, options: { '--app-name|-a': 'Human readable name for the app (Use quotes around the name)', - '--id|-i': 'Package name set as config' + '--id|-i': 'Package name set in the config, ie: com.mycompany.myapp' }, task: IonicStartTask }, @@ -135,6 +135,7 @@ var TASKS = [ '--ios-certificate-password|-d': 'iOS certificate password', '--ios-profile-file|-f': 'iOS profile file', '--output|-o': 'Path to save the packaged app', + '--no-email|-n': 'Do not send a build package email', '--clear-signing|-l': 'Clear out all signing data from Ionic server', '--email|-e': 'Ionic account email', '--password|-p': 'Ionic account password' diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 7ff3e2e30e..089b0d38ff 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -73,6 +73,8 @@ IonicPackageTask.prototype.loadProject = function() { this.ionic.fail('No platforms specified.', 'package'); } + this.buildStatusEmail = (!argv['no-email'] && !argv.n); + this.loadPlugins(); }; @@ -81,12 +83,12 @@ IonicPackageTask.prototype.loadPlugins = function() { this.plugins = []; try { - var pluginsPath = path.resolve('./plugins/'); + var pluginsPath = resolvePath('./plugins/'); var pluginDirs = fs.readdirSync(pluginsPath); for(var x=0; x Date: Fri, 18 Jul 2014 15:18:20 -0500 Subject: [PATCH 0138/1100] v1.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0c89e605b6..3f6115f7d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.0-beta6", + "version": "1.1.0", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 19e3ad1753506d0d1e0a0d23bbf49e59bef3ec88 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 18 Jul 2014 15:22:10 -0500 Subject: [PATCH 0139/1100] fix start cmd options --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 09bcb96306..9c2fa70028 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,11 @@ There are three choices of templates: * Side-Menu (sidemenu) * Tabs (tabs) * Blank (blank) - + Command-line flags/options: - -a, --app ................... your app's name (Use quotes around the name) - -p, --package ........... package name, such as "com.mycompany.myapp" + --app-name, -a ........................ Human readable name for the app (Use quotes around the name) + --id, -i .............................. Package name set in the config, ie: com.mycompany.myapp ## Testing in a Browser From d7c0b9a55d04501080a0de9f12ddd360769d5878 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sat, 19 Jul 2014 13:32:30 -0500 Subject: [PATCH 0140/1100] Update serve.js --- lib/ionic/serve.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 6d52b8b08c..90a4a71243 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -19,7 +19,6 @@ IonicServeTask.prototype = new IonicTask(); IonicServeTask.prototype.run = function(ionic) { var project = IonicProject.load(); - // Grab the name of the app this.port = argv._[1] || 8100; this.liveReloadPort = argv._[2] || 35729; From a10db157a41384f05cfd73837dbc0c3f0f0abc1d Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sat, 19 Jul 2014 13:32:48 -0500 Subject: [PATCH 0141/1100] Update platform.js --- lib/ionic/platform.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ionic/platform.js b/lib/ionic/platform.js index fd91a80780..c7cd0476a9 100644 --- a/lib/ionic/platform.js +++ b/lib/ionic/platform.js @@ -18,7 +18,6 @@ IonicPlatformTask.prototype.run = function(ionic) { ionic.fail('No platforms specified.', 'platform'); } - // Grab the name of the app var argPlatforms = argv._.slice(1); var cordovaArgs = ['add', 'remove', 'rm', 'list', 'ls', 'update', 'up']; From a118acd7f6293d6a5a07a31a0972735e420240e1 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sat, 19 Jul 2014 21:34:56 -0500 Subject: [PATCH 0142/1100] fix promise resolve --- lib/ionic.js | 2 +- lib/ionic/start.js | 20 +++++++++++++------- package.json | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 3200524889..6867733e13 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -63,7 +63,7 @@ var TASKS = [ }, options: { '--app-name|-a': 'Human readable name for the app (Use quotes around the name)', - '--id|-i': 'Package name set in the config, ie: com.mycompany.myapp' + '--id|-i': 'Package name for config, ie: com.mycompany.myapp' }, task: IonicStartTask }, diff --git a/lib/ionic/start.js b/lib/ionic/start.js index fcfc675460..23c5c5e054 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -186,7 +186,7 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { self._updateConfigXml(); self._installCordovaPlugins().then(function(output) { - pluginQ.resolve(stdout); + pluginQ.resolve(); }, function(err) { pluginQ.reject(err); }); @@ -206,7 +206,11 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { }); }, function(err) { - Ionic.fail('Unable to grab wrapper project:'.error.bold, err); + Ionic.fail( ('Unable to grab wrapper project: ' + err).error.bold ); + + }).catch(function(err) { + console.error('Error: Unable to fetch wrapper: HTTP:'.error.bold, err.statusCode, err); + Ionic.fail(''); }); }; @@ -217,12 +221,14 @@ IonicStartTask.prototype._printQuickHelp = function(projectDirectory) { console.log('\n * cd into your project:', ('$ cd ' + projectDirectory).info.bold); console.log('\n * Add a platform (ios or Android):', 'ionic platform add ios [android]'.info.bold); console.log(' Note: iOS development requires OS X currently'.small); - console.log(' See the Android Platform Guid for full Android installation instructions: https://cordova.apache.org/docs/en/3.4.0/guide_platforms_android_index.md.html#Android%20Platform%20Guide'.small); - console.log('\n * Build your app:', 'ionic build [platform]'.info.bold); - console.log('\n * Simulate your app:', 'ionic emulate [platform]'.info.bold); - console.log('\n * Run your app on a device:', 'ionic run [platform]'.info.bold); + console.log(' See the Android Platform Guid for full Android installation instructions:'.small); + console.log(' https://cordova.apache.org/docs/en/3.4.0/guide_platforms_android_index.md.html#Android%20Platform%20Guide'.small); + console.log('\n * Build your app:', 'ionic build '.info.bold); + console.log('\n * Simulate your app:', 'ionic emulate '.info.bold); + console.log('\n * Run your app on a device:', 'ionic run '.info.bold); console.log('\n * Develop in the browser with live reload:', 'ionic serve'.info.bold); - console.log('\n\nFor more help, visit the Ionic docs:', 'http://ionicframework.com/docs'.info.bold); + console.log('\n * Package an app using Ionic package service:', 'ionic package '.info.bold); + console.log('\n\nFor more help use', 'ionic --help'.info.bold, 'or visit the Ionic docs:', 'http://ionicframework.com/docs'.info.bold, '\n'); }; IonicStartTask.prototype._writeProjectFile = function(ionic) { diff --git a/package.json b/package.json index 3f6115f7d3..3c6ab7c240 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.0", + "version": "1.1.1-beta1", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From a38b6b6d99f4946a59918c0c15a955d6e935fd8f Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sat, 19 Jul 2014 22:19:11 -0500 Subject: [PATCH 0143/1100] check latest npm version --- lib/ionic.js | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 6867733e13..071d9aa1ce 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -312,8 +312,7 @@ Ionic = { run: function() { if(argv.version || argv.v) { - console.log('Ionic CLI version ' + settings.version); - process.exit(0); + return this.version(); } if(argv.help || argv.h) { @@ -346,6 +345,36 @@ Ionic = { process.exit(1); }, + version: function() { + process.stderr.write('Installed Ionic CLI version: ' + settings.version + '\n'); + process.stderr.write('Latest npm version: '); + + try { + var child = spawn('npm', ['show', 'ionic', 'version']); + child.stdout.setEncoding('utf8'); + child.stdout.on('data', function(npmVersion) { + process.stderr.write(npmVersion.trim() + '\n'); + + if(npmVersion.trim() != settings.version.trim()) { + process.stderr.write('Use ' + 'npm update -g ionic'.bold + ' to update to the latest version\n\n'); + } + + }); + child.stderr.on('data', function(data) { + process.stderr.clearLine(); + process.stderr.cursorTo(0); + process.stderr.write('Error loading current npm version: ' + data); + }); + child.on('error', function(err) { + process.stderr.clearLine(); + process.stderr.cursorTo(0); + process.stderr.write('Error loading current npm version: ' + err); + }); + } catch(e) { + console.log(e) + } + }, + spawnPromise: function(cmd, args, onStdOut, onStdErr) { var q = Q.defer(); var child; @@ -355,7 +384,6 @@ Ionic = { } catch(e) { } child.stdout.setEncoding('utf8'); - child.stderr.setEncoding('utf8'); child.stdout.on('data', function(data) { process.stdout.write(data); onStdOut && onStdOut(data); From e7b95e0b09b612f1bd9bdbc31274a4dae0f6905e Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sat, 19 Jul 2014 22:47:51 -0500 Subject: [PATCH 0144/1100] more error catching --- lib/ionic.js | 13 ++++++++++--- lib/ionic/start.js | 2 ++ package.json | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 071d9aa1ce..e9160a534f 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -427,6 +427,9 @@ Ionic = { writeStream.on('close', function() { q.resolve(repoFolderName); }); + writeStream.on('error', function(err) { + q.reject(err); + }); readStream.pipe(writeStream); }; @@ -435,9 +438,13 @@ Ionic = { q.reject(res); return; } - tempZipFileStream.write(body); - tempZipFileStream.close(); - unzipRepo(tempZipFilePath); + try { + tempZipFileStream.write(body); + tempZipFileStream.close(); + unzipRepo(tempZipFilePath); + } catch(e) { + q.reject(e); + } }); return q.promise; diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 23c5c5e054..38347ee4aa 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -93,6 +93,8 @@ IonicStartTask.prototype._fetchWrapper = function() { q.resolve(self.targetPath); }, function(err) { q.reject(err); + }).catch(function(err) { + Ionic.fail('Error: Unable to fetch wrapper repo: ' + err); }); return q.promise; diff --git a/package.json b/package.json index 3c6ab7c240..2c7376feae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.1-beta1", + "version": "1.1.1-beta2", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 3adb15900ebc504ced2b8746f4e2f2b598deeafe Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sat, 19 Jul 2014 23:37:39 -0500 Subject: [PATCH 0145/1100] --no-cordova option --- lib/ionic.js | 7 ++++--- lib/ionic/start.js | 44 ++++++++++++++++++++++++++++++-------------- package.json | 2 +- 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index e9160a534f..407d98b85e 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -63,7 +63,8 @@ var TASKS = [ }, options: { '--app-name|-a': 'Human readable name for the app (Use quotes around the name)', - '--id|-i': 'Package name for config, ie: com.mycompany.myapp' + '--id|-i': 'Package name for config, ie: com.mycompany.myapp', + '--no-cordova|-n': 'Do not create an app targeted for Cordova' }, task: IonicStartTask }, @@ -353,10 +354,10 @@ Ionic = { var child = spawn('npm', ['show', 'ionic', 'version']); child.stdout.setEncoding('utf8'); child.stdout.on('data', function(npmVersion) { - process.stderr.write(npmVersion.trim() + '\n'); + process.stderr.write(npmVersion.trim() + '\n\n'); if(npmVersion.trim() != settings.version.trim()) { - process.stderr.write('Use ' + 'npm update -g ionic'.bold + ' to update to the latest version\n\n'); + process.stderr.write(' * Use ' + 'npm update -g ionic'.info.bold + ' to update to the latest version\n\n'); } }); diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 38347ee4aa..0956a07249 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -53,6 +53,7 @@ IonicStartTask.prototype.run = function(ionic) { } this.packageName = argv.id || argv.i; + this.isCordovaProject = (argv.cordova !== false && !argv.n); // start project template can come from cmd line args -t, --template, or the 3rd arg, and defaults to tabs var starterProject = argv.template || argv.t || argv._[2] || 'tabs'; @@ -90,6 +91,14 @@ IonicStartTask.prototype._fetchWrapper = function() { rm('-rf', self.targetPath + '/' + repoFolderName + '/'); cd(self.targetPath); + if(!self.isCordovaProject) { + // remove any cordova files/directories if they only want the www + var cordovaFiles = ['hooks/', 'platforms/', 'plugins/', 'config.xml']; + for(var x=0; x'.info.bold); - console.log('\n * Simulate your app:', 'ionic emulate '.info.bold); - console.log('\n * Run your app on a device:', 'ionic run '.info.bold); console.log('\n * Develop in the browser with live reload:', 'ionic serve'.info.bold); - console.log('\n * Package an app using Ionic package service:', 'ionic package '.info.bold); + + if(this.isCordovaProject) { + console.log('\n * Add a platform (ios or Android):', 'ionic platform add ios [android]'.info.bold); + console.log(' Note: iOS development requires OS X currently'.small); + console.log(' See the Android Platform Guid for full Android installation instructions:'.small); + console.log(' https://cordova.apache.org/docs/en/3.4.0/guide_platforms_android_index.md.html#Android%20Platform%20Guide'.small); + console.log('\n * Build your app:', 'ionic build '.info.bold); + console.log('\n * Simulate your app:', 'ionic emulate '.info.bold); + console.log('\n * Run your app on a device:', 'ionic run '.info.bold); + console.log('\n * Package an app using Ionic package service:', 'ionic package '.info.bold); + } console.log('\n\nFor more help use', 'ionic --help'.info.bold, 'or visit the Ionic docs:', 'http://ionicframework.com/docs'.info.bold, '\n'); }; diff --git a/package.json b/package.json index 2c7376feae..85cdfed33d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.1-beta2", + "version": "1.1.1-beta3", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From fba948f4d505126185b30c06dbb1df533c01587d Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Mon, 21 Jul 2014 10:45:56 -0500 Subject: [PATCH 0146/1100] fix latest version print out --- lib/ionic.js | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 407d98b85e..98ade02569 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -348,32 +348,18 @@ Ionic = { version: function() { process.stderr.write('Installed Ionic CLI version: ' + settings.version + '\n'); - process.stderr.write('Latest npm version: '); try { var child = spawn('npm', ['show', 'ionic', 'version']); child.stdout.setEncoding('utf8'); child.stdout.on('data', function(npmVersion) { - process.stderr.write(npmVersion.trim() + '\n\n'); - + process.stderr.write('Latest npm version: ' + npmVersion.trim() + '\n\n'); if(npmVersion.trim() != settings.version.trim()) { process.stderr.write(' * Use ' + 'npm update -g ionic'.info.bold + ' to update to the latest version\n\n'); } }); - child.stderr.on('data', function(data) { - process.stderr.clearLine(); - process.stderr.cursorTo(0); - process.stderr.write('Error loading current npm version: ' + data); - }); - child.on('error', function(err) { - process.stderr.clearLine(); - process.stderr.cursorTo(0); - process.stderr.write('Error loading current npm version: ' + err); - }); - } catch(e) { - console.log(e) - } + } catch(e) {} }, spawnPromise: function(cmd, args, onStdOut, onStdErr) { From 7aa51514aa601d63ea144f8f06773808269f7221 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Mon, 21 Jul 2014 14:24:11 -0500 Subject: [PATCH 0147/1100] print latest version on all tasks --- lib/ionic.js | 53 +++++++++++++++++++++++++++++++++++-------- lib/ionic/build.js | 6 ++--- lib/ionic/emulate.js | 6 ++--- lib/ionic/login.js | 12 +++++----- lib/ionic/package.js | 38 +++++++++++++++---------------- lib/ionic/platform.js | 4 ++-- lib/ionic/run.js | 6 ++--- lib/ionic/serve.js | 8 +++++-- lib/ionic/start.js | 18 +++++++-------- lib/ionic/upload.js | 10 ++++---- package.json | 2 +- 11 files changed, 100 insertions(+), 63 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 98ade02569..2410678240 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -275,7 +275,7 @@ Ionic = { } process.stderr.write('\n'); - process.exit(1); + this.processExit(1); }, _printHelpLines: function() { @@ -290,7 +290,7 @@ Ionic = { } process.stderr.write('\n'); - process.exit(1); + this.processExit(1); }, _printIonic: function() { @@ -312,6 +312,14 @@ Ionic = { }, run: function() { + var self = this; + + self.checkLatestVersion(); + + process.on('exit', function(){ + self.printVersionWarning(); + }); + if(argv.version || argv.v) { return this.version(); } @@ -343,23 +351,48 @@ Ionic = { } } process.stderr.write('\n'); - process.exit(1); + this.processExit(1); }, version: function() { process.stderr.write('Installed Ionic CLI version: ' + settings.version + '\n'); + }, - try { + checkLatestVersion: function() { + this.latestVersion = Q.defer(); + var self = this; + + if(settings.version.indexOf('beta') > -1) { + // don't bother if its a beta version + this.latestVersion.resolve(); + } else { + // check the latest version in npm var child = spawn('npm', ['show', 'ionic', 'version']); child.stdout.setEncoding('utf8'); child.stdout.on('data', function(npmVersion) { - process.stderr.write('Latest npm version: ' + npmVersion.trim() + '\n\n'); - if(npmVersion.trim() != settings.version.trim()) { - process.stderr.write(' * Use ' + 'npm update -g ionic'.info.bold + ' to update to the latest version\n\n'); - } - + self.npmVersion = npmVersion.trim(); + self.latestVersion.resolve(); }); - } catch(e) {} + child.stdout.on('error', function() { + self.latestVersion.resolve(); + }); + } + }, + + printVersionWarning: function() { + if(this.npmVersion && this.npmVersion != settings.version.trim()) { + process.stdout.write('Ionic CLI is out of date:\n'.bold.yellow); + process.stdout.write( (' * Locally installed version: ' + settings.version + '\n').yellow ); + process.stdout.write( (' * Latest version: ' + this.npmVersion + '\n').yellow ); + process.stdout.write( ' * Use '.yellow + 'npm update -g ionic'.info.bold + ' to update to the latest version\n\n'.yellow ); + this.npmVersion = null; + } + }, + + processExit: function(code) { + this.latestVersion.promise.then(function(){ + process.exit(code); + }); }, spawnPromise: function(cmd, args, onStdOut, onStdErr) { diff --git a/lib/ionic/build.js b/lib/ionic/build.js index 60a91c6d8c..ca2396bfec 100644 --- a/lib/ionic/build.js +++ b/lib/ionic/build.js @@ -15,13 +15,13 @@ IonicBuildTask.prototype = new IonicTask(); IonicBuildTask.prototype.run = function(ionic) { if(argv._.length < 2) { - ionic.fail('No platforms specified.', 'build'); + return ionic.fail('No platforms specified.', 'build'); } var platforms = argv._.slice(1); if(platforms.length < 1) { - ionic.fail('No platforms specified.', 'build'); + return ionic.fail('No platforms specified.', 'build'); } IonicStats.t('build', { 'platform': platforms.join(',') }); @@ -30,7 +30,7 @@ IonicBuildTask.prototype.run = function(ionic) { var platform = platforms[i]; console.log('Building platform', platform); if(exec("cordova build " + platform).code !== 0) { - ionic.fail('Unable to build app on platform ' + platform + '. Please see console for more info.'); + return ionic.fail('Unable to build app on platform ' + platform + '. Please see console for more info.'); } } }; diff --git a/lib/ionic/emulate.js b/lib/ionic/emulate.js index ca30dffb5c..1798615f93 100644 --- a/lib/ionic/emulate.js +++ b/lib/ionic/emulate.js @@ -18,13 +18,13 @@ IonicEmulateTask.prototype.run = function(ionic) { var patform; if(argv._.length < 2) { - ionic.fail('No platforms specified.', 'emulate'); + return ionic.fail('No platforms specified.', 'emulate'); } var platforms = argv._.slice(1); if(platforms.length < 1) { - ionic.fail('No platforms specified.', 'emulate'); + return ionic.fail('No platforms specified.', 'emulate'); } IonicStats.t('emulate', { 'platform': platforms.join(',') }); @@ -33,7 +33,7 @@ IonicEmulateTask.prototype.run = function(ionic) { platform = platforms[i]; console.log('Emulating app on platform', platform); if(exec("cordova emulate " + platform).code !== 0) { - ionic.fail('Unable to emulate app on platform ' + platform + '. Please see console for more info.'); + return ionic.fail('Unable to emulate app on platform ' + platform + '. Please see console for more info.'); } } }; diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 43deb50b83..1b918a411a 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -23,10 +23,10 @@ IonicLoginTask.prototype.get = function(ionic, callback) { this.password = argv.password || argv.p || process.env.IONIC_PASSWORD; if(!this.email && this.password) { - ionic.fail('--email or -e command line flag, or IONIC_EMAIL environment variable required'); + return ionic.fail('--email or -e command line flag, or IONIC_EMAIL environment variable required'); } if(this.email && !this.password) { - ionic.fail('--password or -p command line flag, or IONIC_PASSWORD environment variable required'); + return ionic.fail('--password or -p command line flag, or IONIC_PASSWORD environment variable required'); } if(!this.email && !this.password) { @@ -76,7 +76,7 @@ IonicLoginTask.prototype.run = function(ionic, callback) { prompt.get(schema, function (err, result) { if(err) { - ionic.fail('Error logging in: ' + err); + return ionic.fail('Error logging in: ' + err); } self.email = result.email; @@ -102,7 +102,7 @@ IonicLoginTask.prototype.requestLogIn = function(ionic, callback, saveCookies) { }, function(err, response, body) { if(err || jar.cookies.length === 0) { - ionic.fail('Error logging in: ' + err); + return ionic.fail('Error logging in: ' + err); } request({ @@ -117,12 +117,12 @@ IonicLoginTask.prototype.requestLogIn = function(ionic, callback, saveCookies) { }, function (err, response, body) { if(err) { - ionic.fail('Error logging in: ' + err); + return ionic.fail('Error logging in: ' + err); } // Should be a 302 redirect status code if correct if(response.statusCode != 302) { - ionic.fail('Email or Password incorrect. Please visit '+ ionic.IONIC_DASH +' for help.'); + return ionic.fail('Email or Password incorrect. Please visit '+ ionic.IONIC_DASH +' for help.'); } if(saveCookies) { diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 089b0d38ff..3f57ac1fb1 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -52,7 +52,7 @@ IonicPackageTask.prototype.loadProject = function() { console.log( ('Loading ' + appName + '...').bold.yellow ); if(argv._.length < 3) { - this.ionic.fail('No platforms or build mode specified.', 'package'); + return this.ionic.fail('No platforms or build mode specified.', 'package'); } this.mode = argv._[2].toLowerCase(); @@ -66,11 +66,11 @@ IonicPackageTask.prototype.loadProject = function() { } if(this.mode != 'debug' && this.mode != 'release') { - this.ionic.fail('Package build mode must be "debug" or "release".', 'package'); + return this.ionic.fail('Package build mode must be "debug" or "release".', 'package'); } if(this.platforms.length < 1) { - this.ionic.fail('No platforms specified.', 'package'); + return this.ionic.fail('No platforms specified.', 'package'); } this.buildStatusEmail = (!argv['no-email'] && !argv.n); @@ -96,7 +96,7 @@ IonicPackageTask.prototype.loadPlugins = function() { } catch(e) {} } } catch(pluginError) { - this.ionic.fail('Unable to find plugin directory. Make sure the working directory is an Ionic project.'); + return this.ionic.fail('Unable to find plugin directory. Make sure the working directory is an Ionic project.'); } }; @@ -116,7 +116,7 @@ IonicPackageTask.prototype.getCmdLineOptions = function() { var value = argv[propertyName] || argv[shortName]; if(value) { if(!fileExists(value)) { - self.ionic.fail("Unable to find file: " + argv[propertyName]); + return self.ionic.fail("Unable to find file: " + argv[propertyName]); } self.inputFiles[propertyName] = value; self.useCmdArgs = true; @@ -169,7 +169,7 @@ IonicPackageTask.prototype.loadAppSigning = function(callback) { request(options, function(err, response, body) { if(err) { - self.ionic.fail("Error loading app signing info: " + err); + return self.ionic.fail("Error loading app signing info: " + err); } if(response.statusCode == 200) { @@ -177,11 +177,11 @@ IonicPackageTask.prototype.loadAppSigning = function(callback) { self.signing = JSON.parse(body); if(!self.signing) { - self.ionic.fail("Invalid app signing information"); + return self.ionic.fail("Invalid app signing information"); } } catch(e) { - self.ionic.fail("Error parsing app signing: " + e); + return self.ionic.fail("Error parsing app signing: " + e); } } @@ -206,7 +206,7 @@ IonicPackageTask.prototype.initPlatforms = function() { prompt.get({properties: promptProperties}, function (err, promptResult) { if(err) { - self.ionic.fail('Error packaging: ' + err); + return self.ionic.fail('Error packaging: ' + err); } for(var propertyName in promptResult) { @@ -351,7 +351,7 @@ IonicPackageTask.prototype.buildPostRequest = function(platform) { inputFile = inputFile.replace(/\\ /g, ' ').trim(); form.append(propertyName.replace(/-/g, '_'), fs.createReadStream( resolvePath(inputFile) ) ); } catch(e) { - this.ionic.fail("Error loading " + resolvePath(inputFile)); + return this.ionic.fail("Error loading " + resolvePath(inputFile)); } } @@ -387,7 +387,7 @@ IonicPackageTask.prototype.submitPostRequest = function(form, platform) { }, function(err, response) { if(err) { - self.ionic.fail("Error packaging " + platform + ": " + err); + return self.ionic.fail("Error packaging " + platform + ": " + err); } response.setEncoding('utf8'); @@ -431,7 +431,7 @@ IonicPackageTask.prototype.submitPostRequest = function(form, platform) { self.project.save(); } catch(e) { - self.ionic.fail('Error submitPostRequest: ' + e); + return self.ionic.fail('Error submitPostRequest: ' + e); } }); @@ -446,7 +446,7 @@ IonicPackageTask.prototype.clearSigning = function() { var appId = self.project.get('app_id'); if(!appId) { - self.ionic.fail('App Id is not known'); + return self.ionic.fail('App Id is not known'); } var login = new IonicLoginTask(); @@ -462,7 +462,7 @@ IonicPackageTask.prototype.clearSigning = function() { request(options, function(err, response, body) { if(err) { - self.ionic.fail("Error clearing app signing: " + err); + return self.ionic.fail("Error clearing app signing: " + err); } console.log( ('App (' + appId + ') signing and credential information cleared\n').green ); }); @@ -480,7 +480,7 @@ function pingBuildStatus(ionic, requestOptions, platform, attempt) { request(requestOptions, function(err, response, body) { if(err) { - ionic.fail("\nError pinging build status: " + err); + return ionic.fail("\nError pinging build status: " + err); } try { @@ -508,7 +508,7 @@ function pingBuildStatus(ionic, requestOptions, platform, attempt) { attempt++; if(attempt > 60) { - ionic.fail("\nUnable to receive build status"); + return ionic.fail("\nUnable to receive build status"); } setTimeout(function(){ @@ -517,15 +517,15 @@ function pingBuildStatus(ionic, requestOptions, platform, attempt) { } else if(d.status == 4) { console.log("Use 'ionic package --clear-signing' to clear app signing and credential data if needed.".bold.red); - ionic.fail("Build failed"); + return ionic.fail("Build failed"); } else if(d.status == 3) { downloadBuildPackage(platform, d); } else { - ionic.fail("\nError receiving build status"); + return ionic.fail("\nError receiving build status"); } } catch(e) { - ionic.fail("\nError pinging build status: " + e); + return ionic.fail("\nError pinging build status: " + e); } }); diff --git a/lib/ionic/platform.js b/lib/ionic/platform.js index c7cd0476a9..b5b326ed5e 100644 --- a/lib/ionic/platform.js +++ b/lib/ionic/platform.js @@ -15,7 +15,7 @@ IonicPlatformTask.prototype = new IonicTask(); IonicPlatformTask.prototype.run = function(ionic) { if(argv._.length < 2) { - ionic.fail('No platforms specified.', 'platform'); + return ionic.fail('No platforms specified.', 'platform'); } var argPlatforms = argv._.slice(1); @@ -38,7 +38,7 @@ IonicPlatformTask.prototype.run = function(ionic) { } if(platforms.length < 1) { - ionic.fail('No platforms specified.', 'platform'); + return ionic.fail('No platforms specified.', 'platform'); } if(cordovaArg == 'add') { diff --git a/lib/ionic/run.js b/lib/ionic/run.js index 51ccd6130f..76c4752e32 100644 --- a/lib/ionic/run.js +++ b/lib/ionic/run.js @@ -15,13 +15,13 @@ IonicRunTask.prototype = new IonicTask(); IonicRunTask.prototype.run = function(ionic) { if(argv._.length < 2) { - ionic.fail('No platforms specified.', 'run'); + return ionic.fail('No platforms specified.', 'run'); } var platforms = argv._.slice(1); if(platforms.length < 1) { - ionic.fail('No platforms specified.', 'run'); + return ionic.fail('No platforms specified.', 'run'); } IonicStats.t('run', { 'platform': platforms.join(',') }); @@ -30,7 +30,7 @@ IonicRunTask.prototype.run = function(ionic) { var platform = platforms[i]; console.log('Running app on platform', platform); if(exec("cordova run " + platform).code !== 0) { - ionic.fail('Unable to run app on platform ' + platform + '. Please see console for more info.'); + return ionic.fail('Unable to run app on platform ' + platform + '. Please see console for more info.'); } } }; diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 2d17238155..448c63e3b8 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -26,6 +26,10 @@ IonicServeTask.prototype.run = function(ionic) { this.runLivereload = !argv.nolivereload && !argv.r; this._start(ionic); + + ionic.latestVersion.promise.then(function(){ + ionic.printVersionWarning(); + }); }; IonicServeTask.prototype._changed = function(filePath) { @@ -53,7 +57,7 @@ IonicServeTask.prototype._start = function(ionic) { var app = connect(); if (!fs.existsSync( path.resolve('www') )) { - ionic.fail('"www" directory cannot be found. Make sure the working directory is an Ionic project.'); + return ionic.fail('"www" directory cannot be found. Make sure the working directory is an Ionic project.'); } if(this.runLivereload) { @@ -65,7 +69,7 @@ IonicServeTask.prototype._start = function(ionic) { server = tinylr(); server.listen(this.liveReloadPort, function(err) { if(err) { - ionic.fail('Unable to start live reload server:', err); + return ionic.fail('Unable to start live reload server:', err); } else { console.log('Running live reload server at', (self.host(self.liveReloadPort).info.bold) ); if(self.launchBrowser) { diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 0956a07249..f618ec0a25 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -38,7 +38,7 @@ IonicStartTask.prototype = new IonicTask(); IonicStartTask.prototype.run = function(ionic) { if(argv._.length < 2) { - ionic.fail('Invalid command', 'start'); + return ionic.fail('Invalid command', 'start'); } // Grab the app's relative directory name @@ -103,7 +103,7 @@ IonicStartTask.prototype._fetchWrapper = function() { }, function(err) { q.reject(err); }).catch(function(err) { - Ionic.fail('Error: Unable to fetch wrapper repo: ' + err); + return Ionic.fail('Error: Unable to fetch wrapper repo: ' + err); }); return q.promise; @@ -138,7 +138,7 @@ IonicStartTask.prototype._updateConfigXml = function() { var parseString = xml2js.parseString; parseString(configString, function (err, jsonConfig) { if(err) { - Ionic.fail('Error parsing config.xml: ' + err); + return Ionic.fail('Error parsing config.xml: ' + err); } if(!self.packageName) { @@ -154,13 +154,13 @@ IonicStartTask.prototype._updateConfigXml = function() { fs.writeFile(configXmlPath, configString, function(err) { if(err) { - Ionic.fail('Error saving config.xml file: ' + err); + return Ionic.fail('Error saving config.xml file: ' + err); } }); }); } catch(e) { - Ionic.fail('Error updating config.xml file: ' + e); + return Ionic.fail('Error updating config.xml file: ' + e); } }; @@ -209,7 +209,7 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { pluginQ.promise.then(function() { self._printQuickHelp(self.appDirectory); }, function(err) { - Ionic.fail('Unable to add plugins. Perhaps your version of Cordova is too old. Try updating (npm install -g cordova), removing this project folder, and trying again.'); + return Ionic.fail('Unable to add plugins. Perhaps your version of Cordova is too old. Try updating (npm install -g cordova), removing this project folder, and trying again.'); }); IonicStats.t('start', {}); @@ -217,15 +217,15 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { }).catch(function(err) { console.error('Error: Unable to fetch project: HTTP:'.error.bold, err.statusCode, err); console.error('Valid project types are blank, tabs, or sidemenu (or see more on our starter page: http://ionicframework.com/getting-started/)'.error.bold); - Ionic.fail(''); + return Ionic.fail(''); }); }, function(err) { - Ionic.fail( ('Unable to grab wrapper project: ' + err).error.bold ); + return Ionic.fail( ('Unable to grab wrapper project: ' + err).error.bold ); }).catch(function(err) { console.error('Error: Unable to fetch wrapper: HTTP:'.error.bold, err.statusCode, err); - Ionic.fail(''); + return Ionic.fail(''); }); }; diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index 08ac6ee9e3..05a14d5eee 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -35,7 +35,7 @@ IonicUploadTask.prototype.run = function(ionic, callback) { archive.finalize(function(err, bytes) { if(err) { - ionic.fail("Error uploading: " + err); + return ionic.fail("Error uploading: " + err); } }); @@ -64,7 +64,7 @@ IonicUploadTask.prototype.run = function(ionic, callback) { rm('-f', TEMP_FILENAME); if(err) { - ionic.fail("Error uploading: " + err); + return ionic.fail("Error uploading: " + err); } response.setEncoding('utf8'); @@ -76,7 +76,7 @@ IonicUploadTask.prototype.run = function(ionic, callback) { console.log( (d.errors[j]).bold.error ); } q.reject('upload_error'); - ionic.fail('Unable to upload app'); + return ionic.fail('Unable to upload app'); } if(response.statusCode == 200) { @@ -90,7 +90,7 @@ IonicUploadTask.prototype.run = function(ionic, callback) { callback(); } catch(callbackEx) { q.reject('upload_error'); - ionic.fail('Error upload callback: ' + callbackEx); + return ionic.fail('Error upload callback: ' + callbackEx); } } @@ -104,7 +104,7 @@ IonicUploadTask.prototype.run = function(ionic, callback) { } catch(parseEx) { q.reject('upload_error'); - ionic.fail('Error upload response: ' + parseEx); + return ionic.fail('Error upload response: ' + parseEx); } }); diff --git a/package.json b/package.json index 85cdfed33d..9fbb4efe91 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.1-beta3", + "version": "1.1.1", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 254f955ac2648deaf335ae2aa35e74800c1f5263 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Mon, 21 Jul 2014 14:27:11 -0500 Subject: [PATCH 0148/1100] --no-cordova option --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9c2fa70028..e3bc7ffe56 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Command-line flags/options: --app-name, -a ........................ Human readable name for the app (Use quotes around the name) --id, -i .............................. Package name set in the config, ie: com.mycompany.myapp + --no-cordova, -n ...................... Do not create an app targeted for Cordova ## Testing in a Browser From 9c69de3e98511de810307b37577bd58b56918750 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Tue, 22 Jul 2014 17:06:25 -0500 Subject: [PATCH 0149/1100] Removed version check for now --- lib/ionic.js | 10 ++++++---- package.json | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 2410678240..158b1c032f 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -314,11 +314,11 @@ Ionic = { run: function() { var self = this; - self.checkLatestVersion(); + //self.checkLatestVersion(); - process.on('exit', function(){ - self.printVersionWarning(); - }); + //process.on('exit', function(){ + //self.printVersionWarning(); + //}); if(argv.version || argv.v) { return this.version(); @@ -390,9 +390,11 @@ Ionic = { }, processExit: function(code) { + /* this.latestVersion.promise.then(function(){ process.exit(code); }); + */ }, spawnPromise: function(cmd, args, onStdOut, onStdErr) { diff --git a/package.json b/package.json index 9fbb4efe91..8a9275b057 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.1", + "version": "1.1.2", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 330fc4c442d0325ca0097c86dfbefe0412742a47 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Tue, 22 Jul 2014 17:13:24 -0500 Subject: [PATCH 0150/1100] V bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8a9275b057..f57f2280b1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.2", + "version": "1.1.3", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From ff3a59c1f22543a6316e5899b78bc6247bd04a9e Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Tue, 22 Jul 2014 17:35:00 -0500 Subject: [PATCH 0151/1100] Bold formatting --- lib/ionic.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic.js b/lib/ionic.js index 158b1c032f..7092421cf5 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -270,7 +270,7 @@ Ionic = { while((name + dots).length < 20) { dots += '.'; } - process.stderr.write(name.green + dots.grey + ' ' + task.summary + '\n'); + process.stderr.write(name.green.bold + dots.grey + ' ' + task.summary + '\n'); } } From 3ce81471efcd9dbb79b872cfc3605557ea54dd79 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Tue, 22 Jul 2014 22:34:09 -0500 Subject: [PATCH 0152/1100] use http api to check for latest ionic version Prefer the http api over spawning `npm show ionic version` due to possible npm version issues and the command line hassles. --- lib/ionic.js | 41 ++++++++++++++++++++++++----------------- package.json | 2 +- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 7092421cf5..2d243e6ed3 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -24,6 +24,7 @@ var IonicStartTask = require('./ionic/start').IonicStartTask, IonicPackageTask = require('./ionic/package').IonicPackageTask, IonicServeTask = require('./ionic/serve'), IonicProject = require('./ionic/project'), + IonicStore = require('./ionic/store').IonicStore, path = require('path'), request = require('request'), os = require('os'), @@ -314,11 +315,11 @@ Ionic = { run: function() { var self = this; - //self.checkLatestVersion(); + self.checkLatestVersion(); - //process.on('exit', function(){ - //self.printVersionWarning(); - //}); + process.on('exit', function(){ + self.printVersionWarning(); + }); if(argv.version || argv.v) { return this.version(); @@ -362,21 +363,29 @@ Ionic = { this.latestVersion = Q.defer(); var self = this; - if(settings.version.indexOf('beta') > -1) { - // don't bother if its a beta version - this.latestVersion.resolve(); - } else { - // check the latest version in npm - var child = spawn('npm', ['show', 'ionic', 'version']); - child.stdout.setEncoding('utf8'); - child.stdout.on('data', function(npmVersion) { - self.npmVersion = npmVersion.trim(); + try { + // stay silent if it errors + var ionicConfig = new IonicStore('ionic.config'); + var versionCheck = ionicConfig.get('versionCheck'); + if(versionCheck && ((versionCheck + 86400000) > Date.now() )) { + // we've recently checked for the latest version, so don't bother again self.latestVersion.resolve(); - }); - child.stdout.on('error', function() { + return; + } + + var proxy = process.env.PROXY || null; + request({ url: 'http://registry.npmjs.org/ionic/latest', proxy: proxy }, function(err, res, body) { + try { + self.npmVersion = JSON.parse(body).version; + ionicConfig.set('versionCheck', Date.now()); + ionicConfig.save(); + } catch(e) {} self.latestVersion.resolve(); }); + } catch(e) { + self.latestVersion.resolve(); } + }, printVersionWarning: function() { @@ -390,11 +399,9 @@ Ionic = { }, processExit: function(code) { - /* this.latestVersion.promise.then(function(){ process.exit(code); }); - */ }, spawnPromise: function(cmd, args, onStdOut, onStdErr) { diff --git a/package.json b/package.json index f57f2280b1..d17e1a1114 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.3", + "version": "1.1.4", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 12c45c77358ab083d6fc4ca141f30d1553c1506c Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Thu, 24 Jul 2014 22:08:17 -0500 Subject: [PATCH 0153/1100] v1.1.5 --- README.md | 14 ++- lib/ionic.js | 122 +++++++++++++------- lib/ionic/build.js | 38 ------ lib/ionic/cordova.js | 21 ++++ lib/ionic/emulate.js | 41 ------- lib/ionic/lib.js | 260 ++++++++++++++++++++++++++++++++++++++++++ lib/ionic/login.js | 9 +- lib/ionic/package.js | 56 +++++---- lib/ionic/platform.js | 57 --------- lib/ionic/run.js | 38 ------ lib/ionic/serve.js | 2 + lib/ionic/start.js | 33 +++--- lib/ionic/stats.js | 83 +++++++++++++- lib/ionic/upload.js | 8 +- package.json | 2 +- 15 files changed, 520 insertions(+), 264 deletions(-) delete mode 100644 lib/ionic/build.js create mode 100644 lib/ionic/cordova.js delete mode 100644 lib/ionic/emulate.js create mode 100644 lib/ionic/lib.js delete mode 100644 lib/ionic/platform.js delete mode 100644 lib/ionic/run.js diff --git a/README.md b/README.md index e3bc7ffe56..2e6ed11ac3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ Ionic-Cli ========= -The Ionic Framework command line utility makes it easy to start, build, run, and emulate [Ionic](http://ionicframework.com/) apps. In the future, it will also have support for our mobile development services and tools that make Ionic even more powerful. Use `ionic --help` for detailed task information. +The Ionic Framework command line utility makes it easy to start, build, run, and emulate [Ionic](http://ionicframework.com/) apps. In the future, it will also have support for our mobile development services and tools that make Ionic even more powerful. + +Use the `ionic --help` command for more detailed task information. ## Installing @@ -25,7 +27,7 @@ Command-line flags/options: --app-name, -a ........................ Human readable name for the app (Use quotes around the name) --id, -i .............................. Package name set in the config, ie: com.mycompany.myapp - --no-cordova, -n ...................... Do not create an app targeted for Cordova + --no-cordova, -w ...................... Do not create an app targeted for Cordova ## Testing in a Browser @@ -59,6 +61,14 @@ $ ionic emulate ios $ ionic run ios ``` +## Update Ionic lib + +Update Ionic library files, which are found in the `www/lib/ionic` directory. + +```bash +$ ionic lib update +``` + ## Packaging an app (beta) diff --git a/lib/ionic.js b/lib/ionic.js index 2d243e6ed3..57df79da29 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -15,13 +15,11 @@ Copyright 2014 Drifty (http://drifty.com/) */ var IonicStartTask = require('./ionic/start').IonicStartTask, - IonicPlatformTask = require('./ionic/platform').IonicPlatformTask, - IonicRunTask = require('./ionic/run').IonicRunTask, - IonicEmulateTask = require('./ionic/emulate').IonicEmulateTask; - IonicBuildTask = require('./ionic/build').IonicBuildTask, + IonicCordovaTask = require('./ionic/cordova').IonicCordovaTask, IonicLoginTask = require('./ionic/login').IonicLoginTask, IonicUploadTask = require('./ionic/upload').IonicUploadTask, IonicPackageTask = require('./ionic/package').IonicPackageTask, + IonicLibTask = require('./ionic/lib').IonicLibTask, IonicServeTask = require('./ionic/serve'), IonicProject = require('./ionic/project'), IonicStore = require('./ionic/store').IonicStore, @@ -65,7 +63,7 @@ var TASKS = [ options: { '--app-name|-a': 'Human readable name for the app (Use quotes around the name)', '--id|-i': 'Package name for config, ie: com.mycompany.myapp', - '--no-cordova|-n': 'Do not create an app targeted for Cordova' + '--no-cordova|-w': 'Create a basic structure without Cordova requirements' }, task: IonicStartTask }, @@ -90,7 +88,7 @@ var TASKS = [ args: { '': '' }, - task: IonicPlatformTask + task: IonicCordovaTask }, { title: 'emulate', @@ -99,7 +97,7 @@ var TASKS = [ args: { '': '' }, - task: IonicEmulateTask + task: IonicCordovaTask }, { title: 'run', @@ -108,7 +106,11 @@ var TASKS = [ args: { '': '' }, - task: IonicRunTask + options: { + '--debug|--release': '', + '--device|--emulator|--target=FOO': '' + }, + task: IonicCordovaTask }, { title: 'build', @@ -117,7 +119,30 @@ var TASKS = [ args: { '': '' }, - task: IonicBuildTask + task: IonicCordovaTask + }, + { + title: 'plugin add', + name: 'plugin', + summary: 'Add a Cordova plugin', + args: { + '': 'Can be a plugin ID, a local path, or a git URL.' + }, + options: { + '--searchpath ': 'When looking up plugins by ID, look in this directory and\n\ + each of its subdirectories for the plugin before hitting the registry.' + }, + task: IonicCordovaTask + }, + { + title: 'prepare', + name: 'prepare', + task: IonicCordovaTask + }, + { + title: 'compile', + name: 'compile', + task: IonicCordovaTask }, { title: 'package', @@ -155,6 +180,18 @@ var TASKS = [ alt: ['up'], task: IonicUploadTask }, + { + title: 'lib', + name: 'lib', + summary: 'Gets Ionic library version info or updates the Ionic library', + args: { + '[update]': 'Updates the Ionic Framework version in www/lib/ionic' + }, + options: { + '--version|-v': 'Exact Ionic version, otherwise it will default to the latest' + }, + task: IonicLibTask + }, { title: 'login', name: 'login', @@ -205,13 +242,13 @@ Ionic = { var indent = ''; var x, arg; - var taskArgs = d.name; + var taskArgs = d.title; for(arg in d.args) { taskArgs += ' ' + arg; } - w(taskArgs.green); + w(taskArgs.green.bold); while( (taskArgs + dots).length < rightColumn + 1) { dots += '.'; @@ -219,7 +256,7 @@ Ionic = { w(' ' + dots.grey + ' '); - w(d.summary); + w(d.summary.bold); for(arg in d.args) { if( !d.args[arg] ) continue; @@ -230,7 +267,7 @@ Ionic = { while(indent.length < rightColumn) { indent += ' '; } - w( (indent + ' ' + argLine + ' = ' + d.args[arg]).grey ); + w( (indent + ' ' + argLine + ' ' + d.args[arg]).bold ); } indent = ''; @@ -244,14 +281,16 @@ Ionic = { var optLine = indent + '[' + opt + '] '; - w(optLine.yellow); + w(optLine.yellow.bold); - while( (dots.length + optLine.length - 2) < rightColumn) { - dots += '.'; - } - w(dots.grey + ' '); + if(d.options[opt]) { + while( (dots.length + optLine.length - 2) < rightColumn) { + dots += '.'; + } + w(dots.grey + ' '); - w(d.options[opt].grey); + w(d.options[opt].bold); + } } w('\n'); @@ -260,6 +299,11 @@ Ionic = { _printAvailableTasks: function() { this._printIonic(); process.stderr.write('\nUsage: ionic task args\n\n===============\n\n'); + + if(process.argv.length > 2) { + process.stderr.write( (process.argv[2] + ' is not a valid task\n\n').bold.red ); + } + process.stderr.write('Available tasks: '.bold); process.stderr.write('(use --help or -h for more info)\n\n'); @@ -271,7 +315,7 @@ Ionic = { while((name + dots).length < 20) { dots += '.'; } - process.stderr.write(name.green.bold + dots.grey + ' ' + task.summary + '\n'); + process.stderr.write(name.green.bold + dots.grey + ' ' + task.summary.bold + '\n'); } } @@ -321,7 +365,7 @@ Ionic = { self.printVersionWarning(); }); - if(argv.version || argv.v) { + if( (argv.version || argv.v) && !argv._.length) { return this.version(); } @@ -339,6 +383,7 @@ Ionic = { }, fail: function(msg, taskHelp) { + this.hasFailed = true; process.stderr.write(msg.error.bold); process.stderr.write('\n'); if(taskHelp) { @@ -356,7 +401,7 @@ Ionic = { }, version: function() { - process.stderr.write('Installed Ionic CLI version: ' + settings.version + '\n'); + process.stderr.write('v' + settings.version + '\n'); }, checkLatestVersion: function() { @@ -439,11 +484,9 @@ Ionic = { fetchRepo: function(targetPath, repoName, repoUrl) { var q = Q.defer(); - var proxy = process.env.PROXY || null; - // The folder name the project will be downloaded and extracted to var repoFolderName = repoName + '-master'; - console.log('\nDOWNLOADING:'.info.bold, repoUrl); + console.log('\nDownloading:'.info.bold, repoUrl); var tmpFolder = os.tmpdir(); var tempZipFilePath = path.join(tmpFolder, repoName + new Date().getTime() + '.zip'); @@ -462,6 +505,7 @@ Ionic = { readStream.pipe(writeStream); }; + var proxy = process.env.PROXY || null; request({ url: repoUrl, encoding: null, proxy: proxy }, function(err, res, body) { if(res.statusCode !== 200) { q.reject(res); @@ -474,25 +518,23 @@ Ionic = { } catch(e) { q.reject(e); } + }).on('response', function(res){ + var ProgressBar = require('progress'); + var bar = new ProgressBar('[:bar] :percent :etas', { + complete: '=', + incomplete: ' ', + width: 30, + total: parseInt(res.headers['content-length'], 10) + }); + + res.on('data', function (chunk) { + bar.tick(chunk.length); + }); }); return q.promise; - }, - - getLocalIps: function() { - var interfaces = os.networkInterfaces(); - var addresses = []; - for (var k in interfaces) { - for (var k2 in interfaces[k]) { - var address = interfaces[k][k2]; - if (address.family == 'IPv4' && !address.internal) { - addresses.push(address.address); - } - } - } - - return addresses; } + }; exports.Ionic = Ionic; diff --git a/lib/ionic/build.js b/lib/ionic/build.js deleted file mode 100644 index ca2396bfec..0000000000 --- a/lib/ionic/build.js +++ /dev/null @@ -1,38 +0,0 @@ -var fs = require('fs'), - os = require('os'), - argv = require('optimist').argv, - request = require('request'), - ncp = require('ncp').ncp, - path = require('path'), - shelljs = require('shelljs/global'), - unzip = require('unzip'), - IonicTask = require('./task').IonicTask, - IonicStats = require('./stats').IonicStats; - -var IonicBuildTask = function() {}; - -IonicBuildTask.prototype = new IonicTask(); - -IonicBuildTask.prototype.run = function(ionic) { - if(argv._.length < 2) { - return ionic.fail('No platforms specified.', 'build'); - } - - var platforms = argv._.slice(1); - - if(platforms.length < 1) { - return ionic.fail('No platforms specified.', 'build'); - } - - IonicStats.t('build', { 'platform': platforms.join(',') }); - - for(var i = 0; i < platforms.length; i++) { - var platform = platforms[i]; - console.log('Building platform', platform); - if(exec("cordova build " + platform).code !== 0) { - return ionic.fail('Unable to build app on platform ' + platform + '. Please see console for more info.'); - } - } -}; - -exports.IonicBuildTask = IonicBuildTask; diff --git a/lib/ionic/cordova.js b/lib/ionic/cordova.js new file mode 100644 index 0000000000..c9044a27c3 --- /dev/null +++ b/lib/ionic/cordova.js @@ -0,0 +1,21 @@ +var IonicTask = require('./task').IonicTask, + IonicStats = require('./stats').IonicStats; + +var IonicCordovaTask = function() {}; + +IonicCordovaTask.prototype = new IonicTask(); + +IonicCordovaTask.prototype.run = function(ionic) { + var cmdName = process.argv[2].toLowerCase(); + var cmdArgs = (process.argv.length > 3 ? process.argv.slice(3) : []); + + var command = 'cordova ' + cmdName + ' ' + cmdArgs.join(' '); + + if(exec(command).code !== 0) { + return ionic.fail('Error from ' + cmdName + ' command.', cmdName); + } + + IonicStats.t(); +}; + +exports.IonicCordovaTask = IonicCordovaTask; diff --git a/lib/ionic/emulate.js b/lib/ionic/emulate.js deleted file mode 100644 index 1798615f93..0000000000 --- a/lib/ionic/emulate.js +++ /dev/null @@ -1,41 +0,0 @@ -var fs = require('fs'), - os = require('os'), - request = require('request'), - ncp = require('ncp').ncp, - path = require('path'), - shelljs = require('shelljs/global'), - unzip = require('unzip'), - IonicTask = require('./task').IonicTask, - IonicStats = require('./stats').IonicStats; - -var argv = require('optimist').argv; - -var IonicEmulateTask = function() {}; - -IonicEmulateTask.prototype = new IonicTask(); - -IonicEmulateTask.prototype.run = function(ionic) { - var patform; - - if(argv._.length < 2) { - return ionic.fail('No platforms specified.', 'emulate'); - } - - var platforms = argv._.slice(1); - - if(platforms.length < 1) { - return ionic.fail('No platforms specified.', 'emulate'); - } - - IonicStats.t('emulate', { 'platform': platforms.join(',') }); - - for(var i = 0; i < platforms.length; i++) { - platform = platforms[i]; - console.log('Emulating app on platform', platform); - if(exec("cordova emulate " + platform).code !== 0) { - return ionic.fail('Unable to emulate app on platform ' + platform + '. Please see console for more info.'); - } - } -}; - -exports.IonicEmulateTask = IonicEmulateTask; diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js new file mode 100644 index 0000000000..07aaedce43 --- /dev/null +++ b/lib/ionic/lib.js @@ -0,0 +1,260 @@ +var fs = require('fs'), + os = require('os'), + request = require('request'), + path = require('path'), + unzip = require('unzip'), + argv = require('optimist').argv, + prompt = require('prompt'), + colors = require('colors'), + Q = require('q'), + IonicTask = require('./task').IonicTask; + IonicStats = require('./stats').IonicStats; + +var IonicLibTask = function() {}; + +IonicLibTask.prototype = new IonicTask(); + +IonicLibTask.prototype.run = function(ionic) { + var self = this; + self.codeHost = 'http://code.ionicframework.com'; + self.local = {}; + self.latest = {}; + + if (!fs.existsSync( path.resolve('www') )) { + return ionic.fail('"www" directory cannot be found. Make sure the working directory is at the top level of an Ionic project.', 'lib'); + } + + if(argv._.length > 1 && (argv._[1].toLowerCase() == 'update' || argv._[1].toLowerCase() == 'up')) { + this.updatePrompt(); + } else { + // just version info + this.printLibVersions(); + } + +}; + + +IonicLibTask.prototype.printLibVersions = function() { + var self = this; + + var p = path.resolve('www/lib/ionic/version.json'); + try { + self.local = require(p); + console.log('Local Ionic version: '.bold.green + self.local.version + ' (' + path.resolve('www/lib/ionic/') + ')'); + } catch(e) { + console.log('Unable to load ionic lib version information'.bold.error); + console.log('Error loading: '.bold.error + p ); + } + + this.getLatestVersions().then(function(){ + console.log('Latest Ionic version: '.bold.green + self.latest.version_number + ' (released ' + self.latest.release_date + ')'); + + if(self.local.version != self.latest.version_number) { + console.log(' * Local version is out of date'.yellow ); + } else { + console.log(' * Local version up to date'.green ); + } + }); +}; + + +IonicLibTask.prototype.getLatestVersions = function() { + var q = Q.defer(); + var self = this; + + var proxy = process.env.PROXY || null; + request({ url: self.codeHost + '/latest.json', proxy: proxy }, function(err, res, body) { + try { + self.latest = JSON.parse(body); + q.resolve(); + } catch(e) { + console.log(e); + q.reject(e); + } + }); + + return q.promise; +}; + + +IonicLibTask.prototype.updatePrompt = function() { + var self = this; + prompt.message = ''; + prompt.delimiter = ''; + prompt.start(); + + var libPath = path.resolve('www/lib/ionic/'); + + console.log('Are you sure you want replace '.green + libPath.info + ' with an updated version of Ionc?'.green); + + var promptProperties = { + areYouSure: { + name: 'areYouSure', + description: '(yes/no):'.yellow.bold, + required: true + } + }; + + prompt.get({properties: promptProperties}, function (err, promptResult) { + if(err) { + return console.log('Error: ' + err); + } + + var areYouSure = promptResult.areYouSure.toLowerCase().trim(); + if(areYouSure == 'yes' || areYouSure == 'y') { + self.getLatest(); + } + }); +}; + + +IonicLibTask.prototype.getLatest = function() { + var self = this; + + var dirPath = path.resolve('www/lib/'); + if(!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath); + } + + dirPath = path.resolve('www/lib/ionic/'); + deleteFolderRecursive(dirPath); + if(!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath); + } + + var version = argv.version || argv.v; + if(version && version !== true && version !== false) { + // get specific version + var archivePath = '/' + version + '/ionic-v' + version + '.zip'; + self.downloadZip(archivePath, version); + + } else { + // get the latest version + this.getLatestVersions().then(function(){ + console.log('Latest version: '.bold.green + self.latest.version_number + ' (released ' + self.latest.release_date + ')'); + + self.downloadZip(self.latest.archive, self.latest.version_number); + + }).catch(function(err) { + console.log('Error: ' + err); + }); + } + +}; + + +IonicLibTask.prototype.downloadZip = function(archivePath, versionNumber) { + var self = this; + + var versionZip = self.codeHost + archivePath; + self.tmpZipPath = path.resolve('www/lib/ionic/ionic.zip'); + + console.log('Downloading: '.green.bold + versionZip); + + var file = fs.createWriteStream(self.tmpZipPath); + var proxy = process.env.PROXY || null; + request({ url: versionZip, encoding: null, proxy: proxy }, function(err, res, body) { + if(err) { + console.log(err) + return; + } + if(res.statusCode == 404) { + console.log( ('Invalid version: ' + versionNumber).bold.red ); + try { + file.close(); + fs.unlink(self.tmpZipPath); + } catch(e) { + console.error( (e).red ); + } + return; + } + if(res.statusCode >= 400) { + console.log('Unable to download zip (' + res.statusCode + ')'); + return; + } + try { + file.write(body); + file.close(function(){ + self.updateFiles(); + }) + } catch(e) { + console.error( (e).red ); + } + }).on('response', function(res){ + + var ProgressBar = require('progress'); + var bar = new ProgressBar('[:bar] :percent :etas', { + complete: '=', + incomplete: ' ', + width: 30, + total: parseInt(res.headers['content-length'], 10) + }); + + res.on('data', function (chunk) { + bar.tick(chunk.length); + }); + + }).on('error', function(err) { + try { + fs.unlink(self.tmpZipPath); + } catch(e) { + console.error( (e).red ); + } + console.error( (err).red ); + }); + +}; + + +IonicLibTask.prototype.updateFiles = function() { + var self = this; + var ionicLibDir = path.resolve('www/lib/ionic/'); + var readStream = fs.createReadStream(self.tmpZipPath); + + try { + var writeStream = unzip.Extract({ path: ionicLibDir }); + writeStream.on('close', function() { + try{ + var newVersion = require(path.resolve('www/lib/ionic/version.json')).version; + + console.log('Ionic version updated to: '.bold.green + newVersion.bold ); + IonicStats.t(); + + } catch(e) { + console.log('Error loading version info'.bold); + } + try { + fs.unlink(self.tmpZipPath); + } catch(e) {} + }); + writeStream.on('error', function(err) { + console.log('Error: ' + err); + }); + readStream.pipe(writeStream); + + readStream.on('error', function(err){ + console.log('Error: ' + err); + }); + + } catch(err) { + console.log('Error: ' + err); + } +}; + + +function deleteFolderRecursive(path) { + if( fs.existsSync(path) ) { + fs.readdirSync(path).forEach(function(file,index){ + var curPath = path + "/" + file; + if(fs.lstatSync(curPath).isDirectory()) { // recurse + deleteFolderRecursive(curPath); + } else { // delete file + fs.unlinkSync(curPath); + } + }); + fs.rmdirSync(path); + } +} + + +exports.IonicLibTask = IonicLibTask; diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 1b918a411a..c61adaa799 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -56,18 +56,18 @@ IonicLoginTask.prototype.run = function(ionic, callback) { var schema = [{ name: 'email', pattern: /^[A-z0-9!#$%&'*+\/=?\^_{|}~\-]+(?:\.[A-z0-9!#$%&'*+\/=?\^_{|}~\-]+)*@(?:[A-z0-9](?:[A-z0-9\-]*[A-z0-9])?\.)+[A-z0-9](?:[A-z0-9\-]*[A-z0-9])?$/, - description: 'Email:', + description: 'Email:'.yellow.bold, required: true }, { name: 'password', - description: 'Password:', + description: 'Password:'.yellow.bold, hidden: true, required: true }]; // prompt for log console.log('\nTo continue, please login to your Ionic account.'.bold.green); - console.log('Don\'t have one? Create a one at: '.grey + (ionic.IONIC_DASH + '/signup').info.bold + '\n'); + console.log('Don\'t have one? Create a one at: '.bold + (ionic.IONIC_DASH + '/signup').info.bold + '\n'); prompt.override = argv; prompt.message = ''; @@ -113,7 +113,8 @@ IonicLoginTask.prototype.requestLogIn = function(ionic, callback, saveCookies) { username: self.email.toString().toLowerCase(), password: self.password, csrfmiddlewaretoken: jar.cookies[0].value - } + }, + proxy: process.env.PROXY || null }, function (err, response, body) { if(err) { diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 3f57ac1fb1..5de1e6da39 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -1,8 +1,7 @@ var fs = require('fs'), - http = require('http'), + request = require('request'), path = require('path'), parseUrl = require('url').parse, - spawn = require("child_process").spawn, argv = require('optimist').argv, prompt = require('prompt'), shelljs = require('shelljs/global'), @@ -11,6 +10,7 @@ var fs = require('fs'), IonicStore = require('./store').IonicStore, IonicTask = require('./task').IonicTask, IonicUploadTask = require('./upload').IonicUploadTask, + IonicStats = require('./stats').IonicStats, IonicLoginTask = require('./login').IonicLoginTask; var IonicPackageTask = function() {}; @@ -26,6 +26,8 @@ IonicPackageTask.prototype.run = function(ionic) { self.inputValues = {}; self.useCmdArgs = false; + IonicStats.t(); + if(argv['clear-signing'] || argv.l) { self.clearSigning(); return; @@ -35,6 +37,8 @@ IonicPackageTask.prototype.run = function(ionic) { this.getCmdLineOptions(); + if(self.ionic.hasFailed) return; + var login = new IonicLoginTask(); login.get(self.ionic, function(jar) { self.jar = jar; @@ -49,7 +53,7 @@ IonicPackageTask.prototype.run = function(ionic) { IonicPackageTask.prototype.loadProject = function() { var appName = this.project.get('name') || "app"; - console.log( ('Loading ' + appName + '...').bold.yellow ); + console.log( ('Loading ' + appName + '...').bold.green ); if(argv._.length < 3) { return this.ionic.fail('No platforms or build mode specified.', 'package'); @@ -164,7 +168,8 @@ IonicPackageTask.prototype.loadAppSigning = function(callback) { return c.name + "=" + encodeURIComponent(c.value); }).join("; "), cck: cck - } + }, + proxy: process.env.PROXY || null }; request(options, function(err, response, body) { @@ -252,7 +257,7 @@ IonicPackageTask.prototype.buildPromptProperties = function() { promptProperties['android-keystore-file']= { name: 'android-keystore-file', - description: 'Android Keystore File (.keystore):', + description: 'Android Keystore File (.keystore):'.yellow.bold, required: true, conform: fileExists, isFile: true @@ -260,20 +265,20 @@ IonicPackageTask.prototype.buildPromptProperties = function() { promptProperties['android-keystore-alias'] = { name: 'android-keystore-alias', - description: 'Keystore Alias:', + description: 'Keystore Alias:'.yellow.bold.yellow.bold, required: true }; promptProperties['android-keystore-password'] = { name: 'android-keystore-password', - description: 'Keystore Password:', + description: 'Keystore Password:'.yellow.bold, hidden: true, required: true }; promptProperties['android-key-password'] = { name: 'android-key-password', - description: 'Key Password (optional):', + description: 'Key Password (optional):'.yellow.bold, hidden: true }; } @@ -282,7 +287,7 @@ IonicPackageTask.prototype.buildPromptProperties = function() { // iOS promptProperties['ios-certificate-file'] = { name: 'ios-certificate-file', - description: 'iOS Certificate File (.p12):', + description: 'iOS Certificate File (.p12):'.yellow.bold, required: true, conform: fileExists, isFile: true @@ -290,14 +295,14 @@ IonicPackageTask.prototype.buildPromptProperties = function() { promptProperties['ios-certificate-password'] = { name: 'ios-certificate-password', - description: 'Certificate Password:', + description: 'Certificate Password:'.yellow.bold, hidden: true, required: true }; promptProperties['ios-profile-file'] = { name: 'ios-profile-file', - description: 'iOS Mobile Provisioning Profile (.mobileprovision):', + description: 'iOS Mobile Provisioning Profile (.mobileprovision):'.yellow.bold, required: true, conform: fileExists, isFile: true @@ -371,7 +376,7 @@ IonicPackageTask.prototype.submitPostRequest = function(form, platform) { var privateData = new IonicStore(this.project.get('app_id')); - console.log( (platform + ' ' + self.mode + ' package building...').bold.grey ); + console.log( (platform + ' ' + self.mode + ' building...').bold ); form.submit({ protocol: params.protocol, @@ -441,7 +446,7 @@ IonicPackageTask.prototype.submitPostRequest = function(form, platform) { IonicPackageTask.prototype.clearSigning = function() { var self = this; - console.log('Clearing app signing and credential information...'.yellow); + console.log('Clearing app signing and credential information...'.yellow.bold); var appId = self.project.get('app_id'); @@ -457,14 +462,15 @@ IonicPackageTask.prototype.clearSigning = function() { cookie: jar.cookies.map(function (c) { return c.name + "=" + encodeURIComponent(c.value); }).join("; ") - } + }, + proxy: process.env.PROXY || null }; request(options, function(err, response, body) { if(err) { return self.ionic.fail("Error clearing app signing: " + err); } - console.log( ('App (' + appId + ') signing and credential information cleared\n').green ); + console.log( ('App (' + appId + ') signing and credential information cleared\n').green.bold ); }); }); }; @@ -533,7 +539,7 @@ function pingBuildStatus(ionic, requestOptions, platform, attempt) { function downloadBuildPackage(platform, data) { - console.log( ('\n\n' + platform + ' build complete, downloading package...').bold.grey ); + console.log( ('\n\n' + platform + ' build complete, downloading package...').bold ); var filePath = argv.output; @@ -546,16 +552,22 @@ function downloadBuildPackage(platform, data) { } } - var params = parseUrl(data.package_url); - var file = fs.createWriteStream(filePath); - var request = http.get({hostname: params.hostname, path: params.path, port: params.port, protocol: 'http:'}, function(response) { - response.pipe(file); - file.on('finish', function() { + + var proxy = process.env.PROXY || null; + request({ url: data.package_url, proxy: proxy }, function(err, res, body) { + if(res.statusCode !== 200) { + console.error( (err).red ); + return; + } + try { + file.write(body); file.close(function(){ console.log( ('Saved ' + platform + ' package: ' + filePath + '\n').bold.green ); }); - }); + } catch(e) { + q.reject(e); + } }).on('response', function(res){ var ProgressBar = require('progress'); diff --git a/lib/ionic/platform.js b/lib/ionic/platform.js deleted file mode 100644 index b5b326ed5e..0000000000 --- a/lib/ionic/platform.js +++ /dev/null @@ -1,57 +0,0 @@ -var fs = require('fs'), - os = require('os'), - argv = require('optimist').argv, - request = require('request'), - ncp = require('ncp').ncp, - path = require('path'), - shelljs = require('shelljs/global'), - unzip = require('unzip'), - IonicTask = require('./task').IonicTask, - IonicStats = require('./stats').IonicStats; - -var IonicPlatformTask = function() {}; - -IonicPlatformTask.prototype = new IonicTask(); - -IonicPlatformTask.prototype.run = function(ionic) { - if(argv._.length < 2) { - return ionic.fail('No platforms specified.', 'platform'); - } - - var argPlatforms = argv._.slice(1); - - var cordovaArgs = ['add', 'remove', 'rm', 'list', 'ls', 'update', 'up']; - var cordovaArg = 'add'; - var platforms = []; - - for(var y=0; y 3 ? process.argv.slice(3) : []); // skip the cmdName + + var statsData = additionalData || {}; + var platforms = []; + var x, y, cmd; + + // update any aliases with the full cmd so there's a common property + var aliasMap = { + 'rm': 'remove', + 'ls': 'list', + 'up': 'update', + '-w': '--no-cordova', + '-b': '--nobrowser', + '-r': '--nolivereload', + '-l': '--clear-signing', + '-n': '--no-email' + }; + for(x=0; x Date: Sat, 26 Jul 2014 00:16:20 +0800 Subject: [PATCH 0154/1100] Fix typo in `ionic start` quick tips --- lib/ionic/start.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index d4b64f47e3..f3e7ad2d4d 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -244,7 +244,7 @@ IonicStartTask.prototype._printQuickHelp = function(projectDirectory) { if(this.isCordovaProject) { console.log('\n * Add a platform (ios or Android):', 'ionic platform add ios [android]'.info.bold); console.log(' Note: iOS development requires OS X currently'.small); - console.log(' See the Android Platform Guid for full Android installation instructions:'.small); + console.log(' See the Android Platform Guide for full Android installation instructions:'.small); console.log(' https://cordova.apache.org/docs/en/3.4.0/guide_platforms_android_index.md.html#Android%20Platform%20Guide'.small); console.log('\n * Build your app:', 'ionic build '.info.bold); console.log('\n * Simulate your app:', 'ionic emulate '.info.bold); From 2a9796a7a6e82d3cc790d76c9d259074f45462dc Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 25 Jul 2014 11:55:46 -0500 Subject: [PATCH 0155/1100] typo fix --- lib/ionic/lib.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index 07aaedce43..7322243c55 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -85,7 +85,7 @@ IonicLibTask.prototype.updatePrompt = function() { var libPath = path.resolve('www/lib/ionic/'); - console.log('Are you sure you want replace '.green + libPath.info + ' with an updated version of Ionc?'.green); + console.log('Are you sure you want to replace '.green.bold + libPath.info.bold + ' with an updated version of Ionc?'.green.bold); var promptProperties = { areYouSure: { @@ -97,7 +97,7 @@ IonicLibTask.prototype.updatePrompt = function() { prompt.get({properties: promptProperties}, function (err, promptResult) { if(err) { - return console.log('Error: ' + err); + return console.log(err); } var areYouSure = promptResult.areYouSure.toLowerCase().trim(); From 46e5f2ca3e3c7f97e908fdb1eac333d0a9a4e62e Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 25 Jul 2014 13:18:41 -0500 Subject: [PATCH 0156/1100] no version checking for betas --- lib/ionic.js | 6 ++++++ lib/ionic/stats.js | 1 + 2 files changed, 7 insertions(+) diff --git a/lib/ionic.js b/lib/ionic.js index 57df79da29..ff2ffb4355 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -409,6 +409,12 @@ Ionic = { var self = this; try { + if(settings.version.indexOf('beta') > -1) { + // don't bother checking if its a beta + self.latestVersion.resolve(); + return; + } + // stay silent if it errors var ionicConfig = new IonicStore('ionic.config'); var versionCheck = ionicConfig.get('versionCheck'); diff --git a/lib/ionic/stats.js b/lib/ionic/stats.js index b6a808a549..d5869665bf 100644 --- a/lib/ionic/stats.js +++ b/lib/ionic/stats.js @@ -586,6 +586,7 @@ exports.IonicStats = { // add which cli version is being used try { statsData.cli_version = require('../../package.json').version; + if(statsData.cli_version.indexOf('beta') > -1) return; } catch(e2) {} IonicStats.mp(cmdName, statsData); From c2e4a1dbc7846e8258872ad4c183c73e2da48c0f Mon Sep 17 00:00:00 2001 From: Alexis THOMAS Date: Sun, 27 Jul 2014 18:30:51 +0200 Subject: [PATCH 0157/1100] Replace link to specific version of Cordova docs Cordova docs link set to "edge" instead of 3.4.0 or 3.5.0 or newer to come --- lib/ionic/start.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index f3e7ad2d4d..2d213c4c7d 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -245,7 +245,7 @@ IonicStartTask.prototype._printQuickHelp = function(projectDirectory) { console.log('\n * Add a platform (ios or Android):', 'ionic platform add ios [android]'.info.bold); console.log(' Note: iOS development requires OS X currently'.small); console.log(' See the Android Platform Guide for full Android installation instructions:'.small); - console.log(' https://cordova.apache.org/docs/en/3.4.0/guide_platforms_android_index.md.html#Android%20Platform%20Guide'.small); + console.log(' https://cordova.apache.org/docs/en/edge/guide_platforms_android_index.md.html#Android%20Platform%20Guide'.small); console.log('\n * Build your app:', 'ionic build '.info.bold); console.log('\n * Simulate your app:', 'ionic emulate '.info.bold); console.log('\n * Run your app on a device:', 'ionic run '.info.bold); From b8bb829556b1697f6b87c42112d9c099abb3e41f Mon Sep 17 00:00:00 2001 From: Alexis THOMAS Date: Sun, 27 Jul 2014 19:09:46 +0200 Subject: [PATCH 0158/1100] Typo in update prompt --- lib/ionic/lib.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index 7322243c55..3b7ef046d3 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -85,7 +85,7 @@ IonicLibTask.prototype.updatePrompt = function() { var libPath = path.resolve('www/lib/ionic/'); - console.log('Are you sure you want to replace '.green.bold + libPath.info.bold + ' with an updated version of Ionc?'.green.bold); + console.log('Are you sure you want to replace '.green.bold + libPath.info.bold + ' with an updated version of Ionic?'.green.bold); var promptProperties = { areYouSure: { From 5524eebe0aeef0ee10fe1d14a7233a63b618d7d9 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Sun, 27 Jul 2014 18:40:41 -0500 Subject: [PATCH 0159/1100] Options --- lib/ionic.js | 11 ++++++++++- package.json | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index ff2ffb4355..2cfc100f7b 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -57,8 +57,9 @@ var TASKS = [ name: 'start', summary: 'Starts a new Ionic project in the specified PATH', args: { + '[options]': 'any flags for the command', '': 'directory for the new project', - '[TEMPLATE]': 'Starter template to use (tabs, sidemenu, blank)' + '[template]': 'Starter template to use (tabs, sidemenu, blank)' }, options: { '--app-name|-a': 'Human readable name for the app (Use quotes around the name)', @@ -72,6 +73,7 @@ var TASKS = [ name: 'serve', summary: 'Start a local development server for app dev and testing', args: { + '[options]': '', '[http-port]': '', '[livereload-port]': '' }, @@ -86,6 +88,7 @@ var TASKS = [ name: 'platform', summary: 'Add platform target for building an Ionic app', args: { + '[options]': '', '': '' }, task: IonicCordovaTask @@ -95,6 +98,7 @@ var TASKS = [ name: 'emulate', summary: 'Emulate an Ionic project on a simulator or emulator', args: { + '[options]': '', '': '' }, task: IonicCordovaTask @@ -104,6 +108,7 @@ var TASKS = [ name: 'run', summary: 'Run an ionic project on a connected device', args: { + '[options]': '', '': '' }, options: { @@ -117,6 +122,7 @@ var TASKS = [ name: 'build', summary: 'Locally build an ionic project for a given platform', args: { + '[options]': '', '': '' }, task: IonicCordovaTask @@ -126,6 +132,7 @@ var TASKS = [ name: 'plugin', summary: 'Add a Cordova plugin', args: { + '[options]': '', '': 'Can be a plugin ID, a local path, or a git URL.' }, options: { @@ -150,6 +157,7 @@ var TASKS = [ alt: ['pack'], summary: 'Package an app using the Ionic Build service', args: { + '[options]': '', '': '"debug" or "release"', '': '"ios" or "android"' }, @@ -185,6 +193,7 @@ var TASKS = [ name: 'lib', summary: 'Gets Ionic library version info or updates the Ionic library', args: { + '[options]': '', '[update]': 'Updates the Ionic Framework version in www/lib/ionic' }, options: { diff --git a/package.json b/package.json index e7b91e07a1..0384d97976 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.5", + "version": "1.1.6", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From aff090b1a6a039418a8cebc6ab672cdeaabb2444 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Tue, 29 Jul 2014 14:11:04 -0500 Subject: [PATCH 0160/1100] network error, error handling for progress bar --- lib/ionic.js | 4 +++- lib/ionic/lib.js | 4 +++- lib/ionic/package.js | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 2cfc100f7b..9219ae1ddc 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -543,7 +543,9 @@ Ionic = { }); res.on('data', function (chunk) { - bar.tick(chunk.length); + try { + bar.tick(chunk.length); + } catch(e){} }); }); diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index 7322243c55..46770022cd 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -191,7 +191,9 @@ IonicLibTask.prototype.downloadZip = function(archivePath, versionNumber) { }); res.on('data', function (chunk) { - bar.tick(chunk.length); + try { + bar.tick(chunk.length); + } catch(e){} }); }).on('error', function(err) { diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 5de1e6da39..32a3171f2d 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -579,7 +579,9 @@ function downloadBuildPackage(platform, data) { }); res.on('data', function (chunk) { - bar.tick(chunk.length); + try { + bar.tick(chunk.length); + } catch(e){} }); res.on('end', function () { From a76dce5f1af4300cd42db994cb78ab2c72d7b210 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sat, 2 Aug 2014 23:02:36 -0500 Subject: [PATCH 0161/1100] prefix CLI to cli version output closes #55 --- lib/ionic.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic.js b/lib/ionic.js index 9219ae1ddc..a2c0a64a0c 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -357,7 +357,7 @@ Ionic = { w(' _ ___ _ __ _ ___ \n'); w(' | |/ _ \\| \'_ \\| |/ __|\n'); w(' | | (_) | | | | | (__ \n'); - w(' |_|\\___/|_| |_|_|\\___| v'+ settings.version + '\n'); + w(' |_|\\___/|_| |_|_|\\___| CLI v'+ settings.version + '\n'); }, From be0885fd9f3fcdfad3a36782dff742f9db99f3f8 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sat, 2 Aug 2014 23:18:26 -0500 Subject: [PATCH 0162/1100] insert `add` cmd when missing from `platform` cmds Closes #54 --- lib/ionic/cordova.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/ionic/cordova.js b/lib/ionic/cordova.js index c9044a27c3..d93ea9cd3c 100644 --- a/lib/ionic/cordova.js +++ b/lib/ionic/cordova.js @@ -9,6 +9,27 @@ IonicCordovaTask.prototype.run = function(ionic) { var cmdName = process.argv[2].toLowerCase(); var cmdArgs = (process.argv.length > 3 ? process.argv.slice(3) : []); + // backwards compatibility prior to fully wrapping cordova cmds + if(cmdName == 'platform') { + // `ionic platform ` used to actually run `ionic platform add ` + // if a cordova platform cmd isn't the cmd then automatically insert `add` + var hasCordovaCmd = false; + var validCommands = 'add remove rm list ls update up check'.split(' '); + var cmdArg, x, y; + for(x=0; x Date: Mon, 4 Aug 2014 11:43:57 -0500 Subject: [PATCH 0163/1100] set platform body class from cordova hook --- .../after_prepare/010_add_platform_class.js | 92 +++++++++++++++++++ lib/ionic/cordova.js | 72 ++++++++++++++- package.json | 2 +- 3 files changed, 164 insertions(+), 2 deletions(-) create mode 100755 lib/hooks/after_prepare/010_add_platform_class.js diff --git a/lib/hooks/after_prepare/010_add_platform_class.js b/lib/hooks/after_prepare/010_add_platform_class.js new file mode 100755 index 0000000000..fc1f8ef3cf --- /dev/null +++ b/lib/hooks/after_prepare/010_add_platform_class.js @@ -0,0 +1,92 @@ +#!/usr/bin/env node + +// Add Platform Class +// v1.0 +// Automatically adds the platform class to the body tag +// after the `prepare` command. This speeds up device rendering +// the correct layout/style for the specific platform. + +var fs = require('fs'); +var path = require('path'); + +var rootdir = process.argv[2]; + +function addPlatformBodyTag(indexPath, platform) { + // add the platform class to the body tag + try { + var platformClass = 'platform-' + platform; + var cordovaClass = 'platform-cordova platform-webview'; + + var html = fs.readFileSync(indexPath, 'utf8'); + + var bodyTag = findBodyTag(html); + if(!bodyTag) return; // no opening body tag, something's wrong + + if(bodyTag.indexOf(platformClass) > -1) return; // already added + + var newBodyTag = bodyTag; + + var classAttr = findClassAttr(bodyTag); + if(classAttr) { + // body tag has existing class attribute, add the classname + var endingQuote = classAttr.substring(classAttr.length-1); + var newClassAttr = classAttr.substring(0, classAttr.length-1); + newClassAttr += ' ' + platformClass + ' ' + cordovaClass + endingQuote; + newBodyTag = bodyTag.replace(classAttr, newClassAttr); + + } else { + // add class attribute tot he body tag + newBodyTag = bodyTag.replace('>', ' class="' + platformClass + ' ' + cordovaClass + '">') + } + + html = html.replace(bodyTag, newBodyTag); + + fs.writeFileSync(indexPath, html, 'utf8'); + + process.stdout.write('add to body class: ' + platformClass + '\n'); + } catch(e) { + process.stdout.write(e); + } +} + +function findBodyTag(html) { + // get the body tag + try{ + return html.match(//gi)[0]; + }catch(e){} +} + +function findClassAttr(bodyTag) { + // get the body tag's class attribute + try{ + return bodyTag.match(/ class=["|'](.*?)["|']/gi)[0]; + }catch(e){} +} + +if (rootdir) { + + // go through each of the platform directories that have been prepared + var platforms = (process.env.CORDOVA_PLATFORMS ? process.env.CORDOVA_PLATFORMS.split(',') : []); + + for(var x=0; x -1) continue; + addcliHookDirectory( path.join(cliHooksPath, files[x]), files[x] ); + } + }); + + function addcliHookDirectory(cliHookPath, hookDirectoryName) { + fs.readdir(cliHookPath, function(err, files){ + // loop through each of the scripts in the ionic-cli hook directory + if(err) return; + for(var x=0; x Date: Tue, 5 Aug 2014 09:26:25 +0200 Subject: [PATCH 0164/1100] Update 010_add_platform_class.js --- lib/hooks/after_prepare/010_add_platform_class.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/hooks/after_prepare/010_add_platform_class.js b/lib/hooks/after_prepare/010_add_platform_class.js index fc1f8ef3cf..cdfb187492 100755 --- a/lib/hooks/after_prepare/010_add_platform_class.js +++ b/lib/hooks/after_prepare/010_add_platform_class.js @@ -35,7 +35,7 @@ function addPlatformBodyTag(indexPath, platform) { newBodyTag = bodyTag.replace(classAttr, newClassAttr); } else { - // add class attribute tot he body tag + // add class attribute to the body tag newBodyTag = bodyTag.replace('>', ' class="' + platformClass + ' ' + cordovaClass + '">') } From fb7902a60e24dde30e25fea5d94e0dbe66a5f674 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Tue, 5 Aug 2014 14:47:09 -0500 Subject: [PATCH 0165/1100] spawn cordova commands --- lib/ionic/cordova.js | 20 +++++++++++++++----- package.json | 2 +- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/ionic/cordova.js b/lib/ionic/cordova.js index 6b0d964fa9..dd6732a24b 100644 --- a/lib/ionic/cordova.js +++ b/lib/ionic/cordova.js @@ -1,7 +1,9 @@ var IonicTask = require('./task').IonicTask, IonicStats = require('./stats').IonicStats, fs = require('fs'), - path = require('path'); + path = require('path'), + spawn = require('child_process').spawn, + colors = require('colors'); var IonicCordovaTask = function() {}; @@ -33,11 +35,19 @@ IonicCordovaTask.prototype.run = function(ionic) { } this.addHooks(); - var command = 'cordova ' + cmdName + ' ' + cmdArgs.join(' '); + cmdArgs.unshift(cmdName); - if(exec(command).code !== 0) { - return ionic.fail('Error from ' + cmdName + ' command.', cmdName); - } + var cordovaCmd = spawn('cordova', cmdArgs); + + cordovaCmd.stdout.on('data', function (data) { + process.stdout.write(data); + }); + + cordovaCmd.stderr.on('data', function (data) { + if(data) { + process.stderr.write(data.toString().error.bold); + } + }); IonicStats.t(); }; diff --git a/package.json b/package.json index 4f394bd4bb..ff0a9d20ed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.7", + "version": "1.1.8-beta1", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 61b09feb40783a32abe8ad925f5f840ed8da93ec Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Tue, 5 Aug 2014 15:06:16 -0500 Subject: [PATCH 0166/1100] use IP when only one IPV4 internal=false address https://github.com/driftyco/ionic/issues/1892 --- lib/ionic/serve.js | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 2faaec0462..a4bc185953 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -93,7 +93,7 @@ IonicServeTask.prototype._start = function(ionic) { IonicServeTask.prototype.host = function(port) { - var address = 'localhost'; + var addresses = []; try { var os = require('os'); @@ -102,13 +102,13 @@ IonicServeTask.prototype.host = function(port) { for (var dev in ifaces) { ifaces[dev].forEach(function(details){ if (details.family == 'IPv4' && !details.internal) { - address = details.address; + addresses.push(details.address); } }); } } catch(e) {} - return 'http://' + address + ':' + port; + return 'http://' + (addresses.length === 1 ? addresses[0] : 'localhost') + ':' + port; }; module.exports = IonicServeTask; diff --git a/package.json b/package.json index ff0a9d20ed..206464bd1c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.8-beta1", + "version": "1.1.8-beta2", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From fc922932171b699cdc9f9358b801a98ad3c9a593 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Tue, 5 Aug 2014 16:01:36 -0500 Subject: [PATCH 0167/1100] update bower file --- lib/ionic/lib.js | 30 ++++++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index 824930247f..263f50d70c 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -219,6 +219,8 @@ IonicLibTask.prototype.updateFiles = function() { try{ var newVersion = require(path.resolve('www/lib/ionic/version.json')).version; + self.updateBowerFile(newVersion); + console.log('Ionic version updated to: '.bold.green + newVersion.bold ); IonicStats.t(); @@ -244,6 +246,34 @@ IonicLibTask.prototype.updateFiles = function() { }; +IonicLibTask.prototype.updateBowerFile = function(newVersion) { + try { + var bowerFilePath = path.resolve('bower.json'); + var bowerData; + + if( fs.existsSync(bowerFilePath) ) { + bowerData = require(bowerFilePath); + } else { + bowerData = { + "name": "HelloIonic", + "private": "true" + }; + } + + if(!bowerData.devDependencies) bowerData.devDependencies = {}; + + bowerData.devDependencies.ionic = 'driftyco/ionic-bower#' + newVersion; + + var file = fs.createWriteStream(bowerFilePath); + file.write( JSON.stringify(bowerData, null, 2) ); + file.close(); + + } catch(e) { + console.log( ('Bower file error: ' + e).error.bold ); + } +}; + + function deleteFolderRecursive(path) { if( fs.existsSync(path) ) { fs.readdirSync(path).forEach(function(file,index){ diff --git a/package.json b/package.json index 206464bd1c..daf5793d1f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.8-beta2", + "version": "1.1.8-beta3", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From e4c9a984a01b04e97e21f7bd1b988e95fc9b9dad Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Tue, 5 Aug 2014 16:15:10 -0500 Subject: [PATCH 0168/1100] handle network errors Closes #62 --- lib/ionic.js | 2 +- lib/ionic/lib.js | 6 ++---- package.json | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index a2c0a64a0c..0dcccd39d1 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -522,7 +522,7 @@ Ionic = { var proxy = process.env.PROXY || null; request({ url: repoUrl, encoding: null, proxy: proxy }, function(err, res, body) { - if(res.statusCode !== 200) { + if(!res || res.statusCode !== 200) { q.reject(res); return; } diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index 263f50d70c..e4599f56b7 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -68,8 +68,8 @@ IonicLibTask.prototype.getLatestVersions = function() { self.latest = JSON.parse(body); q.resolve(); } catch(e) { - console.log(e); - q.reject(e); + console.log('Error loading latest version information'.bold.error ); + q.reject(); } }); @@ -135,8 +135,6 @@ IonicLibTask.prototype.getLatest = function() { self.downloadZip(self.latest.archive, self.latest.version_number); - }).catch(function(err) { - console.log('Error: ' + err); }); } diff --git a/package.json b/package.json index daf5793d1f..d03dc32df5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.8-beta3", + "version": "1.1.8-beta4", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 13502fbd16bfa059cff8f6af20a1ff7c76fa8836 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 6 Aug 2014 09:31:13 -0500 Subject: [PATCH 0169/1100] set app name in bower.json --- lib/ionic/bower.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ lib/ionic/lib.js | 41 +++++++---------------------------------- lib/ionic/start.js | 3 +++ 3 files changed, 55 insertions(+), 34 deletions(-) create mode 100644 lib/ionic/bower.js diff --git a/lib/ionic/bower.js b/lib/ionic/bower.js new file mode 100644 index 0000000000..0f7ca0b7c4 --- /dev/null +++ b/lib/ionic/bower.js @@ -0,0 +1,45 @@ +var fs = require('fs'), + path = require('path'), + colors = require('colors'); + +exports.IonicBower = { + + setIonicVersion: function(newVersion) { + var bowerData = this.getData(); + if(!bowerData.devDependencies) bowerData.devDependencies = {}; + bowerData.devDependencies.ionic = 'driftyco/ionic-bower#' + newVersion; + this.saveData(bowerData); + }, + + setAppName: function(newAppName) { + var bowerData = this.getData(); + bowerData.name = newAppName; + this.saveData(bowerData); + }, + + getData: function() { + var bowerFilePath = path.resolve('bower.json'); + + if( fs.existsSync(bowerFilePath) ) { + return require(bowerFilePath); + } + + return { + "name": "HelloIonic", + "private": "true" + }; + }, + + saveData: function(bowerData) { + try { + var bowerFilePath = path.resolve('bower.json'); + + var file = fs.createWriteStream(bowerFilePath); + file.write( JSON.stringify(bowerData, null, 2) ); + file.close(); + } catch(e) { + console.log( ('Error saving ' + bowerFilePath + ': ' + e).error.bold ); + } + } + +}; diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index e4599f56b7..bfee7c84b1 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -7,7 +7,7 @@ var fs = require('fs'), prompt = require('prompt'), colors = require('colors'), Q = require('q'), - IonicTask = require('./task').IonicTask; + IonicTask = require('./task').IonicTask, IonicStats = require('./stats').IonicStats; var IonicLibTask = function() {}; @@ -153,7 +153,7 @@ IonicLibTask.prototype.downloadZip = function(archivePath, versionNumber) { var proxy = process.env.PROXY || null; request({ url: versionZip, encoding: null, proxy: proxy }, function(err, res, body) { if(err) { - console.log(err) + console.log(err); return; } if(res.statusCode == 404) { @@ -174,7 +174,7 @@ IonicLibTask.prototype.downloadZip = function(archivePath, versionNumber) { file.write(body); file.close(function(){ self.updateFiles(); - }) + }); } catch(e) { console.error( (e).red ); } @@ -217,13 +217,14 @@ IonicLibTask.prototype.updateFiles = function() { try{ var newVersion = require(path.resolve('www/lib/ionic/version.json')).version; - self.updateBowerFile(newVersion); - console.log('Ionic version updated to: '.bold.green + newVersion.bold ); IonicStats.t(); + var ionicBower = require('./bower').IonicBower; + ionicBower.setIonicVersion(newVersion); + } catch(e) { - console.log('Error loading version info'.bold); + console.log( ('Error loading version info: ' + e).error.bold ); } try { fs.unlink(self.tmpZipPath); @@ -244,34 +245,6 @@ IonicLibTask.prototype.updateFiles = function() { }; -IonicLibTask.prototype.updateBowerFile = function(newVersion) { - try { - var bowerFilePath = path.resolve('bower.json'); - var bowerData; - - if( fs.existsSync(bowerFilePath) ) { - bowerData = require(bowerFilePath); - } else { - bowerData = { - "name": "HelloIonic", - "private": "true" - }; - } - - if(!bowerData.devDependencies) bowerData.devDependencies = {}; - - bowerData.devDependencies.ionic = 'driftyco/ionic-bower#' + newVersion; - - var file = fs.createWriteStream(bowerFilePath); - file.write( JSON.stringify(bowerData, null, 2) ); - file.close(); - - } catch(e) { - console.log( ('Bower file error: ' + e).error.bold ); - } -}; - - function deleteFolderRecursive(path) { if( fs.existsSync(path) ) { fs.readdirSync(path).forEach(function(file,index){ diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 2d213c4c7d..c8782e7c63 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -212,6 +212,9 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { } pluginQ.promise.then(function() { + var ionicBower = require('./bower').IonicBower; + ionicBower.setAppName(self.appName); + self._printQuickHelp(self.appDirectory); }, function(err) { return self.ionic.fail('Unable to add plugins. Perhaps your version of Cordova is too old. Try updating (npm install -g cordova), removing this project folder, and trying again.'); From c716a1473d7f68b54c342c6cade0524625f04977 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 6 Aug 2014 10:09:01 -0500 Subject: [PATCH 0170/1100] update lib w/ bower when using bower --- lib/ionic.js | 4 +++- lib/ionic/lib.js | 51 +++++++++++++++++++++++++++++++++++++----------- package.json | 2 +- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 0dcccd39d1..5b341998e7 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -450,10 +450,12 @@ Ionic = { printVersionWarning: function() { if(this.npmVersion && this.npmVersion != settings.version.trim()) { + process.stdout.write('\n-------------------------\n'.red); process.stdout.write('Ionic CLI is out of date:\n'.bold.yellow); process.stdout.write( (' * Locally installed version: ' + settings.version + '\n').yellow ); process.stdout.write( (' * Latest version: ' + this.npmVersion + '\n').yellow ); - process.stdout.write( ' * Use '.yellow + 'npm update -g ionic'.info.bold + ' to update to the latest version\n\n'.yellow ); + process.stdout.write( ' * Run '.yellow + 'npm update -g ionic'.info.bold + ' to update\n'.yellow ); + process.stdout.write('-------------------------\n\n'.red); this.npmVersion = null; } }, diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index bfee7c84b1..9d0ded7b68 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -6,6 +6,7 @@ var fs = require('fs'), argv = require('optimist').argv, prompt = require('prompt'), colors = require('colors'), + spawn = require('child_process').spawn, Q = require('q'), IonicTask = require('./task').IonicTask, IonicStats = require('./stats').IonicStats; @@ -19,13 +20,16 @@ IonicLibTask.prototype.run = function(ionic) { self.codeHost = 'http://code.ionicframework.com'; self.local = {}; self.latest = {}; + self.usesBower = false; if (!fs.existsSync( path.resolve('www') )) { return ionic.fail('"www" directory cannot be found. Make sure the working directory is at the top level of an Ionic project.', 'lib'); } + this.loadVersionData(); + if(argv._.length > 1 && (argv._[1].toLowerCase() == 'update' || argv._[1].toLowerCase() == 'up')) { - this.updatePrompt(); + this.updateLibVersion(); } else { // just version info this.printLibVersions(); @@ -34,17 +38,25 @@ IonicLibTask.prototype.run = function(ionic) { }; -IonicLibTask.prototype.printLibVersions = function() { - var self = this; +IonicLibTask.prototype.loadVersionData = function() { + this.versionFilePath = path.resolve('www/lib/ionic/version.json'); + if( !fs.existsSync(this.versionFilePath) ) { + this.versionFilePath = path.resolve('www/lib/ionic/bower.json'); + this.usesBower = fs.existsSync(this.versionFilePath); + } - var p = path.resolve('www/lib/ionic/version.json'); try { - self.local = require(p); - console.log('Local Ionic version: '.bold.green + self.local.version + ' (' + path.resolve('www/lib/ionic/') + ')'); + this.local = require(this.versionFilePath); } catch(e) { console.log('Unable to load ionic lib version information'.bold.error); - console.log('Error loading: '.bold.error + p ); } +}; + + +IonicLibTask.prototype.printLibVersions = function() { + var self = this; + + console.log('Local Ionic version: '.bold.green + this.local.version + ' (' + this.versionFilePath + ')'); this.getLatestVersions().then(function(){ console.log('Latest Ionic version: '.bold.green + self.latest.version_number + ' (released ' + self.latest.release_date + ')'); @@ -77,8 +89,25 @@ IonicLibTask.prototype.getLatestVersions = function() { }; -IonicLibTask.prototype.updatePrompt = function() { +IonicLibTask.prototype.updateLibVersion = function() { var self = this; + + if(self.usesBower) { + var bowerCmd = spawn('bower', ['update', 'ionic']); + + bowerCmd.stdout.on('data', function (data) { + process.stdout.write(data); + }); + + bowerCmd.stderr.on('data', function (data) { + if(data) { + process.stderr.write(data.toString().error.bold); + } + }); + + return; + } + prompt.message = ''; prompt.delimiter = ''; prompt.start(); @@ -215,13 +244,13 @@ IonicLibTask.prototype.updateFiles = function() { var writeStream = unzip.Extract({ path: ionicLibDir }); writeStream.on('close', function() { try{ - var newVersion = require(path.resolve('www/lib/ionic/version.json')).version; + self.loadVersionData(); - console.log('Ionic version updated to: '.bold.green + newVersion.bold ); + console.log('Ionic version updated to: '.bold.green + self.local.version.bold ); IonicStats.t(); var ionicBower = require('./bower').IonicBower; - ionicBower.setIonicVersion(newVersion); + ionicBower.setIonicVersion(self.local.version); } catch(e) { console.log( ('Error loading version info: ' + e).error.bold ); diff --git a/package.json b/package.json index d03dc32df5..51e893fa42 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.8-beta4", + "version": "1.1.8-beta5", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From c39070209802e5d9e5bd32577e6b337689dbfe86 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 6 Aug 2014 10:13:05 -0500 Subject: [PATCH 0171/1100] v1.1.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 51e893fa42..c96e0f0d6c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.8-beta5", + "version": "1.1.8", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 3542e9a490d70d9d2b95b65fb85a188d232b23ef Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 6 Aug 2014 10:20:13 -0500 Subject: [PATCH 0172/1100] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e6ed11ac3..4618f97515 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,9 @@ $ ionic run ios ## Update Ionic lib -Update Ionic library files, which are found in the `www/lib/ionic` directory. +Update Ionic library files, which are found in the `www/lib/ionic` directory. If bower is being used +by the project, this command will automatically run `bower update`, otherwise this command updates +the local static files from Ionic's CDN. ```bash $ ionic lib update From 0cd183bbbd8a5e516d1a5ddb09041afacfecf150 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 6 Aug 2014 10:20:30 -0500 Subject: [PATCH 0173/1100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4618f97515..0edc636d0c 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ $ ionic run ios ## Update Ionic lib Update Ionic library files, which are found in the `www/lib/ionic` directory. If bower is being used -by the project, this command will automatically run `bower update`, otherwise this command updates +by the project, this command will automatically run `bower update ionic`, otherwise this command updates the local static files from Ionic's CDN. ```bash From ddc60c524cd823e86f83c2ed58c0b8c04d3fb6fc Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 6 Aug 2014 11:01:59 -0500 Subject: [PATCH 0174/1100] ionic serve description --- README.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0edc636d0c..4c55aacd1d 100644 --- a/README.md +++ b/README.md @@ -25,17 +25,24 @@ There are three choices of templates: Command-line flags/options: - --app-name, -a ........................ Human readable name for the app (Use quotes around the name) - --id, -i .............................. Package name set in the config, ie: com.mycompany.myapp - --no-cordova, -w ...................... Do not create an app targeted for Cordova + --app-name, -a ...... Human readable name for the app (Use quotes around the name) + --id, -i ............ Package name set in the config, ie: com.mycompany.myapp + --no-cordova, -w .... Do not create an app targeted for Cordova ## Testing in a Browser +Start a local development server for app dev and testing. This is also useful to test within a device browser which is connected to the same wireless network. Additionally, LiveReload is installed which is used to monitor changes in the file system. As soon as you save a file, it is preprocessed as needed, and the browser is refreshed automatically. + ```bash -$ ionic serve +$ ionic serve [http-port] [livereload-port] [options] ``` +Command-line flags/options: + + [--nobrowser|-b] ...... Disable launching a browser + [--nolivereload|-r] ... Do not start live reload + ## Adding a platform target From 4e5bb2403f9214d393231ffb0d7d161599bbfb9d Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 6 Aug 2014 11:05:17 -0500 Subject: [PATCH 0175/1100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4c55aacd1d..11bd1cd8c1 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Command-line flags/options: ## Testing in a Browser -Start a local development server for app dev and testing. This is also useful to test within a device browser which is connected to the same wireless network. Additionally, LiveReload is installed which is used to monitor changes in the file system. As soon as you save a file, it is preprocessed as needed, and the browser is refreshed automatically. +Use `ionic serve` to start a local development server for app dev and testing. This is useful for both desktop browser testing, and to test within a device browser which is connected to the same network. Additionally, this command starts LiveReload which is used to monitor changes in the file system. As soon as you save a file the browser is refreshed automatically. ```bash $ ionic serve [http-port] [livereload-port] [options] From f48afd86c5500414c0970cd0fd6affdd2235150f Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Thu, 7 Aug 2014 21:39:32 -0500 Subject: [PATCH 0176/1100] fix windows cordova cmd issues --- lib/ionic/cordova.js | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ionic/cordova.js b/lib/ionic/cordova.js index dd6732a24b..2d97530118 100644 --- a/lib/ionic/cordova.js +++ b/lib/ionic/cordova.js @@ -2,7 +2,7 @@ var IonicTask = require('./task').IonicTask, IonicStats = require('./stats').IonicStats, fs = require('fs'), path = require('path'), - spawn = require('child_process').spawn, + exec = require('child_process').exec, colors = require('colors'); var IonicCordovaTask = function() {}; @@ -37,7 +37,7 @@ IonicCordovaTask.prototype.run = function(ionic) { cmdArgs.unshift(cmdName); - var cordovaCmd = spawn('cordova', cmdArgs); + var cordovaCmd = exec('cordova ' + cmdArgs.join(' ')); cordovaCmd.stdout.on('data', function (data) { process.stdout.write(data); diff --git a/package.json b/package.json index c96e0f0d6c..0b4d072aa0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.8", + "version": "1.1.9-beta1", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 6b4c3d861c268cf7fc86f71d298008297c76dd02 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Thu, 7 Aug 2014 21:40:04 -0500 Subject: [PATCH 0177/1100] get version data from ionic-bower for scss files --- lib/ionic/bower.js | 8 ++- lib/ionic/lib.js | 169 ++++++++++++++++++++++++++++----------------- 2 files changed, 111 insertions(+), 66 deletions(-) diff --git a/lib/ionic/bower.js b/lib/ionic/bower.js index 0f7ca0b7c4..82e447f6d5 100644 --- a/lib/ionic/bower.js +++ b/lib/ionic/bower.js @@ -20,9 +20,11 @@ exports.IonicBower = { getData: function() { var bowerFilePath = path.resolve('bower.json'); - if( fs.existsSync(bowerFilePath) ) { - return require(bowerFilePath); - } + try { + if( fs.existsSync(bowerFilePath) ) { + return require(bowerFilePath); + } + } catch(e){} return { "name": "HelloIonic", diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index 9d0ded7b68..b0656b40d0 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -6,7 +6,7 @@ var fs = require('fs'), argv = require('optimist').argv, prompt = require('prompt'), colors = require('colors'), - spawn = require('child_process').spawn, + exec = require('child_process').exec, Q = require('q'), IonicTask = require('./task').IonicTask, IonicStats = require('./stats').IonicStats; @@ -19,7 +19,7 @@ IonicLibTask.prototype.run = function(ionic) { var self = this; self.codeHost = 'http://code.ionicframework.com'; self.local = {}; - self.latest = {}; + self.versionData = {}; self.usesBower = false; if (!fs.existsSync( path.resolve('www') )) { @@ -58,10 +58,10 @@ IonicLibTask.prototype.printLibVersions = function() { console.log('Local Ionic version: '.bold.green + this.local.version + ' (' + this.versionFilePath + ')'); - this.getLatestVersions().then(function(){ - console.log('Latest Ionic version: '.bold.green + self.latest.version_number + ' (released ' + self.latest.release_date + ')'); + this.getVersionData('latest').then(function(){ + console.log('Latest Ionic version: '.bold.green + self.versionData.version_number + ' (released ' + self.versionData.release_date + ')'); - if(self.local.version != self.latest.version_number) { + if(self.local.version != self.versionData.version_number) { console.log(' * Local version is out of date'.yellow ); } else { console.log(' * Local version up to date'.green ); @@ -70,17 +70,39 @@ IonicLibTask.prototype.printLibVersions = function() { }; -IonicLibTask.prototype.getLatestVersions = function() { +IonicLibTask.prototype.getVersionData = function(version) { var q = Q.defer(); var self = this; + var url = self.codeHost; + if(version == 'latest') { + url += '/latest.json'; + } else { + url += '/' + version + '/version.json'; + } + var proxy = process.env.PROXY || null; - request({ url: self.codeHost + '/latest.json', proxy: proxy }, function(err, res, body) { + request({ url: url, proxy: proxy }, function(err, res, body) { try { - self.latest = JSON.parse(body); + if(err || !res || !body) { + console.log('Unable to receive version data'); + q.reject(); + return; + } + if(res.statusCode == 404) { + console.log( ('Invalid version: ' + version).bold.red ); + q.reject(); + return; + } + if(res.statusCode >= 400) { + console.log('Unable to load version data'); + q.reject(); + return; + } + self.versionData = JSON.parse(body); q.resolve(); } catch(e) { - console.log('Error loading latest version information'.bold.error ); + console.log('Error loading '.bold.error + version.bold + ' version information'.bold.error ); q.reject(); } }); @@ -93,7 +115,7 @@ IonicLibTask.prototype.updateLibVersion = function() { var self = this; if(self.usesBower) { - var bowerCmd = spawn('bower', ['update', 'ionic']); + var bowerCmd = exec('bower update ionic'); bowerCmd.stdout.on('data', function (data) { process.stdout.write(data); @@ -146,47 +168,44 @@ IonicLibTask.prototype.getLatest = function() { } dirPath = path.resolve('www/lib/ionic/'); - deleteFolderRecursive(dirPath); if(!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath); } var version = argv.version || argv.v; - if(version && version !== true && version !== false) { - // get specific version - var archivePath = '/' + version + '/ionic-v' + version + '.zip'; - self.downloadZip(archivePath, version); - - } else { - // get the latest version - this.getLatestVersions().then(function(){ - console.log('Latest version: '.bold.green + self.latest.version_number + ' (released ' + self.latest.release_date + ')'); - - self.downloadZip(self.latest.archive, self.latest.version_number); - - }); + if(version === true || !version) { + version = 'latest'; } + this.getVersionData(version).then(function(){ + if(version == 'latest') { + console.log('Latest version: '.bold.green + self.versionData.version_number + ' (released ' + self.versionData.release_date + ')'); + } else { + console.log('Version: '.bold.green + self.versionData.version_number + ' (released ' + self.versionData.release_date + ')'); + } + self.downloadZip(self.versionData.version_number); + }); + }; -IonicLibTask.prototype.downloadZip = function(archivePath, versionNumber) { +IonicLibTask.prototype.downloadZip = function(version) { var self = this; - var versionZip = self.codeHost + archivePath; + var archivePath = 'https://github.com/driftyco/ionic-bower/archive/v' + version + '.zip'; self.tmpZipPath = path.resolve('www/lib/ionic/ionic.zip'); - console.log('Downloading: '.green.bold + versionZip); + console.log('Downloading: '.green.bold + archivePath); var file = fs.createWriteStream(self.tmpZipPath); var proxy = process.env.PROXY || null; - request({ url: versionZip, encoding: null, proxy: proxy }, function(err, res, body) { + request({ url: archivePath, encoding: null, proxy: proxy }, function(err, res, body) { if(err) { console.log(err); return; } if(res.statusCode == 404) { - console.log( ('Invalid version: ' + versionNumber).bold.red ); + console.log( ('Invalid version: ' + version).bold.red ); try { file.close(); fs.unlink(self.tmpZipPath); @@ -235,58 +254,82 @@ IonicLibTask.prototype.downloadZip = function(archivePath, versionNumber) { }; -IonicLibTask.prototype.updateFiles = function() { +IonicLibTask.prototype.updateFiles = function(version) { var self = this; - var ionicLibDir = path.resolve('www/lib/ionic/'); + var ionicLibDir = path.resolve('www/lib/ionic'); var readStream = fs.createReadStream(self.tmpZipPath); try { - var writeStream = unzip.Extract({ path: ionicLibDir }); - writeStream.on('close', function() { - try{ - self.loadVersionData(); - console.log('Ionic version updated to: '.bold.green + self.local.version.bold ); - IonicStats.t(); + function onEntry(entry) { + var libEntryPath = entry.path.replace('ionic-bower-' + self.versionData.version_number + '/', ionicLibDir + '/'); - var ionicBower = require('./bower').IonicBower; - ionicBower.setIonicVersion(self.local.version); + if( /bower.json|readme/gi.test(entry.path) ) { + entry.autodrain(); + return; + } - } catch(e) { - console.log( ('Error loading version info: ' + e).error.bold ); + if(entry.type == 'Directory') { + if( !fs.existsSync(libEntryPath) ) { + fs.mkdirSync(libEntryPath); + } + } else { + var writeStream = fs.createWriteStream(libEntryPath); + writeStream.on('error', function(err) { + console.log( ('Error writing, ' + libEntryPath + ': ' + err).bold.red ); + }); + entry.pipe(writeStream); } - try { - fs.unlink(self.tmpZipPath); - } catch(e) {} - }); - writeStream.on('error', function(err) { - console.log('Error: ' + err); - }); - readStream.pipe(writeStream); + + entry.autodrain(); + } + + readStream.pipe(unzip.Parse()) + .on('entry', onEntry) + .on('close', function(){ + try{ + console.log('Ionic version updated to: '.bold.green + self.versionData.version_number.bold ); + IonicStats.t(); + + self.writeVersionData(); + + var ionicBower = require('./bower').IonicBower; + ionicBower.setIonicVersion(self.versionData.version_number); + + } catch(e) { + console.log( ('Error loading version info: ' + e).error.bold ); + } + }); readStream.on('error', function(err){ console.log('Error: ' + err); }); + readStream.on('close', function(err){ + try { + fs.unlink(self.tmpZipPath); + } catch(e){} + }); + } catch(err) { console.log('Error: ' + err); } }; - -function deleteFolderRecursive(path) { - if( fs.existsSync(path) ) { - fs.readdirSync(path).forEach(function(file,index){ - var curPath = path + "/" + file; - if(fs.lstatSync(curPath).isDirectory()) { // recurse - deleteFolderRecursive(curPath); - } else { // delete file - fs.unlinkSync(curPath); - } - }); - fs.rmdirSync(path); +IonicLibTask.prototype.writeVersionData = function() { + try { + var versionData = { + "version": this.versionData.version_number || this.versionData.version, + "codename": this.versionData.version_codename || this.versionData.codename, + "date": this.versionData.release_date || this.versionData.date + }; + + var file = fs.createWriteStream( path.resolve('www/lib/ionic/version.json') ); + file.write( JSON.stringify(versionData, null, 2) ); + file.close(); + } catch(e) { + console.log( ('Error writing version data: ' + e).error.bold ); } -} - +}; exports.IonicLibTask = IonicLibTask; From c65760db79bdad46184718551416b2d8d4f8a1eb Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sat, 9 Aug 2014 23:20:51 -0500 Subject: [PATCH 0178/1100] ionic setup sass --- README.md | 12 +++++ lib/ionic.js | 20 ++++++-- lib/ionic/bower.js | 4 +- lib/ionic/login.js | 2 +- lib/ionic/serve.js | 27 +++++++++- lib/ionic/setup.js | 123 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 179 insertions(+), 9 deletions(-) create mode 100644 lib/ionic/setup.js diff --git a/README.md b/README.md index 2e6ed11ac3..ca06471175 100644 --- a/README.md +++ b/README.md @@ -87,3 +87,15 @@ The third argument can be either `debug` or `release`, and the last argument can Ionic uses Cordova underneath, so you can also substitute Cordova commands to prepare/build/emulate/run, or to add additional plugins. Note: we occasionally send anonymous usage statistics to the Ionic team to make the tool better. + + +## Using Sass + +By default, starter projects are hooked up to Ionic's precompiled CSS file, which is found in the project's `www/lib/ionic/css` directory, and is linked to the app in the head of the root `index.html` file. However, Ionic projects can also be customized using [Sass](http://sass-lang.com/), which gives developers and designers "superpowers" in terms of creating and maintaining CSS. Please follow these steps if you would like to use Sass for an Ionic project: + +1) Run `npm install` from the working directory of an Ionic project. This will install the [gulp.js](http://gulpjs.com/) build system and a few handy tasks, such as [gulp-sass](https://www.npmjs.org/package/gulp-sass) and [gulp-minify-css](https://www.npmjs.org/package/gulp-minify-css). +2) Remove `` from the `` of the root `index.html` file. +3) Remove `` from the `` of the root `index.html` file. +4) Add `` to the `` of the root `index.html` file. +5) In the `ionic.project` file, add the JavaScript property `sass: true` to the object. + diff --git a/lib/ionic.js b/lib/ionic.js index 5b341998e7..d8f7613f85 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -20,6 +20,7 @@ var IonicStartTask = require('./ionic/start').IonicStartTask, IonicUploadTask = require('./ionic/upload').IonicUploadTask, IonicPackageTask = require('./ionic/package').IonicPackageTask, IonicLibTask = require('./ionic/lib').IonicLibTask, + IonicSetupTask = require('./ionic/setup').IonicSetupTask, IonicServeTask = require('./ionic/serve'), IonicProject = require('./ionic/project'), IonicStore = require('./ionic/store').IonicStore, @@ -137,7 +138,7 @@ var TASKS = [ }, options: { '--searchpath ': 'When looking up plugins by ID, look in this directory and\n\ - each of its subdirectories for the plugin before hitting the registry.' + each of its subdirectories for the plugin before hitting the registry.' }, task: IonicCordovaTask }, @@ -201,6 +202,15 @@ var TASKS = [ }, task: IonicLibTask }, + { + title: 'setup', + name: 'setup', + summary: 'Configure the project with a build tool ' + '(beta)'.yellow, + args: { + '[sass]': 'Setup the project to use Sass CSS precompiling' + }, + task: IonicSetupTask + }, { title: 'login', name: 'login', @@ -209,7 +219,7 @@ var TASKS = [ ]; Ionic = { - IONIC_DASH: 'http://apps.ionicframework.com', + IONIC_DASH: 'https://ionic.io', //IONIC_DASH: 'http://localhost:8000', IONIC_API: '/api/v1/', PRIVATE_PATH: '.ionic', @@ -246,7 +256,7 @@ Ionic = { w('\n'); - var rightColumn = 45; + var rightColumn = 47; var dots = ''; var indent = ''; var x, arg; @@ -265,7 +275,9 @@ Ionic = { w(' ' + dots.grey + ' '); - w(d.summary.bold); + if(d.summary) { + w(d.summary.bold); + } for(arg in d.args) { if( !d.args[arg] ) continue; diff --git a/lib/ionic/bower.js b/lib/ionic/bower.js index 82e447f6d5..9ceab6e83f 100644 --- a/lib/ionic/bower.js +++ b/lib/ionic/bower.js @@ -1,6 +1,6 @@ var fs = require('fs'), - path = require('path'), - colors = require('colors'); + path = require('path'), + colors = require('colors'); exports.IonicBower = { diff --git a/lib/ionic/login.js b/lib/ionic/login.js index c61adaa799..61f4de86d4 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -123,7 +123,7 @@ IonicLoginTask.prototype.requestLogIn = function(ionic, callback, saveCookies) { // Should be a 302 redirect status code if correct if(response.statusCode != 302) { - return ionic.fail('Email or Password incorrect. Please visit '+ ionic.IONIC_DASH +' for help.'); + return ionic.fail('Email or Password incorrect. Please visit '+ ionic.IONIC_DASH.white +' for help.'.red); } if(saveCookies) { diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index a4bc185953..f62c14d40c 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -8,6 +8,7 @@ var fs = require('fs'), vfs = require('vinyl-fs'), request = require('request'), Q = require('q'), + spawn = require('child_process').spawn, IonicProject = require('./project'), IonicTask = require('./task').IonicTask; IonicStats = require('./stats').IonicStats; @@ -24,6 +25,7 @@ IonicServeTask.prototype.run = function(ionic) { this.launchBrowser = !argv.nobrowser && !argv.b; this.runLivereload = !argv.nolivereload && !argv.r; + this.watchSass = project.get('sass') === true && !argv.nosass && !argv.s; this._start(ionic); @@ -62,6 +64,20 @@ IonicServeTask.prototype._start = function(ionic) { return ionic.fail('"www" directory cannot be found. Make sure the working directory is an Ionic project.'); } + if(this.watchSass) { + var childProcess = spawn('gulp', ['watch']); + + childProcess.stdout.on('data', function (data) { + process.stdout.write(data); + }); + + childProcess.stderr.on('data', function (data) { + if(data) { + process.stderr.write(data.toString().yellow); + } + }); + } + if(this.runLivereload) { vfs.watch('www/**/*', { }, function(f) { @@ -73,7 +89,7 @@ IonicServeTask.prototype._start = function(ionic) { if(err) { return ionic.fail('Unable to start live reload server:', err); } else { - console.log('Running live reload server at', (self.host(self.liveReloadPort).info.bold) ); + console.log('Running live reload server:'.green.bold, (self.host(self.liveReloadPort).bold) ); if(self.launchBrowser) { open( self.host(self.port) ); } @@ -88,7 +104,14 @@ IonicServeTask.prototype._start = function(ionic) { app.use(connect.static('www')) .listen(this.port); - console.log('Running dev server at', ( this.host(this.port) ).info.bold); + console.log('Running dev server:'.green.bold, ( this.host(this.port) ).bold); + + process.stdin.on('readable', function() { + var chunk = process.stdin.read(); + if (chunk !== null && /exit|quit|close|stop/gi.test(chunk)) { + process.exit(); + } + }); }; diff --git a/lib/ionic/setup.js b/lib/ionic/setup.js new file mode 100644 index 0000000000..cc835892be --- /dev/null +++ b/lib/ionic/setup.js @@ -0,0 +1,123 @@ +var IonicTask = require('./task').IonicTask, + IonicStats = require('./stats').IonicStats, + fs = require('fs'), + path = require('path'), + argv = require('optimist').argv, + exec = require('child_process').exec, + Q = require('q'), + IonicProject = require('./project'), + colors = require('colors'); + +var IonicSetupTask = function() {}; + +IonicSetupTask.prototype = new IonicTask(); + +IonicSetupTask.prototype.run = function(ionic) { + if( argv._.length < 2 ) { + return ionic.fail('Missing setup task command.', 'setup'); + } + + var tasks = argv._.slice(1); + + for(var x=0; x(.*?)/gi.test(line) ) { + keepLine = false; + activeRemoving = false; + } else if( /lib\/ionic\/css\/ionic.css|css\/style.css/gi.test(line) ) { + keepLine = false; + } + + if(keepLine) { + cleanedLines.push(line); + } + } + + var project = IonicProject.load(); + project.set('sass', true); + project.save(); + + fs.writeFileSync(indexPath, cleanedLines.join('\n'), 'utf8'); + + console.log('Updated '.green.bold + indexPath.bold + ' '.yellow.bold + ' references to sass compiled css'.green.bold); + + console.log('\nIonic project ready to use Sass!'.green.bold); + console.log(' * Customize the app using'.yellow.bold, 'scss/ionic.app.scss'.bold); + console.log(' * Run'.yellow.bold, 'ionic serve'.bold, 'to compile to CSS and start a local dev server'.yellow.bold); + console.log(''); + + } catch(e) { + return ionic.fail('Error parsing ' + indexPath + ': ' + e); + } + + }); +}; + + +IonicSetupTask.prototype.npmInstall = function() { + var q = Q.defer(); + + var childProcess = exec('npm install'); + + childProcess.stdout.on('data', function (data) { + process.stdout.write(data); + }); + + childProcess.stderr.on('data', function (data) { + if(data) { + data = data.toString(); + if( !/no repository field/gi.test(data) ) { + process.stderr.write(data.yellow); + } + } + }); + + childProcess.on('exit', function(code){ + process.stderr.write('\n'); + if(code === 0) { + console.log('Successful '.green.bold + 'npm install'.bold); + q.resolve(); + } else { + console.log( 'Error running '.error.bold + 'npm install'.bold ); + q.reject() + } + }); + + return q.promise; +}; + + +exports.IonicSetupTask = IonicSetupTask; From 71ac69cd5ad81f26fbac6a21299b771cec70d9ec Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sat, 9 Aug 2014 23:22:15 -0500 Subject: [PATCH 0179/1100] v1.1.9 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0b4d072aa0..f152434f79 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.9-beta1", + "version": "1.1.9", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 93d5aae155fd2897a01a393d3719868502772dd8 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Mon, 11 Aug 2014 09:22:44 -0500 Subject: [PATCH 0180/1100] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ae892be806..67329e6840 100644 --- a/README.md +++ b/README.md @@ -102,9 +102,9 @@ Note: we occasionally send anonymous usage statistics to the Ionic team to make By default, starter projects are hooked up to Ionic's precompiled CSS file, which is found in the project's `www/lib/ionic/css` directory, and is linked to the app in the head of the root `index.html` file. However, Ionic projects can also be customized using [Sass](http://sass-lang.com/), which gives developers and designers "superpowers" in terms of creating and maintaining CSS. Please follow these steps if you would like to use Sass for an Ionic project: -1) Run `npm install` from the working directory of an Ionic project. This will install the [gulp.js](http://gulpjs.com/) build system and a few handy tasks, such as [gulp-sass](https://www.npmjs.org/package/gulp-sass) and [gulp-minify-css](https://www.npmjs.org/package/gulp-minify-css). -2) Remove `` from the `` of the root `index.html` file. -3) Remove `` from the `` of the root `index.html` file. -4) Add `` to the `` of the root `index.html` file. -5) In the `ionic.project` file, add the JavaScript property `sass: true` to the object. +1. Run `npm install` from the working directory of an Ionic project. This will install [gulp.js](http://gulpjs.com/) and a few handy tasks, such as [gulp-sass](https://www.npmjs.org/package/gulp-sass) and [gulp-minify-css](https://www.npmjs.org/package/gulp-minify-css). +2. Remove `` from the `` of the root `index.html` file. +3. Remove `` from the `` of the root `index.html` file. +4. Add `` to the `` of the root `index.html` file. +5. In the `ionic.project` file, add the JavaScript property `sass: true` to the object. From 24e88dcbf6151bcdc0b34bbea87e236a99821708 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Mon, 11 Aug 2014 09:32:03 -0500 Subject: [PATCH 0181/1100] Update README.md --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 67329e6840..04da02ce8f 100644 --- a/README.md +++ b/README.md @@ -100,11 +100,17 @@ Note: we occasionally send anonymous usage statistics to the Ionic team to make ## Using Sass -By default, starter projects are hooked up to Ionic's precompiled CSS file, which is found in the project's `www/lib/ionic/css` directory, and is linked to the app in the head of the root `index.html` file. However, Ionic projects can also be customized using [Sass](http://sass-lang.com/), which gives developers and designers "superpowers" in terms of creating and maintaining CSS. Please follow these steps if you would like to use Sass for an Ionic project: +By default, starter projects are hooked up to Ionic's precompiled CSS file, which is found in the project's `www/lib/ionic/css` directory, and is linked to the app in the head of the root `index.html` file. However, Ionic projects can also be customized using [Sass](http://sass-lang.com/), which gives developers and designers "superpowers" in terms of creating and maintaining CSS. Below are two ways to setup Sass for your Ionic project (the `ionic setup sass` command simply does the manual steps for you). Once Sass has been setup for your Ionic project, then the `ionic serve` command will also watch for Sass changes. + +#### Setup Sass Automatically + + ionic setup sass + + +#### Setup Sass Manually 1. Run `npm install` from the working directory of an Ionic project. This will install [gulp.js](http://gulpjs.com/) and a few handy tasks, such as [gulp-sass](https://www.npmjs.org/package/gulp-sass) and [gulp-minify-css](https://www.npmjs.org/package/gulp-minify-css). 2. Remove `` from the `` of the root `index.html` file. 3. Remove `` from the `` of the root `index.html` file. 4. Add `` to the `` of the root `index.html` file. 5. In the `ionic.project` file, add the JavaScript property `sass: true` to the object. - From 6c7419cf38e29478d5e84b2cd59e44f781249428 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Mon, 11 Aug 2014 09:47:54 -0500 Subject: [PATCH 0182/1100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 04da02ce8f..7b755fcea9 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Command-line flags/options: ## Testing in a Browser -Use `ionic serve` to start a local development server for app dev and testing. This is useful for both desktop browser testing, and to test within a device browser which is connected to the same network. Additionally, this command starts LiveReload which is used to monitor changes in the file system. As soon as you save a file the browser is refreshed automatically. +Use `ionic serve` to start a local development server for app dev and testing. This is useful for both desktop browser testing, and to test within a device browser which is connected to the same network. Additionally, this command starts LiveReload which is used to monitor changes in the file system. As soon as you save a file the browser is refreshed automatically. View [Using Sass](https://github.com/driftyco/ionic-cli/edit/master/README.md#using-sass) if you would also like to have `ionic serve` watch the project's Sass files. ```bash $ ionic serve [http-port] [livereload-port] [options] From 338b53dfdbe8831fe37ebb3b7b0967938c0c7a40 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Mon, 11 Aug 2014 09:48:22 -0500 Subject: [PATCH 0183/1100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b755fcea9..8d43e2a2d9 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Command-line flags/options: ## Testing in a Browser -Use `ionic serve` to start a local development server for app dev and testing. This is useful for both desktop browser testing, and to test within a device browser which is connected to the same network. Additionally, this command starts LiveReload which is used to monitor changes in the file system. As soon as you save a file the browser is refreshed automatically. View [Using Sass](https://github.com/driftyco/ionic-cli/edit/master/README.md#using-sass) if you would also like to have `ionic serve` watch the project's Sass files. +Use `ionic serve` to start a local development server for app dev and testing. This is useful for both desktop browser testing, and to test within a device browser which is connected to the same network. Additionally, this command starts LiveReload which is used to monitor changes in the file system. As soon as you save a file the browser is refreshed automatically. View [Using Sass](https://github.com/driftyco/ionic-cli/blob/master/README.md#using-sass) if you would also like to have `ionic serve` watch the project's Sass files. ```bash $ ionic serve [http-port] [livereload-port] [options] From 24a00409ea38d73f5c184f0dd4edb0ad5e6343f0 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Mon, 11 Aug 2014 11:32:24 -0500 Subject: [PATCH 0184/1100] setup sass --- lib/ionic.js | 3 ++- lib/ionic/setup.js | 12 +++++++++++- lib/ionic/start.js | 16 ++++++++++++++-- lib/ionic/stats.js | 5 +++-- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index d8f7613f85..7c56cf9ac4 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -65,7 +65,8 @@ var TASKS = [ options: { '--app-name|-a': 'Human readable name for the app (Use quotes around the name)', '--id|-i': 'Package name for config, ie: com.mycompany.myapp', - '--no-cordova|-w': 'Create a basic structure without Cordova requirements' + '--no-cordova|-w': 'Create a basic structure without Cordova requirements', + '--sass|-s': 'Setup the project to use Sass CSS precompiling' }, task: IonicStartTask }, diff --git a/lib/ionic/setup.js b/lib/ionic/setup.js index cc835892be..4ec4002b85 100644 --- a/lib/ionic/setup.js +++ b/lib/ionic/setup.js @@ -32,6 +32,8 @@ IonicSetupTask.prototype.run = function(ionic) { IonicSetupTask.prototype.sassSetup = function(ionic) { + var q = Q.defer(); + this.npmInstall().then(function(){ var indexPath = path.resolve('www/index.html'); @@ -41,6 +43,7 @@ IonicSetupTask.prototype.sassSetup = function(ionic) { try { lines = fs.readFileSync(indexPath, 'utf8').split('\n'); } catch(e) { + q.reject(e); return ionic.fail('Error loading ' + indexPath); } @@ -76,14 +79,21 @@ IonicSetupTask.prototype.sassSetup = function(ionic) { console.log('\nIonic project ready to use Sass!'.green.bold); console.log(' * Customize the app using'.yellow.bold, 'scss/ionic.app.scss'.bold); - console.log(' * Run'.yellow.bold, 'ionic serve'.bold, 'to compile to CSS and start a local dev server'.yellow.bold); + console.log(' * Run'.yellow.bold, 'ionic serve'.bold, 'to start a local dev server and watch/compile Sass to CSS'.yellow.bold); console.log(''); + q.resolve(); + } catch(e) { + q.reject(e); return ionic.fail('Error parsing ' + indexPath + ': ' + e); } + }, function(e){ + q.reject(e); }); + + return q.promise; }; diff --git a/lib/ionic/start.js b/lib/ionic/start.js index c8782e7c63..e17ae2cd95 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -9,6 +9,7 @@ var fs = require('fs'), spawn = require('child_process').spawn, Q = require('q'), xml2js = require('xml2js'), + setup = require('./setup'), IonicProject = require('./project'), IonicTask = require('./task').IonicTask; IonicStats = require('./stats').IonicStats; @@ -63,7 +64,6 @@ IonicStartTask.prototype.run = function(ionic) { // Make sure to create this, or ask them if they want to override it if(this._checkTargetPath() === false) { - process.stderr.write('\nExiting.\n'); process.exit(1); } @@ -188,6 +188,7 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { var npmQ = Q.defer(); var gulpQ = Q.defer(); var pluginQ = Q.defer(); + var q = Q.defer(); // Install dependencies for the starter project // Move the content of this repo into the www folder @@ -215,7 +216,18 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { var ionicBower = require('./bower').IonicBower; ionicBower.setAppName(self.appName); - self._printQuickHelp(self.appDirectory); + if(argv.sass) { + console.log('setup sass'.green.bold); + var setupTask = new setup.IonicSetupTask(); + q = setupTask.sassSetup(self.ionic) + } else { + q.resolve(); + } + + q.then(function(){ + self._printQuickHelp(self.appDirectory); + }); + }, function(err) { return self.ionic.fail('Unable to add plugins. Perhaps your version of Cordova is too old. Try updating (npm install -g cordova), removing this project folder, and trying again.'); }); diff --git a/lib/ionic/stats.js b/lib/ionic/stats.js index d5869665bf..ffd7ab3041 100644 --- a/lib/ionic/stats.js +++ b/lib/ionic/stats.js @@ -539,7 +539,8 @@ exports.IonicStats = { '-b': '--nobrowser', '-r': '--nolivereload', '-l': '--clear-signing', - '-n': '--no-email' + '-n': '--no-email', + '-s': '--sass' }; for(x=0; x Date: Mon, 11 Aug 2014 11:33:21 -0500 Subject: [PATCH 0185/1100] api login --- lib/ionic/login.js | 65 +++++++++++++++++++-------------------------- lib/ionic/upload.js | 14 +++++++--- 2 files changed, 37 insertions(+), 42 deletions(-) diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 61f4de86d4..83b8823c25 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -97,53 +97,42 @@ IonicLoginTask.prototype.requestLogIn = function(ionic, callback, saveCookies) { var jar = request.jar(); request({ - url: ionic.IONIC_DASH + '/login', - jar: jar + method: 'POST', + url: ionic.IONIC_DASH + ionic.IONIC_API + '/user/login', + jar: jar, + form: { + username: self.email.toString().toLowerCase(), + password: self.password + }, + proxy: process.env.PROXY || null }, - function(err, response, body) { - if(err || jar.cookies.length === 0) { + function (err, response, body) { + if(err) { return ionic.fail('Error logging in: ' + err); } - request({ - method: 'POST', - url: ionic.IONIC_DASH + '/login', - jar: jar, - form: { - username: self.email.toString().toLowerCase(), - password: self.password, - csrfmiddlewaretoken: jar.cookies[0].value - }, - proxy: process.env.PROXY || null - }, - function (err, response, body) { - if(err) { - return ionic.fail('Error logging in: ' + err); - } - - // Should be a 302 redirect status code if correct - if(response.statusCode != 302) { - return ionic.fail('Email or Password incorrect. Please visit '+ ionic.IONIC_DASH.white +' for help.'.red); - } + // Should be a 302 redirect status code if correct + if(response.statusCode != 200) { + return ionic.fail('Email or Password incorrect. Please visit '+ ionic.IONIC_DASH.white +' for help.'.red); + } - if(saveCookies) { - // save cookies - if(!self.cookieData) { - self.cookieData = new IonicStore('cookies'); - } - self.cookieData.set(ionic.IONIC_DASH, jar); - self.cookieData.save(); + if(saveCookies) { + // save cookies + if(!self.cookieData) { + self.cookieData = new IonicStore('cookies'); } + self.cookieData.set(ionic.IONIC_DASH, jar); + self.cookieData.save(); + } - // save in memory - ionic.jar = jar; + // save in memory + ionic.jar = jar; - console.log('Logged in! :)'.green); + console.log('Logged in! :)'.green); - if(callback) { - callback(jar); - } - }); + if(callback) { + callback(jar); + } }); }; diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index 612e586ae9..e0400a4f6a 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -70,6 +70,16 @@ IonicUploadTask.prototype.run = function(ionic, callback) { response.setEncoding('utf8'); response.on("data", function(data) { try { + + if(response.statusCode == 401) { + q.reject('not_logged_in'); + return ionic.fail('Session expired (401). Please log in and run this command again.'); + + } else if(response.statusCode == 403) { + q.reject('forbidden'); + return ionic.fail('Forbidden upload (403)'); + } + var d = JSON.parse(data); if(d.errors && d.errors.length) { for (var j = 0; j < d.errors.length; j++) { @@ -94,10 +104,6 @@ IonicUploadTask.prototype.run = function(ionic, callback) { } } - } else if(response.statusCode == 401) { - console.error( ('Session expired. Please log in again and run this command again.').red.bold ); - q.reject('not_logged_in'); - } else { q.reject('upload_error'); } From b49d1792dd376a389fa6f6e20f23a85e49c66c48 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Tue, 12 Aug 2014 16:19:01 -0500 Subject: [PATCH 0186/1100] cli version in fail msg --- .gitignore | 1 + lib/ionic.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ccc574f1f2..d0acd17ee4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.sw* *.cookies node_modules/ +.idea/ diff --git a/lib/ionic.js b/lib/ionic.js index 7c56cf9ac4..b163fb7332 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -407,7 +407,7 @@ Ionic = { fail: function(msg, taskHelp) { this.hasFailed = true; process.stderr.write(msg.error.bold); - process.stderr.write('\n'); + process.stderr.write( (' (CLI v' + settings.version + ')').error.bold + '\n'); if(taskHelp) { for(var i = 0; i < TASKS.length; i++) { var task = TASKS[i]; From 44835e31c8a9a5a6fb903e75eb3f56304068e8b3 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 15 Aug 2014 10:07:32 -0500 Subject: [PATCH 0187/1100] build sass on setup --- lib/ionic/setup.js | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/ionic/setup.js b/lib/ionic/setup.js index 4ec4002b85..68263d22ec 100644 --- a/lib/ionic/setup.js +++ b/lib/ionic/setup.js @@ -33,6 +33,7 @@ IonicSetupTask.prototype.run = function(ionic) { IonicSetupTask.prototype.sassSetup = function(ionic) { var q = Q.defer(); + var self = this; this.npmInstall().then(function(){ @@ -82,7 +83,9 @@ IonicSetupTask.prototype.sassSetup = function(ionic) { console.log(' * Run'.yellow.bold, 'ionic serve'.bold, 'to start a local dev server and watch/compile Sass to CSS'.yellow.bold); console.log(''); - q.resolve(); + self.buildSass().then(function(){ + q.resolve(); + }); } catch(e) { q.reject(e); @@ -97,6 +100,36 @@ IonicSetupTask.prototype.sassSetup = function(ionic) { }; +IonicSetupTask.prototype.buildSass = function() { + var q = Q.defer(); + + var childProcess = exec('gulp sass'); + + childProcess.stdout.on('data', function (data) { + process.stdout.write(data); + }); + + childProcess.stderr.on('data', function (data) { + if(data) { + process.stderr.write(data.toString().yellow); + } + }); + + childProcess.on('exit', function(code){ + process.stderr.write('\n'); + if(code === 0) { + console.log('Successful '.green.bold + 'sass build'.bold); + q.resolve(); + } else { + console.log( 'Error running '.error.bold + 'gulp sass'.bold ); + q.reject() + } + }); + + return q.promise; +}; + + IonicSetupTask.prototype.npmInstall = function() { var q = Q.defer(); From a987653e07c3498b7216d896c6015942dd993756 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 15 Aug 2014 22:28:20 -0500 Subject: [PATCH 0188/1100] lower case start template name --- lib/ionic/start.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index e17ae2cd95..0c82cdf427 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -58,7 +58,7 @@ IonicStartTask.prototype.run = function(ionic) { this.isCordovaProject = (argv.cordova !== false && !argv.w); // start project template can come from cmd line args -t, --template, or the 3rd arg, and defaults to tabs - var starterProject = argv.template || argv.t || argv._[2] || 'tabs'; + var starterProject = (argv.template || argv.t || argv._[2] || 'tabs').toLowerCase(); this.targetPath = path.resolve(this.appDirectory); From 2805b39b148a05ab24541b7d92cb7f630538d7b1 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Mon, 18 Aug 2014 09:41:17 -0500 Subject: [PATCH 0189/1100] rm /www/Readme.md --- lib/ionic/start.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 0c82cdf427..26f71b674d 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -216,6 +216,10 @@ IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { var ionicBower = require('./bower').IonicBower; ionicBower.setAppName(self.appName); + try { + fs.unlinkSync(self.targetPath + '/www/README.md'); + } catch(e) {} + if(argv.sass) { console.log('setup sass'.green.bold); var setupTask = new setup.IonicSetupTask(); From ba8c9620bb488bc355db80bb0ca43e2755c12a22 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Mon, 18 Aug 2014 09:41:33 -0500 Subject: [PATCH 0190/1100] gulp sass on watch --- lib/ionic/serve.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index f62c14d40c..617fb85a7f 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -65,7 +65,7 @@ IonicServeTask.prototype._start = function(ionic) { } if(this.watchSass) { - var childProcess = spawn('gulp', ['watch']); + var childProcess = spawn('gulp', ['sass','watch']); childProcess.stdout.on('data', function (data) { process.stdout.write(data); From bca11f48276d2619cc1f03a6c9fa6ecdeabd5b1e Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Mon, 18 Aug 2014 09:41:48 -0500 Subject: [PATCH 0191/1100] 1.1.10 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f152434f79..85b51b8be3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.9", + "version": "1.1.10", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 62eba5e718ee40e90634a626057197e6b1ac0737 Mon Sep 17 00:00:00 2001 From: Steven Pautz Date: Mon, 18 Aug 2014 11:00:16 -0400 Subject: [PATCH 0192/1100] Update after_prepare hook to match tags which have no attributes. Fixes #74 --- lib/hooks/after_prepare/010_add_platform_class.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/hooks/after_prepare/010_add_platform_class.js b/lib/hooks/after_prepare/010_add_platform_class.js index cdfb187492..edd86c9065 100755 --- a/lib/hooks/after_prepare/010_add_platform_class.js +++ b/lib/hooks/after_prepare/010_add_platform_class.js @@ -52,7 +52,7 @@ function addPlatformBodyTag(indexPath, platform) { function findBodyTag(html) { // get the body tag try{ - return html.match(//gi)[0]; + return html.match(/])(.*?)>/gi)[0]; }catch(e){} } From 96496ea58f713737aebdd0ef89a5bd025d8ec426 Mon Sep 17 00:00:00 2001 From: Jamieson Warner Date: Tue, 26 Aug 2014 15:41:08 -0500 Subject: [PATCH 0193/1100] remove double slash from api login calls --- lib/ionic/login.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 83b8823c25..ce1109faae 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -98,7 +98,7 @@ IonicLoginTask.prototype.requestLogIn = function(ionic, callback, saveCookies) { var jar = request.jar(); request({ method: 'POST', - url: ionic.IONIC_DASH + ionic.IONIC_API + '/user/login', + url: ionic.IONIC_DASH + ionic.IONIC_API + 'user/login', jar: jar, form: { username: self.email.toString().toLowerCase(), From 03bdf23418c0457be98e95ac2d9944b59b7bb2b0 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Thu, 28 Aug 2014 00:22:39 -0500 Subject: [PATCH 0194/1100] start ionic apps from codepen url --- lib/ionic.js | 19 +- lib/ionic/start.js | 425 ++++++++++++++++++++++++++++++++------------- package.json | 2 +- 3 files changed, 312 insertions(+), 134 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index b163fb7332..caf294ca6a 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -406,8 +406,10 @@ Ionic = { fail: function(msg, taskHelp) { this.hasFailed = true; - process.stderr.write(msg.error.bold); - process.stderr.write( (' (CLI v' + settings.version + ')').error.bold + '\n'); + if(msg) { + process.stderr.write(msg.error.bold); + process.stderr.write( (' (CLI v' + settings.version + ')').error.bold + '\n'); + } if(taskHelp) { for(var i = 0; i < TASKS.length; i++) { var task = TASKS[i]; @@ -509,17 +511,16 @@ Ionic = { return q.promise; }, /** - * Fetch a repo from GitHub, unzip it to a specific folder. + * Download a zip file, unzip it to a specific folder. */ - fetchRepo: function(targetPath, repoName, repoUrl) { + fetchArchive: function(targetPath, archiveUrl) { var q = Q.defer(); // The folder name the project will be downloaded and extracted to - var repoFolderName = repoName + '-master'; - console.log('\nDownloading:'.info.bold, repoUrl); + console.log('\nDownloading:'.info.bold, archiveUrl); var tmpFolder = os.tmpdir(); - var tempZipFilePath = path.join(tmpFolder, repoName + new Date().getTime() + '.zip'); + var tempZipFilePath = path.join(tmpFolder, 'ionic-starter-' + new Date().getTime() + '.zip'); var tempZipFileStream = fs.createWriteStream(tempZipFilePath); var unzipRepo = function(fileName) { @@ -527,7 +528,7 @@ Ionic = { var writeStream = unzip.Extract({ path: targetPath }); writeStream.on('close', function() { - q.resolve(repoFolderName); + q.resolve(); }); writeStream.on('error', function(err) { q.reject(err); @@ -536,7 +537,7 @@ Ionic = { }; var proxy = process.env.PROXY || null; - request({ url: repoUrl, encoding: null, proxy: proxy }, function(err, res, body) { + request({ url: archiveUrl, encoding: null, proxy: proxy }, function(err, res, body) { if(!res || res.statusCode !== 200) { q.reject(res); return; diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 26f71b674d..9c71169b12 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -14,20 +14,6 @@ var fs = require('fs'), IonicTask = require('./task').IonicTask; IonicStats = require('./stats').IonicStats; -fs.mkdirParent = function(dirPath, mode, callback) { - //Call the standard fs.mkdir - fs.mkdir(dirPath, mode, function(error) { - //When it fail in this way, do the custom steps - if (error && error.errno === 34) { - //Create all the parents recursively - fs.mkdirParent(path.dirname(dirPath), mode, callback); - //And then the directory - fs.mkdirParent(dirPath, mode, callback); - } - //Manually run the callback since we used our own callback to do all these - callback && callback(error); - }); -}; var IonicStartTask = function() {}; @@ -54,43 +40,66 @@ IonicStartTask.prototype.run = function(ionic) { this.appName = appNameSplit[ appNameSplit.length -1 ]; } + // get a packge name, like com.ionic.myapp this.packageName = argv.id || argv.i; this.isCordovaProject = (argv.cordova !== false && !argv.w); // start project template can come from cmd line args -t, --template, or the 3rd arg, and defaults to tabs - var starterProject = (argv.template || argv.t || argv._[2] || 'tabs').toLowerCase(); + this.template = (argv.template || argv.t || argv._[2] || 'tabs'); + // figure out the full path this.targetPath = path.resolve(this.appDirectory); - // Make sure to create this, or ask them if they want to override it - if(this._checkTargetPath() === false) { - process.exit(1); - } - - console.log('Creating Ionic app in folder', this.targetPath, 'based on', starterProject.info.bold, 'project'); + console.log('Creating Ionic app in folder', this.targetPath, 'based on', this.template.info.bold, 'project'); - fs.mkdirSync(this.targetPath); + // Make sure to create this + if( fs.existsSync(this.targetPath) ) { + // check if its empty first + if( fs.readdirSync(this.targetPath).length ) { + // only freak if target path already has files in it + return this.ionic.fail('The directory '.error.bold + this.targetPath + ' already exists, please remove it if you would like to create a new ionic project there.'.error.bold); + } + } else { + // create the app directory + fs.mkdirSync(this.targetPath); + } - this._fetchAndWriteSeed(starterProject.toLowerCase()); + // kick it all off + this.startApp(); +}; - if(ionic.hasFailed) return; - this._writeProjectFile(ionic); +IonicStartTask.prototype.startApp = function() { + var self = this; + self.fetchWrapper() + .then(function(){ + return self.fetchSeed(); + }) + .then(function(){ + return self.initCordova(); + }) + .then(function(){ + return self.setupSass(); + }) + .then(function(){ + return self.finalize(); + }) + .catch(function(err) { + console.error('Error: Unable to initalize app: '.error.bold, err); + return self.ionic.fail(''); + }); }; -IonicStartTask.prototype._getStarterUrl = function(repo) { - return 'https://github.com/driftyco/' + repo + '/archive/master.zip' ; -}; -IonicStartTask.prototype._fetchWrapper = function() { +IonicStartTask.prototype.fetchWrapper = function() { var q = Q.defer(); var self = this; - var repoName = IonicStartTask.WRAPPER_REPO_NAME; var repoUrl = 'https://github.com/driftyco/' + IonicStartTask.WRAPPER_REPO_NAME + '/archive/master.zip'; - Ionic.fetchRepo(self.targetPath, repoName, repoUrl).then(function(repoFolderName) { + Ionic.fetchArchive(self.targetPath, repoUrl).then(function() { + var repoFolderName = IonicStartTask.WRAPPER_REPO_NAME + '-master'; cp('-R', self.targetPath + '/' + repoFolderName + '/.', self.targetPath); rm('-rf', self.targetPath + '/' + repoFolderName + '/'); cd(self.targetPath); @@ -113,25 +122,148 @@ IonicStartTask.prototype._fetchWrapper = function() { return q.promise; }; -IonicStartTask.prototype._installCordovaPlugins = function() { + +IonicStartTask.prototype.fetchSeed = function() { + + if(this.template.toLowerCase().indexOf('codepen') > -1) { + this.seedType = 'codepen'; + return this.fetchCodepen(); + } + + this.seedType = 'ionic-starter'; + return this.fetchIonicStarter(); +}; + + +IonicStartTask.prototype.fetchCodepen = function() { + // Edit Url: http://codepen.io/ionic/pen/gblny + // Download: http://codepen.io/ionic/share/zip/gblny/ + var self = this; + var splt = self.template.split('?')[0].split('#')[0].split('/'); + var wwwPath = path.join(self.targetPath, 'www'); + var codepenId = splt[ splt.length - 1 ]; + if(!codepenId) { + codepenId = splt[ splt.length - 2 ]; + } + + console.log('\nDownloading Codepen:'.info.bold, codepenId); + + var qHTML = Q.defer(); + var qCSS = Q.defer(); + var qJS = Q.defer(); + + var htmlUrl = 'http://codepen.io/ionic/pen/' + codepenId + '.html'; + var cssUrl = 'http://codepen.io/ionic/pen/' + codepenId + '.css'; + var jsUrl = 'http://codepen.io/ionic/pen/' + codepenId + '.js'; + + var proxy = process.env.PROXY || null; + + request({ url: htmlUrl, proxy: proxy }, function(err, res, html) { + if(!err && res && res.statusCode === 200 && html) { + html = '\n' + html; + + var resources = ' \n' + + ' \n\n' + + ' '; + + html = html.replace(/<\/head>/i, '\n' + resources); + + fs.writeFileSync(path.join(wwwPath, 'index.html'), html, 'utf8'); + } + qHTML.resolve(); + }); + + request({ url: cssUrl, proxy: proxy }, function(err, res, css) { + if(!err && res && res.statusCode === 200 && css) { + var cssPath = path.join(wwwPath, 'css'); + if(!fs.existsSync(cssPath)) { + fs.mkdirSync(cssPath); + } + css = css.replace("cursor: url('http://ionicframework.com/img/finger.png'), auto;", ''); + fs.writeFileSync(path.join(cssPath, 'style.css'), css, 'utf8'); + } + qCSS.resolve(); + }); + + request({ url: jsUrl, proxy: proxy }, function(err, res, js) { + if(!err && res && res.statusCode === 200 && js) { + var jsPath = path.join(wwwPath, 'js'); + if(!fs.existsSync(jsPath)) { + fs.mkdirSync(jsPath); + } + fs.writeFileSync(path.join(jsPath, 'app.js'), js, 'utf8'); + } + qJS.resolve(); + }); + + return Q.all([qHTML.promise, qCSS.promise, qJS.promise]); +}; + + +IonicStartTask.prototype.fetchIonicStarter = function() { + var self = this; var q = Q.defer(); - console.log('Initializing cordova project.'.info.bold); - exec('cordova plugin add org.apache.cordova.device && ' + - 'cordova plugin add org.apache.cordova.console && ' + - 'cordova plugin add https://github.com/driftyco/ionic-plugins-keyboard', - function(err, stdout, stderr) { - if(err) { - q.reject(stderr); - } else { - q.resolve(stdout); - } + // Get the starter project repo name: + var repoName = 'ionic-starter-' + self.template; + + // Get the URL for the starter project repo: + var repoUrl = 'https://github.com/driftyco/' + repoName + '/archive/master.zip'; + + Ionic.fetchArchive(self.targetPath, repoUrl).then(function() { + + var repoFolderName = repoName + '-master'; + + // Move the content of this repo into the www folder + cp('-Rf', self.targetPath + '/' + repoFolderName + '/.', 'www'); + + // Clean up start template folder + rm('-rf', self.targetPath + '/' + repoFolderName + '/'); + + q.resolve(); + + }).catch(function(err) { + console.error('Error: Unable to fetch project: HTTP:'.error.bold, err.statusCode); + console.error('Valid project types are "blank", "tabs", or "sidemenu"'.error.bold); + console.error('More info available at: http://ionicframework.com/getting-started/'.error.bold); + return self.ionic.fail(''); }); return q.promise; }; -IonicStartTask.prototype._updateConfigXml = function() { + +IonicStartTask.prototype.initCordova = function() { + var q = Q.defer(); + + if(this.isCordovaProject) { + // update the config.xml file from cmd line args + this.updateConfigXml(); + + if(this.ionic.hasFailed) return; + + console.log('Initializing cordova project.'.info.bold); + exec('cordova plugin add org.apache.cordova.device && ' + + 'cordova plugin add org.apache.cordova.console && ' + + 'cordova plugin add https://github.com/driftyco/ionic-plugins-keyboard', + function(err, stdout, stderr) { + if(err) { + this.ionic.fail('Unable to add plugins. Perhaps your version of Cordova is too old. Try updating (npm install -g cordova), removing this project folder, and trying again.'); + q.reject(stderr); + } else { + q.resolve(stdout); + } + }); + + } else { + q.resolve(); + } + + return q.promise; +}; + + +IonicStartTask.prototype.updateConfigXml = function() { var self = this; console.log('\nUpdate config.xml'.info.bold); @@ -168,123 +300,168 @@ IonicStartTask.prototype._updateConfigXml = function() { } }; -IonicStartTask.prototype._fetchAndWriteSeed = function(projectType) { - var self = this; - // First, grab the wrapper project. - var wrapperPromise = this._fetchWrapper(); +IonicStartTask.prototype.setupSass = function() { + if(argv.sass) { + // auto setup sass if they set the option + console.log('setup sass'.green.bold); + var setupTask = new setup.IonicSetupTask(); + return setupTask.sassSetup(this.ionic); + } + + // didn't ask to setup sass, so resolve it + var q = Q.defer(); + q.resolve(); + return q.promise; +}; - wrapperPromise.then(function() { - // Get the starter project repo name: - var repoName = 'ionic-starter-' + projectType; - // The folder name the project will be downloaded and extracted to - var repoFolderName = repoName + '-master'; +IonicStartTask.prototype.updateLibFiles = function() { + var libPath = argv.lib || argv.l || 'lib/ionic'; - // Get the URL for the starter project repo: - var repoUrl = self._getStarterUrl(repoName); + if(libPath == 'lib' && this.seedType == 'ionic-starter') { + // don't bother if it still will be the default + return; + } - Ionic.fetchRepo(self.targetPath, repoName, repoUrl).then(function(repoFolderName) { - var npmQ = Q.defer(); - var gulpQ = Q.defer(); - var pluginQ = Q.defer(); - var q = Q.defer(); - // Install dependencies for the starter project + var libFiles = 'ionic.css ionic.min.css ionic.js ionic.min.js ionic.bundle.js ionic.bundle.min.js ionic-angular.js ionic-angular.min.js'.split(' '); - // Move the content of this repo into the www folder - cp('-Rf', self.targetPath + '/' + repoFolderName + '/.', 'www'); + function changeLibPath(originalUrl) { + var splt = originalUrl.split('/'); + var newUrl = [ libPath ]; + var filename = splt[ splt.length - 1 ]; - // Clean up start template folder - rm('-rf', self.targetPath + '/' + repoFolderName + '/'); + if(filename.indexOf('.css') > -1) { + newUrl.push('css'); + } else if(filename.indexOf('.js') > -1) { + newUrl.push('js'); + } - if(self.isCordovaProject) { - // update the config.xml file from cmd line args - self._updateConfigXml(); + newUrl.push(filename); - if(self.ionic.hasFailed) return; + return newUrl.join('/'); + } - self._installCordovaPlugins().then(function(output) { - pluginQ.resolve(); - }, function(err) { - pluginQ.reject(err); - }); + function replaceResource(html, originalTag) { + originalTag = originalTag.replace(/'/g, '"'); + var splt = originalTag.split('"'); + var newTagArray = []; + + for(var x=0; x -1 ) { + return true; + } + } + } - q.then(function(){ - self._printQuickHelp(self.appDirectory); - }); + function getLibTags(html) { + var resourceTags = []; + var libTags = []; - }, function(err) { - return self.ionic.fail('Unable to add plugins. Perhaps your version of Cordova is too old. Try updating (npm install -g cordova), removing this project folder, and trying again.'); - }); + try{ + resourceTags = resourceTags.concat( html.match(/\n\n' + + ' \n' + ' '; html = html.replace(/<\/head>/i, '\n' + resources); + html = self.convertTemplates(html); + fs.writeFileSync(path.join(wwwPath, 'index.html'), html, 'utf8'); } qHTML.resolve(); }); - request({ url: cssUrl, proxy: proxy }, function(err, res, css) { + request({ url: codepenUrl + '.css', proxy: proxy }, function(err, res, css) { if(!err && res && res.statusCode === 200 && css) { var cssPath = path.join(wwwPath, 'css'); if(!fs.existsSync(cssPath)) { @@ -185,7 +180,7 @@ IonicStartTask.prototype.fetchCodepen = function() { qCSS.resolve(); }); - request({ url: jsUrl, proxy: proxy }, function(err, res, js) { + request({ url: codepenUrl + '.js', proxy: proxy }, function(err, res, js) { if(!err && res && res.statusCode === 200 && js) { var jsPath = path.join(wwwPath, 'js'); if(!fs.existsSync(jsPath)) { @@ -200,6 +195,64 @@ IonicStartTask.prototype.fetchCodepen = function() { }; +IonicStartTask.prototype.convertTemplates = function(html) { + var templates = []; + var self = this; + + try { + var scripts = html.match(/\n' + - ' '; + ' \n'; + + if(self.isCordovaProject) { + resources += ' \n'; + } + + resources += ' '; html = html.replace(/<\/head>/i, '\n' + resources); From a4f1e251b8e7338015c25940d817d8240d9f580b Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 29 Aug 2014 12:17:31 -0500 Subject: [PATCH 0198/1100] update help printout --- lib/ionic.js | 56 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index caf294ca6a..2a6013b341 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -60,11 +60,13 @@ var TASKS = [ args: { '[options]': 'any flags for the command', '': 'directory for the new project', - '[template]': 'Starter template to use (tabs, sidemenu, blank)' + '[template]': 'Template name, ex: tabs, sidemenu, blank\n' + + 'Codepen url, ex: http://codepen.io/ionic/pen/odqCz\n' + + 'Defaults to Ionic "tabs" starter template' }, options: { '--app-name|-a': 'Human readable name for the app (Use quotes around the name)', - '--id|-i': 'Package name for config, ie: com.mycompany.myapp', + '--id|-i': 'Package name for config, ex: com.mycompany.myapp', '--no-cordova|-w': 'Create a basic structure without Cordova requirements', '--sass|-s': 'Setup the project to use Sass CSS precompiling' }, @@ -76,7 +78,7 @@ var TASKS = [ summary: 'Start a local development server for app dev and testing', args: { '[options]': '', - '[http-port]': '', + '[port]': '', '[livereload-port]': '' }, options: { @@ -138,8 +140,8 @@ var TASKS = [ '': 'Can be a plugin ID, a local path, or a git URL.' }, options: { - '--searchpath ': 'When looking up plugins by ID, look in this directory and\n\ - each of its subdirectories for the plugin before hitting the registry.' + '--searchpath ': 'When looking up plugins by ID, look in this directory and\n' + + 'subdirectories for the plugin before using the registry.' }, task: IonicCordovaTask }, @@ -157,7 +159,7 @@ var TASKS = [ title: 'package', name: 'package', alt: ['pack'], - summary: 'Package an app using the Ionic Build service', + summary: 'Package an app using the Ionic Build service' + ' (beta)'.yellow, args: { '[options]': '', '': '"debug" or "release"', @@ -193,13 +195,13 @@ var TASKS = [ { title: 'lib', name: 'lib', - summary: 'Gets Ionic library version info or updates the Ionic library', + summary: 'Ionic library version info or updates the Ionic library', args: { '[options]': '', - '[update]': 'Updates the Ionic Framework version in www/lib/ionic' + '[update]': 'Updates the Ionic Framework in www/lib/ionic' }, options: { - '--version|-v': 'Exact Ionic version, otherwise it will default to the latest' + '--version|-v': 'Ionic version, otherwise it will default to the latest' }, task: IonicLibTask }, @@ -257,7 +259,7 @@ Ionic = { w('\n'); - var rightColumn = 47; + var rightColumn = 41; var dots = ''; var indent = ''; var x, arg; @@ -282,14 +284,28 @@ Ionic = { for(arg in d.args) { if( !d.args[arg] ) continue; - var argLine = arg; indent = ''; w('\n'); while(indent.length < rightColumn) { indent += ' '; } - w( (indent + ' ' + argLine + ' ' + d.args[arg]).bold ); + w( (indent + ' ' + arg + ' ').bold ); + + var argDescs = d.args[arg].split('\n'); + var argIndent = indent + ' '; + + for(x=0; x Date: Fri, 29 Aug 2014 12:18:00 -0500 Subject: [PATCH 0199/1100] fix obj reference --- lib/ionic/start.js | 4 +++- package.json | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 9f06ea1ace..0e7083060f 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -317,6 +317,7 @@ IonicStartTask.prototype.fetchIonicStarter = function() { IonicStartTask.prototype.initCordova = function() { + var self = this; var q = Q.defer(); if(this.isCordovaProject) { @@ -331,7 +332,8 @@ IonicStartTask.prototype.initCordova = function() { 'cordova plugin add https://github.com/driftyco/ionic-plugins-keyboard', function(err, stdout, stderr) { if(err) { - this.ionic.fail('Unable to add plugins. Perhaps your version of Cordova is too old. Try updating (npm install -g cordova), removing this project folder, and trying again.'); + self.ionic.fail('Unable to add plugins. Perhaps your version of Cordova is too old. ' + + 'Try updating (npm install -g cordova), removing this project folder, and trying again.'); q.reject(stderr); } else { q.resolve(stdout); diff --git a/package.json b/package.json index 65ed3ac00e..5f3e09afe1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.11", + "version": "1.1.12-beta1", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From bd8f695b60527188abba2e38d3ea0d4a2dd60779 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 29 Aug 2014 13:48:36 -0500 Subject: [PATCH 0200/1100] use cordova plugin add com.ionic.keyboard --- lib/ionic/start.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 0e7083060f..c7338606aa 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -329,7 +329,7 @@ IonicStartTask.prototype.initCordova = function() { console.log('Initializing cordova project.'.info.bold); exec('cordova plugin add org.apache.cordova.device && ' + 'cordova plugin add org.apache.cordova.console && ' + - 'cordova plugin add https://github.com/driftyco/ionic-plugins-keyboard', + 'cordova plugin add com.ionic.keyboard', function(err, stdout, stderr) { if(err) { self.ionic.fail('Unable to add plugins. Perhaps your version of Cordova is too old. ' + From a5a386380ce28334841bca5812dbaa3fa00dba93 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 29 Aug 2014 13:48:48 -0500 Subject: [PATCH 0201/1100] 1.1.12 --- README.md | 25 +++++++++++++++++-------- package.json | 2 +- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 8d43e2a2d9..445e299d73 100644 --- a/README.md +++ b/README.md @@ -14,19 +14,28 @@ $ sudo npm install -g ionic ## Starting an Ionic App ```bash -$ ionic start myApp [template] +$ ionic start myapp [template] ``` -There are three choices of templates: +Starter templates can either come from a named template, or from any Codepen URL. -* Side-Menu (sidemenu) -* Tabs (tabs) -* Blank (blank) +Named template starters: + +* [tabs](https://github.com/driftyco/ionic-starter-tabs) (Default) +* [sidemenu](https://github.com/driftyco/ionic-starter-sidemenu) +* [blank](https://github.com/driftyco/ionic-starter-blank) + +Codepen URL starters: + +* Any Codepen url, ex: [http://codepen.io/ionic/pen/odqCz](http://codepen.io/ionic/pen/odqCz) +* [Ionic Codepen Demos](http://codepen.io/ionic/public-list/) Command-line flags/options: - --app-name, -a ...... Human readable name for the app (Use quotes around the name) - --id, -i ............ Package name set in the config, ie: com.mycompany.myapp + --app-name, -a ...... Human readable name for the app + (Use quotes around the name) + --id, -i ............ Package name set in the config + ex: com.mycompany.myapp --no-cordova, -w .... Do not create an app targeted for Cordova @@ -71,7 +80,7 @@ $ ionic run ios ## Update Ionic lib Update Ionic library files, which are found in the `www/lib/ionic` directory. If bower is being used -by the project, this command will automatically run `bower update ionic`, otherwise this command updates +by the project, this command will automatically run `bower update ionic`, otherwise this command updates the local static files from Ionic's CDN. ```bash diff --git a/package.json b/package.json index 5f3e09afe1..06a22f33fd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.12-beta1", + "version": "1.1.12", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From ee8238b3e5088cd94fa5f773f0b8c882932de218 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sat, 30 Aug 2014 21:16:51 -0500 Subject: [PATCH 0202/1100] fix start stats --- lib/ionic.js | 8 ++++++++ lib/ionic/start.js | 2 ++ lib/ionic/stats.js | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/lib/ionic.js b/lib/ionic.js index 2a6013b341..bf0dd6f2f1 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -423,6 +423,14 @@ Ionic = { return this._printHelpLines(); } + if(argv['stats-opt-out']) { + var ionicConfig = new IonicStore('ionic.config'); + ionicConfig.set('statsOptOut', true); + ionicConfig.save(); + console.log('Successful stats opt-out'); + return; + } + var task = this._tryBuildingTask(); if(!task) { return this._printAvailableTasks(); diff --git a/lib/ionic/start.js b/lib/ionic/start.js index c7338606aa..b933496bfe 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -531,6 +531,8 @@ IonicStartTask.prototype.finalize = function() { } catch(e) {} this.printQuickHelp(); + + IonicStats.t(); }; diff --git a/lib/ionic/stats.js b/lib/ionic/stats.js index ffd7ab3041..7c47ecd853 100644 --- a/lib/ionic/stats.js +++ b/lib/ionic/stats.js @@ -523,6 +523,10 @@ exports.IonicStats = { if(process.argv.length < 3) return; + if( ionicConfig.get('statsOptOut') === true ) { + return; + } + var cmdName = process.argv[2].toLowerCase(); var cmdArgs = (process.argv.length > 3 ? process.argv.slice(3) : []); // skip the cmdName From 86d3e1c86d2778511ee1a6af7acac058bfd502e4 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sun, 31 Aug 2014 01:17:53 -0500 Subject: [PATCH 0203/1100] live reload for run command --- README.md | 12 +++ lib/ionic.js | 5 +- lib/ionic/cordova.js | 181 ++++++++++++++++++++++++++++++++++++++++++- lib/ionic/serve.js | 73 ++++++++++------- package.json | 2 +- 5 files changed, 239 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 445e299d73..9195d8ee84 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,18 @@ $ ionic emulate ios $ ionic run ios ``` +Command-line flags/options: + + [--live-reload|-l] ... Live reload app dev files from the device (beta) + [--debug|--release] + +### Live Reload Within Cordova + +The `run` command will deploy the app to the specified platform devices and emulators. You can also run __live reload__ on the specified platform device by adding the `--live-reload` option. The `run` command's live reload functionality is similar to `ionic serve`, but instead of using a standard browser, the Cordova app itself is watching for any changes to its files and reloading when needed. This reduces the requirement to constantly rebuild the Cordova app for small changes. However, any changes to plugins will still require a full rebuild. + +In order for live reload to work, symlinks are automatically created and added to the `www` root, and the `` node in the config.xml file is updated to point to the dev server created by Ionic's CLI. On exit (by typing "exit" or using ctrl+c), the app's original settings will be restored and the symlinks should automatically be deleted. __Currently in beta__ + + ## Update Ionic lib Update Ionic library files, which are found in the `www/lib/ionic` directory. If bower is being used diff --git a/lib/ionic.js b/lib/ionic.js index bf0dd6f2f1..4142cfd653 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -21,7 +21,7 @@ var IonicStartTask = require('./ionic/start').IonicStartTask, IonicPackageTask = require('./ionic/package').IonicPackageTask, IonicLibTask = require('./ionic/lib').IonicLibTask, IonicSetupTask = require('./ionic/setup').IonicSetupTask, - IonicServeTask = require('./ionic/serve'), + IonicServeTask = require('./ionic/serve').IonicServeTask, IonicProject = require('./ionic/project'), IonicStore = require('./ionic/store').IonicStore, path = require('path'), @@ -116,6 +116,7 @@ var TASKS = [ '': '' }, options: { + '--live-reload|-l': 'Live reload app dev files from the device' + ' (beta)'.yellow, '--debug|--release': '', '--device|--emulator|--target=FOO': '' }, @@ -159,7 +160,7 @@ var TASKS = [ title: 'package', name: 'package', alt: ['pack'], - summary: 'Package an app using the Ionic Build service' + ' (beta)'.yellow, + summary: 'Package an app using the Ionic Build service' + ' (beta)'.yellow, args: { '[options]': '', '': '"debug" or "release"', diff --git a/lib/ionic/cordova.js b/lib/ionic/cordova.js index 2d97530118..27569334bd 100644 --- a/lib/ionic/cordova.js +++ b/lib/ionic/cordova.js @@ -1,6 +1,8 @@ var IonicTask = require('./task').IonicTask, IonicStats = require('./stats').IonicStats, fs = require('fs'), + argv = require('optimist').argv, + xml2js = require('xml2js'), path = require('path'), exec = require('child_process').exec, colors = require('colors'); @@ -10,9 +12,17 @@ var IonicCordovaTask = function() {}; IonicCordovaTask.prototype = new IonicTask(); IonicCordovaTask.prototype.run = function(ionic) { + var self = this; + self.ionic = ionic; var cmdName = process.argv[2].toLowerCase(); var cmdArgs = (process.argv.length > 3 ? process.argv.slice(3) : []); + self.isLiveReload = (cmdName == 'run' && (argv.livereload || argv['live-reload'] || argv.l)); + + if(self.isLiveReload) { + self.setupLiveReload(); + } + // backwards compatibility prior to fully wrapping cordova cmds if(cmdName == 'platform') { // `ionic platform ` used to actually run `ionic platform add ` @@ -37,22 +47,187 @@ IonicCordovaTask.prototype.run = function(ionic) { cmdArgs.unshift(cmdName); - var cordovaCmd = exec('cordova ' + cmdArgs.join(' ')); + var cordovaProcess = exec('cordova ' + cmdArgs.join(' ')); - cordovaCmd.stdout.on('data', function (data) { + cordovaProcess.stdout.on('data', function (data) { process.stdout.write(data); }); - cordovaCmd.stderr.on('data', function (data) { + cordovaProcess.stderr.on('data', function (data) { if(data) { process.stderr.write(data.toString().error.bold); } }); + cordovaProcess.on('exit', function(){ + if(self.isLiveReload) { + self.wwwSymlinks(true); + self.setConfigXml({ + resetContent: true + }); + } + }); + + if(self.isLiveReload) { + if (process.platform === 'win32'){ + // emit SIGINT on windows + var readline = require ('readline'), + rl = readline.createInterface ({ + input: process.stdin, + output: process.stdout + }); + + rl.on ('SIGINT', function (){ + process.emit('SIGINT'); + }); + } + + process.on('SIGINT', function(){ + // doing this so the exit event fires on Cmd + c + process.exit(); + }); + process.on('exit', function() { + // remove the symlinks + self.wwwSymlinks(false); + }); + } + IonicStats.t(); }; +IonicCordovaTask.prototype.setupLiveReload = function() { + console.log('Setup Live Reload'.green.bold); + + var serve = new require('./serve'); + var serveTask = new serve.IonicServeTask(); + serveTask.loadSettings(); + serveTask.runLivereload = true; + serveTask.launchBrowser = false; + serveTask.start(this.ionic); + + this.setConfigXml({ + devServer: serveTask.devServer + }); +}; + + +IonicCordovaTask.prototype.setConfigXml = function(options) { + var self = this; + var madeChange = false; + + try { + var configXmlPath = path.resolve('config.xml'); + var configString = fs.readFileSync(configXmlPath, { encoding: 'utf8' }); + + var parseString = xml2js.parseString; + parseString(configString, function (err, jsonConfig) { + if(err) { + return self.ionic.fail('Error parsing config.xml: ' + err); + } + + if(options.devServer) { + if( !jsonConfig.widget.content[0].$['original-src'] ) { + jsonConfig.widget.content[0].$['original-src'] = jsonConfig.widget.content[0].$.src; + madeChange = true; + } + if(jsonConfig.widget.content[0].$.src !== options.devServer) { + jsonConfig.widget.content[0].$.src = options.devServer; + madeChange = true; + } + + } else if(options.resetContent) { + if( jsonConfig.widget.content[0].$['original-src'] ) { + jsonConfig.widget.content[0].$.src = jsonConfig.widget.content[0].$['original-src']; + delete jsonConfig.widget.content[0].$['original-src']; + madeChange = true; + } + } + + if(madeChange) { + var xmlBuilder = new xml2js.Builder(); + configString = xmlBuilder.buildObject(jsonConfig); + + fs.writeFileSync(configXmlPath, configString); + } + + }); + + } catch(e) { + return self.ionic.fail('Error updating config.xml file: ' + e); + } +}; + + +IonicCordovaTask.prototype.wwwSymlinks = function(createSymlinks) { + var platformCordovaJs, platformPluginJs, platformPluginsDir; + + var wwwCordovaJs = path.resolve( path.join('www', 'cordova.js') ); + var wwwPluginJs = path.resolve( path.join('www', 'cordova_plugins.js') ); + var wwwPluginsDir = path.resolve( path.join('www', 'plugins') ); + + if(createSymlinks) { + + var platform; + var platforms = 'android ios'.split(' '); + for(var x=0; x -1) continue; ifaces[dev].forEach(function(details){ if (details.family == 'IPv4' && !details.internal) { addresses.push(details.address); @@ -134,4 +151,4 @@ IonicServeTask.prototype.host = function(port) { return 'http://' + (addresses.length === 1 ? addresses[0] : 'localhost') + ':' + port; }; -module.exports = IonicServeTask; +exports.IonicServeTask = IonicServeTask; diff --git a/package.json b/package.json index 06a22f33fd..8ca102be71 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.12", + "version": "1.1.13", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From bdeaba3167eab0c0f0f67316538622efe1dc8738 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sun, 31 Aug 2014 01:21:58 -0500 Subject: [PATCH 0204/1100] update --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9195d8ee84..0d00ccc88d 100644 --- a/README.md +++ b/README.md @@ -82,11 +82,11 @@ Command-line flags/options: [--live-reload|-l] ... Live reload app dev files from the device (beta) [--debug|--release] -### Live Reload Within Cordova +### Live Reload Within Cordova (beta) The `run` command will deploy the app to the specified platform devices and emulators. You can also run __live reload__ on the specified platform device by adding the `--live-reload` option. The `run` command's live reload functionality is similar to `ionic serve`, but instead of using a standard browser, the Cordova app itself is watching for any changes to its files and reloading when needed. This reduces the requirement to constantly rebuild the Cordova app for small changes. However, any changes to plugins will still require a full rebuild. -In order for live reload to work, symlinks are automatically created and added to the `www` root, and the `` node in the config.xml file is updated to point to the dev server created by Ionic's CLI. On exit (by typing "exit" or using ctrl+c), the app's original settings will be restored and the symlinks should automatically be deleted. __Currently in beta__ +In order for live reload to work, symlinks are automatically created and added to the `www` root, and the `` node in the config.xml file is updated to point to the dev server created by Ionic's CLI. On exit (by typing "exit" or using ctrl+c), the app's original settings will be restored and the symlinks will be removed. __Currently in beta.__ ## Update Ionic lib From 5dea143c3e7ffe561633768d81b95a3cd9df1b79 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Tue, 2 Sep 2014 15:17:22 -0500 Subject: [PATCH 0205/1100] app live reload --- README.md | 48 ++++++---- lib/ionic.js | 53 ++++++----- lib/ionic/cordova.js | 108 +++-------------------- lib/ionic/lib.js | 3 +- lib/ionic/serve.js | 206 ++++++++++++++++++++++++++++++++++++++++--- lib/ionic/start.js | 44 ++++++++- package.json | 4 +- 7 files changed, 316 insertions(+), 150 deletions(-) diff --git a/README.md b/README.md index 0d00ccc88d..2f2157598d 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,12 @@ Use the `ionic --help` command for more detailed task information. ## Installing ```bash -$ sudo npm install -g ionic +$ npm install -g ionic ``` +Note: For a global install of `-g ionic`, OSX/Linux users may need to prefix the command with `sudo`. + + ## Starting an Ionic App ```bash @@ -32,7 +35,7 @@ Codepen URL starters: Command-line flags/options: - --app-name, -a ...... Human readable name for the app + --appname, -a ....... Human readable name for the app (Use quotes around the name) --id, -i ............ Package name set in the config ex: com.mycompany.myapp @@ -44,13 +47,17 @@ Command-line flags/options: Use `ionic serve` to start a local development server for app dev and testing. This is useful for both desktop browser testing, and to test within a device browser which is connected to the same network. Additionally, this command starts LiveReload which is used to monitor changes in the file system. As soon as you save a file the browser is refreshed automatically. View [Using Sass](https://github.com/driftyco/ionic-cli/blob/master/README.md#using-sass) if you would also like to have `ionic serve` watch the project's Sass files. ```bash -$ ionic serve [http-port] [livereload-port] [options] +$ ionic serve [options] ``` Command-line flags/options: - [--nobrowser|-b] ...... Disable launching a browser - [--nolivereload|-r] ... Do not start live reload + [--consolelogs|-c] ...... Print app console logs to Ionic CLI + [--serverlogs|-s] ....... Print dev server logs to Ionic CLI + [--port|-p] ............. Dev server HTTP port (8100 default) + [--livereload-port|-i] .. Live Reload port (35729 default) + [--nobrowser|-b] ........ Disable launching a browser + [--nolivereload|-r] ..... Do not start live reload ## Adding a platform target @@ -65,28 +72,37 @@ $ ionic platform ios android $ ionic build ios ``` -## Emulating your app - -```bash -$ ionic emulate ios -``` - ## Running your app +Deploys the Ionic app on specified platform devices. + ```bash -$ ionic run ios +$ ionic run ios [options] ``` Command-line flags/options: - [--live-reload|-l] ... Live reload app dev files from the device (beta) + [--livereload|-l] ....... Live Reload app dev files from the device (beta) + [--consolelogs|-c] ...... Print app console logs to Ionic CLI (live reload req.) + [--serverlogs|-s] ....... Print dev server logs to Ionic CLI (live reload req.) + [--port|-p] ............. Dev server HTTP port (8100 default, live reload req.) + [--livereload-port|-i] .. Live Reload port (35729 default, live reload req.) [--debug|--release] -### Live Reload Within Cordova (beta) +### Live Reload App During Development (beta) -The `run` command will deploy the app to the specified platform devices and emulators. You can also run __live reload__ on the specified platform device by adding the `--live-reload` option. The `run` command's live reload functionality is similar to `ionic serve`, but instead of using a standard browser, the Cordova app itself is watching for any changes to its files and reloading when needed. This reduces the requirement to constantly rebuild the Cordova app for small changes. However, any changes to plugins will still require a full rebuild. +The `run` or `emulate` command will deploy the app to the specified platform devices/emulators. You can also run __live reload__ on the specified platform device by adding the `--livereload` option. The live reload functionality is similar to `ionic serve`, but instead of developing and debugging an app using a standard browser, the compiled hybrid app itself is watching for any changes to its files and reloading the app when needed. This reduces the requirement to constantly rebuild the app for small changes. However, any changes to plugins will still require a full rebuild. For live reload to work, the dev machine and device must be on the same local network. -In order for live reload to work, symlinks are automatically created and added to the `www` root, and the `` node in the config.xml file is updated to point to the dev server created by Ionic's CLI. On exit (by typing "exit" or using ctrl+c), the app's original settings will be restored and the symlinks will be removed. __Currently in beta.__ +With live reload enabled, an app's console logs can also be printed to the terminal/command prompt by including the `--consolelogs` or `-c` option. Additionally, the development server's request logs can be printed out using `--serverlogs` or `-s` options. + + +## Emulating your app + +Deploys the Ionic app on specified platform emulator. This is simply an alias for `run --emulator`. Live reload options are the same as the `run` options listed above. + +```bash +$ ionic emulate ios [options] +``` ## Update Ionic lib diff --git a/lib/ionic.js b/lib/ionic.js index 4142cfd653..d38f10792c 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -77,13 +77,15 @@ var TASKS = [ name: 'serve', summary: 'Start a local development server for app dev and testing', args: { - '[options]': '', - '[port]': '', - '[livereload-port]': '' + '[options]': '' }, options: { - '--nobrowser|-b': 'Disable auto browser launch', - '--nolivereload|-r': 'Do not start live reload' + '--consolelogs|-c': 'Print app console logs to Ionic CLI', + '--serverlogs|-s': 'Print dev server logs to Ionic CLI', + '--port|-p': 'Dev server HTTP port (8100 default)', + '--livereload-port|-r': 'Live Reload port (35729 default)', + '--nobrowser|-b': 'Disable launching a browser', + '--nolivereload': 'Do not start live reload' }, task: IonicServeTask }, @@ -98,25 +100,38 @@ var TASKS = [ task: IonicCordovaTask }, { - title: 'emulate', - name: 'emulate', - summary: 'Emulate an Ionic project on a simulator or emulator', + title: 'run', + name: 'run', + summary: 'Run an Ionic project on a connected device', args: { '[options]': '', '': '' }, + options: { + '--livereload|-l': 'Live reload app dev files from the device' + ' (beta)'.yellow, + '--port|-p': 'Dev server HTTP port (8100 default, livereload required)', + '--livereload-port|-r': 'Live Reload port (35729 default, livereload req.)', + '--consolelogs|-c': 'Print app console logs to Ionic CLI (livereload req.)', + '--serverlogs|-s': 'Print dev server logs to Ionic CLI (livereload req.)', + '--debug|--release': '', + '--device|--emulator|--target=FOO': '' + }, task: IonicCordovaTask }, { - title: 'run', - name: 'run', - summary: 'Run an ionic project on a connected device', + title: 'emulate', + name: 'emulate', + summary: 'Emulate an Ionic project on a simulator or emulator', args: { '[options]': '', '': '' }, options: { - '--live-reload|-l': 'Live reload app dev files from the device' + ' (beta)'.yellow, + '--livereload|-l': 'Live reload app dev files from the device' + ' (beta)'.yellow, + '--port|-p': 'Dev server HTTP port (8100 default, livereload required)', + '--livereload-port|-r': 'Live Reload port (35729 default, livereload req.)', + '--consolelogs|-c': 'Print app console logs to Ionic CLI (livereload req.)', + '--serverlogs|-s': 'Print dev server logs to Ionic CLI (livereload req.)', '--debug|--release': '', '--device|--emulator|--target=FOO': '' }, @@ -125,7 +140,7 @@ var TASKS = [ { title: 'build', name: 'build', - summary: 'Locally build an ionic project for a given platform', + summary: 'Locally build an Ionic project for a given platform', args: { '[options]': '', '': '' @@ -196,13 +211,13 @@ var TASKS = [ { title: 'lib', name: 'lib', - summary: 'Ionic library version info or updates the Ionic library', + summary: 'Gets Ionic library version or updates the Ionic library', args: { '[options]': '', '[update]': 'Updates the Ionic Framework in www/lib/ionic' }, options: { - '--version|-v': 'Ionic version, otherwise it will default to the latest' + '--version|-v': 'Spcific Ionic version, otherwise it defaults to the latest' }, task: IonicLibTask }, @@ -349,7 +364,7 @@ Ionic = { _printAvailableTasks: function() { this._printIonic(); - process.stderr.write('\nUsage: ionic task args\n\n===============\n\n'); + process.stderr.write('\nUsage: ionic task args\n\n=======================\n\n'); if(process.argv.length > 2) { process.stderr.write( (process.argv[2] + ' is not a valid task\n\n').bold.red ); @@ -403,10 +418,6 @@ Ionic = { }, - _loadTaskRunner: function(which) { - - }, - run: function() { var self = this; @@ -462,7 +473,7 @@ Ionic = { }, version: function() { - process.stderr.write('v' + settings.version + '\n'); + process.stderr.write(settings.version + '\n'); }, checkLatestVersion: function() { diff --git a/lib/ionic/cordova.js b/lib/ionic/cordova.js index 27569334bd..5db1181e6f 100644 --- a/lib/ionic/cordova.js +++ b/lib/ionic/cordova.js @@ -17,7 +17,18 @@ IonicCordovaTask.prototype.run = function(ionic) { var cmdName = process.argv[2].toLowerCase(); var cmdArgs = (process.argv.length > 3 ? process.argv.slice(3) : []); - self.isLiveReload = (cmdName == 'run' && (argv.livereload || argv['live-reload'] || argv.l)); + // clean out any cmds that may confuse cordova + var port = argv.port || argv.p || ''; + var liveReloadPort = argv.livereloadport || argv['livereload-port'] || argv.i || ''; + if(port || liveReloadPort) { + for(var x=0; x -1 || ua.indexOf('ipad') > -1 || ua.indexOf('ipod') > -1) { + platformPath = path.join('platforms', 'ios', 'www'); + + } else if(ua.indexOf('android') > -1) { + platformPath = path.join('platforms', 'android', 'assets', 'www'); + } + } + + return platformPath; +} + + +function insertConsoleLogScript(html) { + try{ + var headTag = html.match(//gi)[0]; + + return html.replace(headTag, headTag + '\n\ + '); + }catch(e){} + + return html; +} + IonicServeTask.prototype._changed = function(filePath) { // Cleanup the path a bit var pwd = process.cwd(); filePath = filePath.replace(pwd + '/', ''); - console.log( (' changed: ' + filePath).green ); + if( filePath.indexOf('.css') > 0 ) { + console.log( (' CSS changed: ' + filePath).green ); + } else if( filePath.indexOf('.js') > 0 ) { + console.log( (' JS changed: ' + filePath).green ); + } else if( filePath.indexOf('.html') > 0 ) { + console.log( ('HTML changed: ' + filePath).green ); + } else { + console.log( ('File changed: ' + filePath).green ); + } var req = request.post('http://localhost:' + this.liveReloadPort + '/changed', { path: '/changed', diff --git a/lib/ionic/start.js b/lib/ionic/start.js index b933496bfe..33d5dfc7f5 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -35,7 +35,7 @@ IonicStartTask.prototype.run = function(ionic) { this.appDirectory = argv._[1]; // Grab the name of the app from -a or --app. Defaults to appDirectory if none provided - this.appName = argv['app-name'] || argv.a; + this.appName = argv.appname || argv['app-name'] || argv.a; if(!this.appName) { var appNameSplit = this.appDirectory.split('/'); appNameSplit = appNameSplit[ appNameSplit.length -1 ].split('\\'); @@ -267,7 +267,7 @@ IonicStartTask.prototype.convertTemplates = function(html) { var tmpPath = path.join(self.targetPath, 'www', path.dirname(tmpl.path)); if(!fs.existsSync(tmpPath)) { - fs.mkdirSync(tmpPath) + fs.mkdirSync(tmpPath); } tmpPath = path.join(self.targetPath, 'www', tmpl.path); @@ -404,11 +404,46 @@ IonicStartTask.prototype.setupSass = function() { IonicStartTask.prototype.updateLibFiles = function() { var libPath = argv.lib || argv.l || 'lib/ionic'; - if(libPath == 'lib' && this.seedType == 'ionic-starter') { - // don't bother if it still will be the default + // create a symlink if the path exists locally + var libSymlinkPath = path.resolve(libPath); + if( fs.existsSync(libSymlinkPath) ) { + // rename the existing lib/ionic directory before creating symlink + var wwwIonicCssPath = path.resolve('www/lib/ionic/css'); + if( fs.existsSync(wwwIonicCssPath) ) { + mv( wwwIonicCssPath, path.resolve('www/lib/ionic/css_local') ); + } + + var wwwIonicJsPath = path.resolve('www/lib/ionic/js'); + if( fs.existsSync(wwwIonicJsPath) ) { + mv( wwwIonicJsPath, path.resolve('www/lib/ionic/js_local') ); + } + + var wwwIonicFontsPath = path.resolve('www/lib/ionic/fonts'); + if( fs.existsSync(wwwIonicFontsPath) ) { + mv( wwwIonicFontsPath, path.resolve('www/lib/ionic/fonts_local') ); + } + + var libCssSymlinkPath = path.join(libSymlinkPath, 'css'); + console.log('Create www/lib/ionic/css symlink to ' + libCssSymlinkPath); + fs.symlinkSync(libCssSymlinkPath, wwwIonicCssPath); + + var libJsSymlinkPath = path.join(libSymlinkPath, 'js'); + console.log('Create www/lib/ionic/js symlink to ' + libJsSymlinkPath); + fs.symlinkSync(libJsSymlinkPath, wwwIonicJsPath); + + var libFontsSymlinkPath = path.join(libSymlinkPath, 'fonts'); + console.log('Create www/lib/ionic/fonts symlink to ' + libFontsSymlinkPath); + fs.symlinkSync(libFontsSymlinkPath, wwwIonicFontsPath); + + libPath = 'lib/ionic'; + } + + if(libPath == 'lib/ionic' && this.seedType == 'ionic-starter') { + // don't bother if its still is the default which comes with the starters return; } + // path did not exist locally, so manually switch out the path in the html var libFiles = 'ionic.css ionic.min.css ionic.js ionic.min.js ionic.bundle.js ionic.bundle.min.js ionic-angular.js ionic-angular.min.js'.split(' '); function isLibFile(tag) { @@ -478,6 +513,7 @@ IonicStartTask.prototype.updateLibFiles = function() { } try { + console.log('Replacing Ionic lib references with ' + libPath); var indexPath = path.join(this.targetPath, 'www', 'index.html'); var html = fs.readFileSync(indexPath, 'utf8'); diff --git a/package.json b/package.json index 8ca102be71..dcb87ef311 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.1.13", + "version": "1.2.0", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -42,6 +42,8 @@ "archiver": "0.5.1", "colors": "^0.6.2", "connect": "^2.14.5", + "serve-static": "^1.5.3", + "finalhandler": "^0.1.0", "connect-livereload": "^0.4.0", "event-stream": "3.0.x", "form-data": "~0.1.0", From bbfb344b305dc336595eea70e809685e2b7ee99d Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Tue, 2 Sep 2014 15:27:06 -0500 Subject: [PATCH 0206/1100] update --- README.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 2f2157598d..4b74fba3fe 100644 --- a/README.md +++ b/README.md @@ -72,15 +72,13 @@ $ ionic platform ios android $ ionic build ios ``` -## Running your app +## Live Reload App During Development (beta) -Deploys the Ionic app on specified platform devices. +The `run` or `emulate` command will deploy the app to the specified platform devices/emulators. You can also run __live reload__ on the specified platform device by adding the `--livereload` option. The live reload functionality is similar to `ionic serve`, but instead of developing and debugging an app using a standard browser, the compiled hybrid app itself is watching for any changes to its files and reloading the app when needed. This reduces the requirement to constantly rebuild the app for small changes. However, any changes to plugins will still require a full rebuild. For live reload to work, the dev machine and device must be on the same local network, and the device must support [web sockets](http://caniuse.com/websockets). -```bash -$ ionic run ios [options] -``` +With live reload enabled, an app's console logs can also be printed to the terminal/command prompt by including the `--consolelogs` or `-c` option. Additionally, the development server's request logs can be printed out using `--serverlogs` or `-s` options. -Command-line flags/options: +Command-line flags/options for `run` and `emulate`: [--livereload|-l] ....... Live Reload app dev files from the device (beta) [--consolelogs|-c] ...... Print app console logs to Ionic CLI (live reload req.) @@ -89,19 +87,22 @@ Command-line flags/options: [--livereload-port|-i] .. Live Reload port (35729 default, live reload req.) [--debug|--release] -### Live Reload App During Development (beta) -The `run` or `emulate` command will deploy the app to the specified platform devices/emulators. You can also run __live reload__ on the specified platform device by adding the `--livereload` option. The live reload functionality is similar to `ionic serve`, but instead of developing and debugging an app using a standard browser, the compiled hybrid app itself is watching for any changes to its files and reloading the app when needed. This reduces the requirement to constantly rebuild the app for small changes. However, any changes to plugins will still require a full rebuild. For live reload to work, the dev machine and device must be on the same local network. +## Emulating your app -With live reload enabled, an app's console logs can also be printed to the terminal/command prompt by including the `--consolelogs` or `-c` option. Additionally, the development server's request logs can be printed out using `--serverlogs` or `-s` options. +Deploys the Ionic app on specified platform emulator. This is simply an alias for `run --emulator`. +```bash +$ ionic emulate ios [options] +``` -## Emulating your app -Deploys the Ionic app on specified platform emulator. This is simply an alias for `run --emulator`. Live reload options are the same as the `run` options listed above. +## Running your app + +Deploys the Ionic app on specified platform devices. If a device is not found it'll then deploy to an emulator/simulator. ```bash -$ ionic emulate ios [options] +$ ionic run ios [options] ``` From 5d023f21884e27312b30ce6778d63192d80166b8 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 3 Sep 2014 02:35:31 -0500 Subject: [PATCH 0207/1100] auto remove read me from root --- lib/ionic/start.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 33d5dfc7f5..a433d3dbbd 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -559,6 +559,13 @@ IonicStartTask.prototype.finalize = function() { ionicBower.setAppName(this.appName); } catch(e) {} + try { + // remove the README file in the root because it + // doesn't make sense because its the README for the repo + // and not helper text while developing an app + fs.unlinkSync(this.targetPath + '/README.md'); + } catch(e) {} + try { // remove the README file in the www root because it // doesn't make sense because its the README for the repo From 9f77b5ead5836c1d92af33fb1f34280c3e320cd4 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 3 Sep 2014 02:38:30 -0500 Subject: [PATCH 0208/1100] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4b74fba3fe..44065c5549 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Use the `ionic --help` command for more detailed task information. $ npm install -g ionic ``` -Note: For a global install of `-g ionic`, OSX/Linux users may need to prefix the command with `sudo`. +*Note: For a global install of `-g ionic`, OSX/Linux users may need to prefix the command with `sudo`.* ## Starting an Ionic App @@ -133,7 +133,7 @@ The third argument can be either `debug` or `release`, and the last argument can Ionic uses Cordova underneath, so you can also substitute Cordova commands to prepare/build/emulate/run, or to add additional plugins. -Note: we occasionally send anonymous usage statistics to the Ionic team to make the tool better. +*Note: we occasionally send anonymous usage statistics to the Ionic team to make the tool better.* ## Using Sass From 3b713d3caa5a284abc3fc5059cf4c4c49566a51d Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 3 Sep 2014 02:52:56 -0500 Subject: [PATCH 0209/1100] add platform class hook --- lib/hooks/after_prepare/010_add_platform_class.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/hooks/after_prepare/010_add_platform_class.js b/lib/hooks/after_prepare/010_add_platform_class.js index edd86c9065..ed0135f834 100755 --- a/lib/hooks/after_prepare/010_add_platform_class.js +++ b/lib/hooks/after_prepare/010_add_platform_class.js @@ -3,8 +3,9 @@ // Add Platform Class // v1.0 // Automatically adds the platform class to the body tag -// after the `prepare` command. This speeds up device rendering -// the correct layout/style for the specific platform. +// after the `prepare` command. By placing the platform CSS classes +// directly in the HTML built for the platform, it speeds up +// rendering the correct layout/style for the specific platform. var fs = require('fs'); var path = require('path'); From 6b75159a851ad0da58237cbafaed4a51e855a38a Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 3 Sep 2014 03:06:15 -0500 Subject: [PATCH 0210/1100] update --- lib/hooks/after_prepare/010_add_platform_class.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/hooks/after_prepare/010_add_platform_class.js b/lib/hooks/after_prepare/010_add_platform_class.js index ed0135f834..bda3e41585 100755 --- a/lib/hooks/after_prepare/010_add_platform_class.js +++ b/lib/hooks/after_prepare/010_add_platform_class.js @@ -5,7 +5,8 @@ // Automatically adds the platform class to the body tag // after the `prepare` command. By placing the platform CSS classes // directly in the HTML built for the platform, it speeds up -// rendering the correct layout/style for the specific platform. +// rendering the correct layout/style for the specific platform +// instead of waiting for the JS to figure out the correct classes. var fs = require('fs'); var path = require('path'); @@ -37,7 +38,7 @@ function addPlatformBodyTag(indexPath, platform) { } else { // add class attribute to the body tag - newBodyTag = bodyTag.replace('>', ' class="' + platformClass + ' ' + cordovaClass + '">') + newBodyTag = bodyTag.replace('>', ' class="' + platformClass + ' ' + cordovaClass + '">'); } html = html.replace(bodyTag, newBodyTag); From 9d01ff2b3d5e8047f98cc4a464a9f9c7da166651 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 3 Sep 2014 03:21:53 -0500 Subject: [PATCH 0211/1100] both app-name and appname work --- lib/ionic.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic.js b/lib/ionic.js index d38f10792c..2d892f38b2 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -65,7 +65,7 @@ var TASKS = [ 'Defaults to Ionic "tabs" starter template' }, options: { - '--app-name|-a': 'Human readable name for the app (Use quotes around the name)', + '--appname|-a': 'Human readable name for the app (Use quotes around the name)', '--id|-i': 'Package name for config, ex: com.mycompany.myapp', '--no-cordova|-w': 'Create a basic structure without Cordova requirements', '--sass|-s': 'Setup the project to use Sass CSS precompiling' From 76915ae1037535d02c9583faa3db5ae5b63891e2 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 3 Sep 2014 03:47:07 -0500 Subject: [PATCH 0212/1100] add platforms from start cmd --- lib/ionic/start.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index a433d3dbbd..09900ad962 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -326,10 +326,24 @@ IonicStartTask.prototype.initCordova = function() { if(this.ionic.hasFailed) return; + var cmds = [ + 'cordova plugin add org.apache.cordova.device', + 'cordova plugin add org.apache.cordova.console', + 'cordova plugin add com.ionic.keyboard' + ]; + + // platform add android with --android flag + if(argv.android) { + cmds.push('cordova platform add android'); + } + + // platform add ios with --android flag + if(argv.ios) { + cmds.push('cordova platform add ios'); + } + console.log('Initializing cordova project.'.info.bold); - exec('cordova plugin add org.apache.cordova.device && ' + - 'cordova plugin add org.apache.cordova.console && ' + - 'cordova plugin add com.ionic.keyboard', + exec(cmds.join(' && '), function(err, stdout, stderr) { if(err) { self.ionic.fail('Unable to add plugins. Perhaps your version of Cordova is too old. ' + From eecf1ce49bea2e29f951e97937fed32b76a2b1e9 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 3 Sep 2014 21:31:02 -0500 Subject: [PATCH 0213/1100] do not require all task modules on each run --- lib/ionic.js | 97 ++++++++++++++------------------------------ lib/ionic/cordova.js | 16 ++++---- lib/ionic/lib.js | 26 ++++++------ lib/ionic/login.js | 14 +++---- lib/ionic/package.js | 30 +++++++------- lib/ionic/project.js | 3 +- lib/ionic/serve.js | 33 +++++++++------ lib/ionic/setup.js | 16 ++++---- lib/ionic/start.js | 40 +++++++++--------- lib/ionic/task.js | 6 +-- lib/ionic/upload.js | 10 ++--- 11 files changed, 131 insertions(+), 160 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 2d892f38b2..2dbf7e5530 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -14,25 +14,11 @@ Licensed under the MIT license. See LICENSE For more. Copyright 2014 Drifty (http://drifty.com/) */ -var IonicStartTask = require('./ionic/start').IonicStartTask, - IonicCordovaTask = require('./ionic/cordova').IonicCordovaTask, - IonicLoginTask = require('./ionic/login').IonicLoginTask, - IonicUploadTask = require('./ionic/upload').IonicUploadTask, - IonicPackageTask = require('./ionic/package').IonicPackageTask, - IonicLibTask = require('./ionic/lib').IonicLibTask, - IonicSetupTask = require('./ionic/setup').IonicSetupTask, - IonicServeTask = require('./ionic/serve').IonicServeTask, - IonicProject = require('./ionic/project'), - IonicStore = require('./ionic/store').IonicStore, - path = require('path'), - request = require('request'), - os = require('os'), - unzip = require('unzip'), +var argv = require('optimist').argv, colors = require('colors'), - spawn = require('child_process').spawn, Q = require('q'), - settings = require('../package.json'); - + settings = require('../package.json'), + shelljs = require('shelljs/global'),; colors.setTheme({ silly: 'rainbow', @@ -49,9 +35,6 @@ colors.setTheme({ }); -var fs = require('fs'), - argv = require('optimist').argv; - var TASKS = [ { title: 'start', @@ -70,7 +53,7 @@ var TASKS = [ '--no-cordova|-w': 'Create a basic structure without Cordova requirements', '--sass|-s': 'Setup the project to use Sass CSS precompiling' }, - task: IonicStartTask + module: './ionic/start' }, { title: 'serve', @@ -87,7 +70,7 @@ var TASKS = [ '--nobrowser|-b': 'Disable launching a browser', '--nolivereload': 'Do not start live reload' }, - task: IonicServeTask + module: './ionic/serve' }, { title: 'platform', @@ -97,7 +80,7 @@ var TASKS = [ '[options]': '', '': '' }, - task: IonicCordovaTask + module: './ionic/cordova' }, { title: 'run', @@ -116,7 +99,7 @@ var TASKS = [ '--debug|--release': '', '--device|--emulator|--target=FOO': '' }, - task: IonicCordovaTask + module: './ionic/cordova' }, { title: 'emulate', @@ -135,7 +118,7 @@ var TASKS = [ '--debug|--release': '', '--device|--emulator|--target=FOO': '' }, - task: IonicCordovaTask + module: './ionic/cordova' }, { title: 'build', @@ -145,7 +128,7 @@ var TASKS = [ '[options]': '', '': '' }, - task: IonicCordovaTask + module: './ionic/cordova' }, { title: 'plugin add', @@ -159,17 +142,17 @@ var TASKS = [ '--searchpath ': 'When looking up plugins by ID, look in this directory and\n' + 'subdirectories for the plugin before using the registry.' }, - task: IonicCordovaTask + module: './ionic/cordova' }, { title: 'prepare', name: 'prepare', - task: IonicCordovaTask + module: './ionic/cordova' }, { title: 'compile', name: 'compile', - task: IonicCordovaTask + module: './ionic/cordova' }, { title: 'package', @@ -195,7 +178,7 @@ var TASKS = [ '--email|-e': 'Ionic account email', '--password|-p': 'Ionic account password' }, - task: IonicPackageTask + module: './ionic/package' }, { title: 'upload', @@ -206,7 +189,7 @@ var TASKS = [ '--password|-p': 'Ionic account password' }, alt: ['up'], - task: IonicUploadTask + module: './ionic/upload' }, { title: 'lib', @@ -219,7 +202,7 @@ var TASKS = [ options: { '--version|-v': 'Spcific Ionic version, otherwise it defaults to the latest' }, - task: IonicLibTask + module: './ionic/lib' }, { title: 'setup', @@ -228,12 +211,12 @@ var TASKS = [ args: { '[sass]': 'Setup the project to use Sass CSS precompiling' }, - task: IonicSetupTask + module: './ionic/setup' }, { title: 'login', name: 'login', - task: IonicLoginTask + module: './ionic/login' } ]; @@ -436,6 +419,7 @@ Ionic = { } if(argv['stats-opt-out']) { + var IonicStore = require('./ionic/store').IonicStore; var ionicConfig = new IonicStore('ionic.config'); ionicConfig.set('statsOptOut', true); ionicConfig.save(); @@ -443,13 +427,14 @@ Ionic = { return; } - var task = this._tryBuildingTask(); - if(!task) { + var taskSetting = this._tryBuildingTask(); + if(!taskSetting) { return this._printAvailableTasks(); } + var taskModule = require(taskSetting.module).IonicTask; - var taskObj = new task.task(); - taskObj.run(this); + var taskInstance = new taskModule(); + taskInstance.run(this); }, fail: function(msg, taskHelp) { @@ -488,6 +473,7 @@ Ionic = { } // stay silent if it errors + var IonicStore = require('./ionic/store').IonicStore; var ionicConfig = new IonicStore('ionic.config'); var versionCheck = ionicConfig.get('versionCheck'); if(versionCheck && ((versionCheck + 86400000) > Date.now() )) { @@ -497,6 +483,7 @@ Ionic = { } var proxy = process.env.PROXY || null; + var request = require('request'); request({ url: 'http://registry.npmjs.org/ionic/latest', proxy: proxy }, function(err, res, body) { try { self.npmVersion = JSON.parse(body).version; @@ -529,39 +516,14 @@ Ionic = { }); }, - spawnPromise: function(cmd, args, onStdOut, onStdErr) { - var q = Q.defer(); - var child; - console.log('\nRUNNING:'.info.bold, cmd, args.join(' ')); - try { - child = spawn(cmd, args); - } catch(e) { - } - child.stdout.setEncoding('utf8'); - child.stdout.on('data', function(data) { - process.stdout.write(data); - onStdOut && onStdOut(data); - }); - child.stderr.on('data', function(data) { - process.stderr.write(data); - onStdErr && onStdErr(data); - }); - child.on('error', function(err) { - q.reject(err); - }); - child.on('exit', function(code) { - if(code === 0) { - q.resolve(); - } else { - q.reject(code); - } - }); - return q.promise; - }, /** * Download a zip file, unzip it to a specific folder. */ fetchArchive: function(targetPath, archiveUrl) { + var os = require('os'); + var fs = require('fs'); + var path = require('path'); + var unzip = require('unzip'); var q = Q.defer(); // The folder name the project will be downloaded and extracted to @@ -585,6 +547,7 @@ Ionic = { }; var proxy = process.env.PROXY || null; + var request = require('request'); request({ url: archiveUrl, encoding: null, proxy: proxy }, function(err, res, body) { if(!res || res.statusCode !== 200) { q.reject(res); diff --git a/lib/ionic/cordova.js b/lib/ionic/cordova.js index 5db1181e6f..40f898c118 100644 --- a/lib/ionic/cordova.js +++ b/lib/ionic/cordova.js @@ -1,4 +1,4 @@ -var IonicTask = require('./task').IonicTask, +var Task = require('./task').Task, IonicStats = require('./stats').IonicStats, fs = require('fs'), argv = require('optimist').argv, @@ -7,11 +7,11 @@ var IonicTask = require('./task').IonicTask, exec = require('child_process').exec, colors = require('colors'); -var IonicCordovaTask = function() {}; +var IonicTask = function() {}; -IonicCordovaTask.prototype = new IonicTask(); +IonicTask.prototype = new Task(); -IonicCordovaTask.prototype.run = function(ionic) { +IonicTask.prototype.run = function(ionic) { var self = this; self.ionic = ionic; var cmdName = process.argv[2].toLowerCase(); @@ -82,7 +82,7 @@ IonicCordovaTask.prototype.run = function(ionic) { }; -IonicCordovaTask.prototype.setupLiveReload = function() { +IonicTask.prototype.setupLiveReload = function() { console.log('Setup Live Reload'.green.bold); var serve = new require('./serve'); @@ -99,7 +99,7 @@ IonicCordovaTask.prototype.setupLiveReload = function() { }; -IonicCordovaTask.prototype.setConfigXml = function(options) { +IonicTask.prototype.setConfigXml = function(options) { var self = this; var madeChange = false; @@ -146,7 +146,7 @@ IonicCordovaTask.prototype.setConfigXml = function(options) { }; -IonicCordovaTask.prototype.addHooks = function() { +IonicTask.prototype.addHooks = function() { // Add hooks which this Ionic project doesn't already have // note: hook scripts must be executable! @@ -212,4 +212,4 @@ IonicCordovaTask.prototype.addHooks = function() { }; -exports.IonicCordovaTask = IonicCordovaTask; +exports.IonicTask = IonicTask; diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index 57372c0212..eefc6e6872 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -8,14 +8,14 @@ var fs = require('fs'), colors = require('colors'), exec = require('child_process').exec, Q = require('q'), - IonicTask = require('./task').IonicTask, + Task = require('./task').Task, IonicStats = require('./stats').IonicStats; -var IonicLibTask = function() {}; +var IonicTask = function() {}; -IonicLibTask.prototype = new IonicTask(); +IonicTask.prototype = new Task(); -IonicLibTask.prototype.run = function(ionic) { +IonicTask.prototype.run = function(ionic) { var self = this; self.local = {}; self.versionData = {}; @@ -37,7 +37,7 @@ IonicLibTask.prototype.run = function(ionic) { }; -IonicLibTask.prototype.loadVersionData = function() { +IonicTask.prototype.loadVersionData = function() { this.versionFilePath = path.resolve('www/lib/ionic/version.json'); if( !fs.existsSync(this.versionFilePath) ) { this.versionFilePath = path.resolve('www/lib/ionic/bower.json'); @@ -52,7 +52,7 @@ IonicLibTask.prototype.loadVersionData = function() { }; -IonicLibTask.prototype.printLibVersions = function() { +IonicTask.prototype.printLibVersions = function() { var self = this; console.log('Local Ionic version: '.bold.green + this.local.version + ' (' + this.versionFilePath + ')'); @@ -69,7 +69,7 @@ IonicLibTask.prototype.printLibVersions = function() { }; -IonicLibTask.prototype.getVersionData = function(version) { +IonicTask.prototype.getVersionData = function(version) { var q = Q.defer(); var self = this; @@ -110,7 +110,7 @@ IonicLibTask.prototype.getVersionData = function(version) { }; -IonicLibTask.prototype.updateLibVersion = function() { +IonicTask.prototype.updateLibVersion = function() { var self = this; if(self.usesBower) { @@ -158,7 +158,7 @@ IonicLibTask.prototype.updateLibVersion = function() { }; -IonicLibTask.prototype.getLatest = function() { +IonicTask.prototype.getLatest = function() { var self = this; var dirPath = path.resolve('www/lib/'); @@ -188,7 +188,7 @@ IonicLibTask.prototype.getLatest = function() { }; -IonicLibTask.prototype.downloadZip = function(version) { +IonicTask.prototype.downloadZip = function(version) { var self = this; var archivePath = 'https://github.com/driftyco/ionic-bower/archive/v' + version + '.zip'; @@ -253,7 +253,7 @@ IonicLibTask.prototype.downloadZip = function(version) { }; -IonicLibTask.prototype.updateFiles = function(version) { +IonicTask.prototype.updateFiles = function(version) { var self = this; var ionicLibDir = path.resolve('www/lib/ionic'); var readStream = fs.createReadStream(self.tmpZipPath); @@ -315,7 +315,7 @@ IonicLibTask.prototype.updateFiles = function(version) { } }; -IonicLibTask.prototype.writeVersionData = function() { +IonicTask.prototype.writeVersionData = function() { try { var versionData = { "version": this.versionData.version_number || this.versionData.version, @@ -331,4 +331,4 @@ IonicLibTask.prototype.writeVersionData = function() { } }; -exports.IonicLibTask = IonicLibTask; +exports.IonicTask = IonicTask; diff --git a/lib/ionic/login.js b/lib/ionic/login.js index ce1109faae..e94a7edff8 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -4,13 +4,13 @@ var fs = require('fs'), prompt = require('prompt'), IonicProject = require('./project'), IonicStore = require('./store').IonicStore, - IonicTask = require('./task').IonicTask; + Task = require('./task').Task; -var IonicLoginTask = function() {}; +var IonicTask = function() {}; -IonicLoginTask.prototype = new IonicTask(); +IonicTask.prototype = new Task(); -IonicLoginTask.prototype.get = function(ionic, callback) { +IonicTask.prototype.get = function(ionic, callback) { this.cookieData = new IonicStore('cookies'); if(ionic.jar) { @@ -48,7 +48,7 @@ IonicLoginTask.prototype.get = function(ionic, callback) { this.run(ionic, callback); }; -IonicLoginTask.prototype.run = function(ionic, callback) { +IonicTask.prototype.run = function(ionic, callback) { var self = this; if(!this.email && !this.password) { @@ -92,7 +92,7 @@ IonicLoginTask.prototype.run = function(ionic, callback) { }; -IonicLoginTask.prototype.requestLogIn = function(ionic, callback, saveCookies) { +IonicTask.prototype.requestLogIn = function(ionic, callback, saveCookies) { var self = this; var jar = request.jar(); @@ -136,4 +136,4 @@ IonicLoginTask.prototype.requestLogIn = function(ionic, callback, saveCookies) { }); }; -exports.IonicLoginTask = IonicLoginTask; +exports.IonicTask = IonicTask; diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 32a3171f2d..e296b0d1dc 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -8,16 +8,16 @@ var fs = require('fs'), FormData = require('form-data'), IonicProject = require('./project'), IonicStore = require('./store').IonicStore, - IonicTask = require('./task').IonicTask, + Task = require('./task').Task, IonicUploadTask = require('./upload').IonicUploadTask, IonicStats = require('./stats').IonicStats, IonicLoginTask = require('./login').IonicLoginTask; -var IonicPackageTask = function() {}; +var IonicTask = function() {}; -IonicPackageTask.prototype = new IonicTask(); +IonicTask.prototype = new Task(); -IonicPackageTask.prototype.run = function(ionic) { +IonicTask.prototype.run = function(ionic) { var self = this; self.ionic = ionic; @@ -51,7 +51,7 @@ IonicPackageTask.prototype.run = function(ionic) { }; -IonicPackageTask.prototype.loadProject = function() { +IonicTask.prototype.loadProject = function() { var appName = this.project.get('name') || "app"; console.log( ('Loading ' + appName + '...').bold.green ); @@ -83,7 +83,7 @@ IonicPackageTask.prototype.loadProject = function() { }; -IonicPackageTask.prototype.loadPlugins = function() { +IonicTask.prototype.loadPlugins = function() { this.plugins = []; try { @@ -105,7 +105,7 @@ IonicPackageTask.prototype.loadPlugins = function() { }; -IonicPackageTask.prototype.getCmdLineOptions = function() { +IonicTask.prototype.getCmdLineOptions = function() { var self = this; function getCmdArgValue(propertyName, shortName) { @@ -138,7 +138,7 @@ IonicPackageTask.prototype.getCmdLineOptions = function() { }; -IonicPackageTask.prototype.loadAppSigning = function(callback) { +IonicTask.prototype.loadAppSigning = function(callback) { var self = this; if(self.useCmdArgs) { @@ -195,7 +195,7 @@ IonicPackageTask.prototype.loadAppSigning = function(callback) { }; -IonicPackageTask.prototype.initPlatforms = function() { +IonicTask.prototype.initPlatforms = function() { var self = this; if(self.useCmdArgs) { @@ -231,7 +231,7 @@ IonicPackageTask.prototype.initPlatforms = function() { }; -IonicPackageTask.prototype.packagePlatforms = function() { +IonicTask.prototype.packagePlatforms = function() { var self = this; var upload = new IonicUploadTask(); @@ -244,7 +244,7 @@ IonicPackageTask.prototype.packagePlatforms = function() { }; -IonicPackageTask.prototype.buildPromptProperties = function() { +IonicTask.prototype.buildPromptProperties = function() { // Just prompt for some build properties var promptProperties = {}; @@ -325,7 +325,7 @@ IonicPackageTask.prototype.buildPromptProperties = function() { }; -IonicPackageTask.prototype.buildPostRequest = function(platform) { +IonicTask.prototype.buildPostRequest = function(platform) { var form = new FormData(); form.append('build_status_email', this.buildStatusEmail.toString()); @@ -370,7 +370,7 @@ IonicPackageTask.prototype.buildPostRequest = function(platform) { }; -IonicPackageTask.prototype.submitPostRequest = function(form, platform) { +IonicTask.prototype.submitPostRequest = function(form, platform) { var self = this; var params = parseUrl(self.ionic.IONIC_DASH + self.ionic.IONIC_API + 'app/' + self.project.get('app_id') + '/package'); @@ -444,7 +444,7 @@ IonicPackageTask.prototype.submitPostRequest = function(form, platform) { }; -IonicPackageTask.prototype.clearSigning = function() { +IonicTask.prototype.clearSigning = function() { var self = this; console.log('Clearing app signing and credential information...'.yellow.bold); @@ -612,4 +612,4 @@ function resolvePath (p) { return path.resolve(p); } -exports.IonicPackageTask = IonicPackageTask; +exports.IonicTask = IonicTask; diff --git a/lib/ionic/project.js b/lib/ionic/project.js index 366a2c4e89..d5c294fa01 100644 --- a/lib/ionic/project.js +++ b/lib/ionic/project.js @@ -1,6 +1,5 @@ var fs = require('fs'), - path = require('path'), - ionic = require('../ionic'); + path = require('path'); module.exports = { PROJECT_FILE: 'ionic.project', diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 1194024d23..bd5bdee492 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -13,17 +13,17 @@ var fs = require('fs'), Q = require('q'), spawn = require('child_process').spawn, IonicProject = require('./project'), - IonicTask = require('./task').IonicTask; + Task = require('./task').Task; IonicStats = require('./stats').IonicStats; var DEFAULT_HTTP_PORT = 8100; var DEFAULT_LIVE_RELOAD_PORT = 35729; -var IonicServeTask = function() {}; +var IonicTask = function() {}; -IonicServeTask.prototype = new IonicTask(); +IonicTask.prototype = new Task(); -IonicServeTask.prototype.run = function(ionic) { +IonicTask.prototype.run = function(ionic) { this.port = argv._[1]; this.liveReloadPort = argv._[2]; @@ -38,7 +38,7 @@ IonicServeTask.prototype.run = function(ionic) { }; -IonicServeTask.prototype.loadSettings = function() { +IonicTask.prototype.loadSettings = function() { var project = IonicProject.load(); this.port = this.port || argv.port || argv.p || DEFAULT_HTTP_PORT; @@ -51,7 +51,7 @@ IonicServeTask.prototype.loadSettings = function() { this.printConsoleLogs = argv.consolelogs || argv['console-logs'] || argv.c; }; -IonicServeTask.prototype.start = function(ionic) { +IonicTask.prototype.start = function(ionic) { var self = this; var app = connect(); @@ -180,14 +180,14 @@ IonicServeTask.prototype.start = function(ionic) { }; -IonicServeTask.prototype.serverLog = function(msg) { +IonicTask.prototype.serverLog = function(msg) { if(this.printServerLogs) { console.log( ('serve: ' + msg).yellow ); } }; -IonicServeTask.prototype.consoleLog = function(req) { +IonicTask.prototype.consoleLog = function(req) { var body = ''; req.on('data', function (data) { @@ -209,7 +209,16 @@ IonicServeTask.prototype.consoleLog = function(req) { while(msg.length < 14) { msg += ' '; } - msg += log.args.join(', '); + + if(log.method == 'dir') { + var dirObjs = []; + log.args.forEach(function(objStr){ + dirObjs.push( JSON.stringify( JSON.parse(objStr) ) ); + }); + msg += dirObjs.join('\n'); + } else { + msg += log.args.join(', '); + } if(log.method == 'error' || log.method == 'exception') msg = msg.red; else if(log.method == 'warn') msg = msg.yellow; @@ -286,7 +295,7 @@ function insertConsoleLogScript(html) { return html; } -IonicServeTask.prototype._changed = function(filePath) { +IonicTask.prototype._changed = function(filePath) { // Cleanup the path a bit var pwd = process.cwd(); filePath = filePath.replace(pwd + '/', ''); @@ -315,7 +324,7 @@ IonicServeTask.prototype._changed = function(filePath) { }; -IonicServeTask.prototype.host = function(port) { +IonicTask.prototype.host = function(port) { var addresses = []; try { @@ -335,4 +344,4 @@ IonicServeTask.prototype.host = function(port) { return 'http://' + (addresses.length === 1 ? addresses[0] : 'localhost') + ':' + port; }; -exports.IonicServeTask = IonicServeTask; +exports.IonicTask = IonicTask; diff --git a/lib/ionic/setup.js b/lib/ionic/setup.js index 68263d22ec..60f6345b82 100644 --- a/lib/ionic/setup.js +++ b/lib/ionic/setup.js @@ -1,4 +1,4 @@ -var IonicTask = require('./task').IonicTask, +var Task = require('./task').Task, IonicStats = require('./stats').IonicStats, fs = require('fs'), path = require('path'), @@ -8,11 +8,11 @@ var IonicTask = require('./task').IonicTask, IonicProject = require('./project'), colors = require('colors'); -var IonicSetupTask = function() {}; +var IonicTask = function() {}; -IonicSetupTask.prototype = new IonicTask(); +IonicTask.prototype = new Task(); -IonicSetupTask.prototype.run = function(ionic) { +IonicTask.prototype.run = function(ionic) { if( argv._.length < 2 ) { return ionic.fail('Missing setup task command.', 'setup'); } @@ -31,7 +31,7 @@ IonicSetupTask.prototype.run = function(ionic) { }; -IonicSetupTask.prototype.sassSetup = function(ionic) { +IonicTask.prototype.sassSetup = function(ionic) { var q = Q.defer(); var self = this; @@ -100,7 +100,7 @@ IonicSetupTask.prototype.sassSetup = function(ionic) { }; -IonicSetupTask.prototype.buildSass = function() { +IonicTask.prototype.buildSass = function() { var q = Q.defer(); var childProcess = exec('gulp sass'); @@ -130,7 +130,7 @@ IonicSetupTask.prototype.buildSass = function() { }; -IonicSetupTask.prototype.npmInstall = function() { +IonicTask.prototype.npmInstall = function() { var q = Q.defer(); var childProcess = exec('npm install'); @@ -163,4 +163,4 @@ IonicSetupTask.prototype.npmInstall = function() { }; -exports.IonicSetupTask = IonicSetupTask; +exports.IonicTask = IonicTask; diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 09900ad962..4bfffa9e87 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -12,18 +12,18 @@ var fs = require('fs'), xml2js = require('xml2js'), setup = require('./setup'), IonicProject = require('./project'), - IonicTask = require('./task').IonicTask; + Task = require('./task').Task; IonicStats = require('./stats').IonicStats; -var IonicStartTask = function() {}; +var IonicTask = function() {}; // The URL for the cordova wrapper project -IonicStartTask.WRAPPER_REPO_NAME = 'ionic-app-base'; +IonicTask.WRAPPER_REPO_NAME = 'ionic-app-base'; -IonicStartTask.prototype = new IonicTask(); +IonicTask.prototype = new Task(); -IonicStartTask.prototype.run = function(ionic) { +IonicTask.prototype.run = function(ionic) { this.ionic = ionic; var self = this; @@ -94,7 +94,7 @@ IonicStartTask.prototype.run = function(ionic) { }; -IonicStartTask.prototype.startApp = function() { +IonicTask.prototype.startApp = function() { var self = this; self.fetchWrapper() @@ -117,14 +117,14 @@ IonicStartTask.prototype.startApp = function() { }; -IonicStartTask.prototype.fetchWrapper = function() { +IonicTask.prototype.fetchWrapper = function() { var q = Q.defer(); var self = this; - var repoUrl = 'https://github.com/driftyco/' + IonicStartTask.WRAPPER_REPO_NAME + '/archive/master.zip'; + var repoUrl = 'https://github.com/driftyco/' + IonicTask.WRAPPER_REPO_NAME + '/archive/master.zip'; Ionic.fetchArchive(self.targetPath, repoUrl).then(function() { - var repoFolderName = IonicStartTask.WRAPPER_REPO_NAME + '-master'; + var repoFolderName = IonicTask.WRAPPER_REPO_NAME + '-master'; cp('-R', self.targetPath + '/' + repoFolderName + '/.', self.targetPath); rm('-rf', self.targetPath + '/' + repoFolderName + '/'); cd(self.targetPath); @@ -148,7 +148,7 @@ IonicStartTask.prototype.fetchWrapper = function() { }; -IonicStartTask.prototype.fetchSeed = function() { +IonicTask.prototype.fetchSeed = function() { if(this.template.toLowerCase().indexOf('codepen') > -1) { this.seedType = 'codepen'; @@ -160,7 +160,7 @@ IonicStartTask.prototype.fetchSeed = function() { }; -IonicStartTask.prototype.fetchCodepen = function() { +IonicTask.prototype.fetchCodepen = function() { var self = this; var codepenUrl = this.template.split('?')[0].split('#')[0]; var wwwPath = path.join(this.targetPath, 'www'); @@ -225,7 +225,7 @@ IonicStartTask.prototype.fetchCodepen = function() { }; -IonicStartTask.prototype.convertTemplates = function(html) { +IonicTask.prototype.convertTemplates = function(html) { var templates = []; var self = this; @@ -283,7 +283,7 @@ IonicStartTask.prototype.convertTemplates = function(html) { }; -IonicStartTask.prototype.fetchIonicStarter = function() { +IonicTask.prototype.fetchIonicStarter = function() { var self = this; var q = Q.defer(); @@ -316,7 +316,7 @@ IonicStartTask.prototype.fetchIonicStarter = function() { }; -IonicStartTask.prototype.initCordova = function() { +IonicTask.prototype.initCordova = function() { var self = this; var q = Q.defer(); @@ -362,7 +362,7 @@ IonicStartTask.prototype.initCordova = function() { }; -IonicStartTask.prototype.updateConfigXml = function() { +IonicTask.prototype.updateConfigXml = function() { var self = this; console.log('\nUpdate config.xml'.info.bold); @@ -400,7 +400,7 @@ IonicStartTask.prototype.updateConfigXml = function() { }; -IonicStartTask.prototype.setupSass = function() { +IonicTask.prototype.setupSass = function() { if(argv.sass) { // auto setup sass if they set the option console.log('setup sass'.green.bold); @@ -415,7 +415,7 @@ IonicStartTask.prototype.setupSass = function() { }; -IonicStartTask.prototype.updateLibFiles = function() { +IonicTask.prototype.updateLibFiles = function() { var libPath = argv.lib || argv.l || 'lib/ionic'; // create a symlink if the path exists locally @@ -547,7 +547,7 @@ IonicStartTask.prototype.updateLibFiles = function() { }; -IonicStartTask.prototype.finalize = function() { +IonicTask.prototype.finalize = function() { this.updateLibFiles(); @@ -593,7 +593,7 @@ IonicStartTask.prototype.finalize = function() { }; -IonicStartTask.prototype.printQuickHelp = function() { +IonicTask.prototype.printQuickHelp = function() { console.log('\nYour Ionic project is ready to go!'.green.bold, 'Some quick tips:'); console.log('\n * cd into your project:', ('$ cd ' + this.appDirectory).info.bold); @@ -618,4 +618,4 @@ IonicStartTask.prototype.printQuickHelp = function() { }; -exports.IonicStartTask = IonicStartTask; +exports.IonicTask = IonicTask; diff --git a/lib/ionic/task.js b/lib/ionic/task.js index 20ffec018f..3385c4af82 100644 --- a/lib/ionic/task.js +++ b/lib/ionic/task.js @@ -1,8 +1,8 @@ -var IonicTask = function() { }; +var Task = function() { }; -IonicTask.prototype = { +Task.prototype = { run: function(ionic) { } }; -exports.IonicTask = IonicTask; +exports.Task = Task; diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index e0400a4f6a..5ba96d3cbd 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -5,16 +5,16 @@ var fs = require('fs'), argv = require('optimist').argv, FormData = require('form-data'), IonicProject = require('./project'), - IonicTask = require('./task').IonicTask, + Task = require('./task').Task, IonicStats = require('./stats').IonicStats, IonicLoginTask = require('./login').IonicLoginTask, Q = require('q'); -var IonicUploadTask = function() {}; +var IonicTask = function() {}; -IonicUploadTask.prototype = new IonicTask(); +IonicTask.prototype = new Task(); -IonicUploadTask.prototype.run = function(ionic, callback) { +IonicTask.prototype.run = function(ionic, callback) { var project = IonicProject.load(); var q = Q.defer(); @@ -129,4 +129,4 @@ IonicUploadTask.prototype.run = function(ionic, callback) { }); }; -exports.IonicUploadTask = IonicUploadTask; +exports.IonicTask = IonicTask; From c732e44e2da3ca3d7fd91e4cf01484356dc7a4f5 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 3 Sep 2014 21:52:12 -0500 Subject: [PATCH 0214/1100] remove white text, closes #70 --- lib/ionic.js | 19 ++++++++++--------- lib/ionic/lib.js | 2 +- lib/ionic/login.js | 2 +- lib/ionic/setup.js | 6 +++--- lib/ionic/start.js | 32 ++++++++++++++++---------------- 5 files changed, 31 insertions(+), 30 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 2dbf7e5530..4c41e65b0a 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -18,7 +18,7 @@ var argv = require('optimist').argv, colors = require('colors'), Q = require('q'), settings = require('../package.json'), - shelljs = require('shelljs/global'),; + shelljs = require('shelljs/global'); colors.setTheme({ silly: 'rainbow', @@ -58,7 +58,7 @@ var TASKS = [ { title: 'serve', name: 'serve', - summary: 'Start a local development server for app dev and testing', + summary: 'Start a local development server for app dev/testing', args: { '[options]': '' }, @@ -92,7 +92,7 @@ var TASKS = [ }, options: { '--livereload|-l': 'Live reload app dev files from the device' + ' (beta)'.yellow, - '--port|-p': 'Dev server HTTP port (8100 default, livereload required)', + '--port|-p': 'Dev server HTTP port (8100 default, livereload req.)', '--livereload-port|-r': 'Live Reload port (35729 default, livereload req.)', '--consolelogs|-c': 'Print app console logs to Ionic CLI (livereload req.)', '--serverlogs|-s': 'Print dev server logs to Ionic CLI (livereload req.)', @@ -111,7 +111,7 @@ var TASKS = [ }, options: { '--livereload|-l': 'Live reload app dev files from the device' + ' (beta)'.yellow, - '--port|-p': 'Dev server HTTP port (8100 default, livereload required)', + '--port|-p': 'Dev server HTTP port (8100 default, livereload req.)', '--livereload-port|-r': 'Live Reload port (35729 default, livereload req.)', '--consolelogs|-c': 'Print app console logs to Ionic CLI (livereload req.)', '--serverlogs|-s': 'Print dev server logs to Ionic CLI (livereload req.)', @@ -139,8 +139,9 @@ var TASKS = [ '': 'Can be a plugin ID, a local path, or a git URL.' }, options: { - '--searchpath ': 'When looking up plugins by ID, look in this directory and\n' + - 'subdirectories for the plugin before using the registry.' + '--searchpath ': 'When looking up plugins by ID, look in this directory\n' + + 'and subdirectories first for the plugin before\n' + + 'looking it up in the registry.' }, module: './ionic/cordova' }, @@ -200,7 +201,7 @@ var TASKS = [ '[update]': 'Updates the Ionic Framework in www/lib/ionic' }, options: { - '--version|-v': 'Spcific Ionic version, otherwise it defaults to the latest' + '--version|-v': 'Spcific Ionic version\nOtherwise it defaults to the latest version' }, module: './ionic/lib' }, @@ -504,7 +505,7 @@ Ionic = { process.stdout.write('Ionic CLI is out of date:\n'.bold.yellow); process.stdout.write( (' * Locally installed version: ' + settings.version + '\n').yellow ); process.stdout.write( (' * Latest version: ' + this.npmVersion + '\n').yellow ); - process.stdout.write( ' * Run '.yellow + 'npm update -g ionic'.info.bold + ' to update\n'.yellow ); + process.stdout.write( ' * Run '.yellow + 'npm update -g ionic'.bold + ' to update\n'.yellow ); process.stdout.write('-------------------------\n\n'.red); this.npmVersion = null; } @@ -527,7 +528,7 @@ Ionic = { var q = Q.defer(); // The folder name the project will be downloaded and extracted to - console.log('\nDownloading:'.info.bold, archiveUrl); + console.log('\nDownloading:'.bold, archiveUrl); var tmpFolder = os.tmpdir(); var tempZipFilePath = path.join(tmpFolder, 'ionic-starter-' + new Date().getTime() + '.zip'); diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index eefc6e6872..bb5bbbf282 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -135,7 +135,7 @@ IonicTask.prototype.updateLibVersion = function() { var libPath = path.resolve('www/lib/ionic/'); - console.log('Are you sure you want to replace '.green.bold + libPath.info.bold + ' with an updated version of Ionic?'.green.bold); + console.log('Are you sure you want to replace '.green.bold + libPath.bold + ' with an updated version of Ionic?'.green.bold); var promptProperties = { areYouSure: { diff --git a/lib/ionic/login.js b/lib/ionic/login.js index e94a7edff8..9b29a04371 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -67,7 +67,7 @@ IonicTask.prototype.run = function(ionic, callback) { // prompt for log console.log('\nTo continue, please login to your Ionic account.'.bold.green); - console.log('Don\'t have one? Create a one at: '.bold + (ionic.IONIC_DASH + '/signup').info.bold + '\n'); + console.log('Don\'t have one? Create a one at: '.bold + (ionic.IONIC_DASH + '/signup').bold + '\n'); prompt.override = argv; prompt.message = ''; diff --git a/lib/ionic/setup.js b/lib/ionic/setup.js index 60f6345b82..0528d4990b 100644 --- a/lib/ionic/setup.js +++ b/lib/ionic/setup.js @@ -49,7 +49,7 @@ IonicTask.prototype.sassSetup = function(ionic) { } try { - var activeRemoving = false; + activeRemoving = false; for(x=0; x'.info.bold); - console.log('\n * Simulate your app:', 'ionic emulate '.info.bold); - console.log('\n * Run your app on a device:', 'ionic run '.info.bold); - console.log('\n * Package an app using Ionic package service:', 'ionic package '.info.bold); + console.log('\n * Add a platform (ios or Android):', 'ionic platform add ios [android]'.bold); + console.log(' Note: iOS development requires OS X currently'.small); + console.log(' See the Android Platform Guide for full Android installation instructions:'.small); + console.log(' https://cordova.apache.org/docs/en/edge/guide_platforms_android_index.md.html'.small); + console.log('\n * Build your app:', 'ionic build '.bold); + console.log('\n * Simulate your app:', 'ionic emulate '.bold); + console.log('\n * Run your app on a device:', 'ionic run '.bold); + console.log('\n * Package an app using Ionic package service:', 'ionic package '.bold); } - console.log('\nFor more help use', 'ionic --help'.info.bold, 'or visit the Ionic docs:', 'http://ionicframework.com/docs\n'.info.bold); + console.log('\nFor more help use', 'ionic --help'.bold, 'or visit the Ionic docs:', 'http://ionicframework.com/docs\n'.bold); }; From ee226be9326c444415bb816623efa5aee8a920c0 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 3 Sep 2014 22:13:34 -0500 Subject: [PATCH 0215/1100] console.dir --- lib/ionic/serve.js | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index bd5bdee492..54a5a63690 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -210,12 +210,32 @@ IonicTask.prototype.consoleLog = function(req) { msg += ' '; } + var msgIndent = ''; + while(msgIndent.length < msg.length) { + msgIndent += ' '; + } + if(log.method == 'dir') { - var dirObjs = []; - log.args.forEach(function(objStr){ - dirObjs.push( JSON.stringify( JSON.parse(objStr) ) ); + var isFirstLine = true; + + log.args.forEach(function(argObj){ + + for(objKey in argObj) { + if(isFirstLine) { + isFirstLine = false; + } else { + msg += '\n' + msgIndent; + } + msg += objKey + ': '; + try { + msg += ( JSON.stringify(argObj[objKey], null, 1) ).replace(/\n/g, ''); + } catch(e) { + msg += argObj[objKey]; + } + } + }); - msg += dirObjs.join('\n'); + } else { msg += log.args.join(', '); } From 11d17a29f4f9b5a79a5b5a520f0984055d06062a Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 3 Sep 2014 22:14:07 -0500 Subject: [PATCH 0216/1100] v update --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dcb87ef311..153ac78d0f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.2.0", + "version": "1.2.1-beta1", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 090fb4cf321c980c2981fedf3c7a3e7d269cec8a Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Thu, 4 Sep 2014 11:19:36 -0500 Subject: [PATCH 0217/1100] fix module references --- lib/ionic/package.js | 4 ++-- lib/ionic/upload.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ionic/package.js b/lib/ionic/package.js index e296b0d1dc..55ae310364 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -9,9 +9,9 @@ var fs = require('fs'), IonicProject = require('./project'), IonicStore = require('./store').IonicStore, Task = require('./task').Task, - IonicUploadTask = require('./upload').IonicUploadTask, + IonicUploadTask = require('./upload').IonicTask, IonicStats = require('./stats').IonicStats, - IonicLoginTask = require('./login').IonicLoginTask; + IonicLoginTask = require('./login').IonicTask; var IonicTask = function() {}; diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index 5ba96d3cbd..53bd0973f9 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -7,7 +7,7 @@ var fs = require('fs'), IonicProject = require('./project'), Task = require('./task').Task, IonicStats = require('./stats').IonicStats, - IonicLoginTask = require('./login').IonicLoginTask, + IonicLoginTask = require('./login').IonicTask, Q = require('q'); var IonicTask = function() {}; From c2048197ba8558af9c230537b1fd1c2d42d0e520 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Thu, 4 Sep 2014 11:20:23 -0500 Subject: [PATCH 0218/1100] clean cordova cmds / ensure config reset --- lib/ionic/cordova.js | 54 ++++++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/lib/ionic/cordova.js b/lib/ionic/cordova.js index 40f898c118..cd54d55766 100644 --- a/lib/ionic/cordova.js +++ b/lib/ionic/cordova.js @@ -16,22 +16,17 @@ IonicTask.prototype.run = function(ionic) { self.ionic = ionic; var cmdName = process.argv[2].toLowerCase(); var cmdArgs = (process.argv.length > 3 ? process.argv.slice(3) : []); - - // clean out any cmds that may confuse cordova - var port = argv.port || argv.p || ''; - var liveReloadPort = argv.livereloadport || argv['livereload-port'] || argv.i || ''; - if(port || liveReloadPort) { - for(var x=0; x Date: Thu, 4 Sep 2014 21:40:52 -0500 Subject: [PATCH 0219/1100] always fs.chmodSync(projectScript, '755'); --- lib/ionic/cordova.js | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/ionic/cordova.js b/lib/ionic/cordova.js index cd54d55766..bb31d764fe 100644 --- a/lib/ionic/cordova.js +++ b/lib/ionic/cordova.js @@ -192,16 +192,7 @@ IonicTask.prototype.addHooks = function() { // check if this hook script has already been added to this ionic project var projectHookPath = path.join('hooks', hookDirectoryName, hookFilename); - if( !fs.existsSync(projectHookPath) ) { - addHookScript(cliHookPath, hookDirectoryName, hookFilename); - } - - // make the script file executable - try { - fs.chmodSync(projectScript, '755'); - } catch(e) { - console.log( ('addcliHookDirectory fs.chmodSync: ' + e).error ); - } + addHookScript(cliHookPath, hookDirectoryName, hookFilename); } }); } @@ -220,10 +211,24 @@ IonicTask.prototype.addHooks = function() { fs.mkdirSync(projectHookPath); } - // copy the hook script to the project - var cliScript = path.join(cliHookPath, hookFilename); var projectScript = path.join(projectHookPath, hookFilename); - fs.createReadStream( cliScript ).pipe(fs.createWriteStream( projectScript )); + if( !fs.existsSync(projectHookPath) ) { + // copy the hook script to the project + try { + var cliScript = path.join(cliHookPath, hookFilename); + fs.createReadStream( cliScript ).pipe(fs.createWriteStream( projectScript )); + } catch(e) { + console.log( ('addcliHookDirectory fs.createReadStream: ' + e).error ); + return; + } + } + + // make the script file executable + try { + fs.chmodSync(projectScript, '755'); + } catch(e) { + console.log( ('addcliHookDirectory fs.chmodSync: ' + e).error ); + } } catch(e) { console.log('Error adding hook script ' + hookDirectoryName + '/' + hookFilename + ', ' + e); From f5342357c97093ec34ee9b94d4fe500a3a6e5027 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Thu, 4 Sep 2014 22:31:13 -0500 Subject: [PATCH 0220/1100] always 200 response --- lib/ionic/serve.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 54a5a63690..a33458cf21 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -120,9 +120,8 @@ IonicTask.prototype.start = function(ionic) { fs.readFile( path.resolve(path.join(platformWWW, platformUrl)), function (err, buf) { res.setHeader('Content-Type', 'application/javascript'); if (err) { - self.serverLog(req.url + ' (Error ' + platformWWW + ')'); - res.statusCode = 404; - res.end('// 404'); + self.serverLog(req.url + ' (' + platformWWW + ')'); + res.end('// mocked cordova.js response to prevent 404 errors during development'); } else { self.serverLog(req.url + ' (' + platformWWW + ')'); res.end(buf); From 151816b8ad947ef6f24ca8f4c0c168ffc7ac62aa Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 5 Sep 2014 10:12:24 -0500 Subject: [PATCH 0221/1100] log read/write errors --- lib/ionic.js | 10 ++++++++++ lib/ionic/lib.js | 17 ++++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 4c41e65b0a..2880ce0034 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -534,14 +534,24 @@ Ionic = { var tempZipFilePath = path.join(tmpFolder, 'ionic-starter-' + new Date().getTime() + '.zip'); var tempZipFileStream = fs.createWriteStream(tempZipFilePath); + tempZipFileStream.on('error', function(err) { + console.log( ('fetchArchive tempZipFileStream: ' + err).error ); + q.reject(err); + }); + var unzipRepo = function(fileName) { var readStream = fs.createReadStream(fileName); + readStream.on('error', function(err) { + console.log( ('unzipRepo readStream: ' + err).error ); + q.reject(err); + }); var writeStream = unzip.Extract({ path: targetPath }); writeStream.on('close', function() { q.resolve(); }); writeStream.on('error', function(err) { + console.log( ('unzipRepo writeStream: ' + err).error ); q.reject(err); }); readStream.pipe(writeStream); diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index bb5bbbf282..6df94a0e00 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -197,6 +197,10 @@ IonicTask.prototype.downloadZip = function(version) { console.log('Downloading: '.green.bold + archivePath); var file = fs.createWriteStream(self.tmpZipPath); + file.on('error', function(err) { + console.log( ('downloadZip createWriteStream: ' + err).error ); + }); + var proxy = process.env.PROXY || null; request({ url: archivePath, encoding: null, proxy: proxy }, function(err, res, body) { if(err) { @@ -275,7 +279,7 @@ IonicTask.prototype.updateFiles = function(version) { } else { var writeStream = fs.createWriteStream(libEntryPath); writeStream.on('error', function(err) { - console.log( ('Error writing, ' + libEntryPath + ': ' + err).bold.red ); + console.log( ('updateFiles error writing, ' + libEntryPath + ': ' + err).error ); }); entry.pipe(writeStream); } @@ -301,7 +305,7 @@ IonicTask.prototype.updateFiles = function(version) { }); readStream.on('error', function(err){ - console.log('Error: ' + err); + console.log( ('updateFiles readStream: ' + err).error ); }); readStream.on('close', function(err){ @@ -323,9 +327,12 @@ IonicTask.prototype.writeVersionData = function() { "date": this.versionData.release_date || this.versionData.date }; - var file = fs.createWriteStream( path.resolve('www/lib/ionic/version.json') ); - file.write( JSON.stringify(versionData, null, 2) ); - file.close(); + var fstream = fs.createWriteStream( path.resolve('www/lib/ionic/version.json') ); + fstream.on('error', function(err) { + console.log( ('writeVersionData err: ' + err).error ); + }); + fstream.write( JSON.stringify(versionData, null, 2) ); + fstream.close(); } catch(e) { console.log( ('Error writing version data: ' + e).error.bold ); } From db8eb6625e3fdfa6dee2ddf9d9a3c7f426f606fe Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 5 Sep 2014 10:12:55 -0500 Subject: [PATCH 0222/1100] reset config.xml after cordova exit --- lib/ionic/cordova.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/ionic/cordova.js b/lib/ionic/cordova.js index bb31d764fe..61adda5350 100644 --- a/lib/ionic/cordova.js +++ b/lib/ionic/cordova.js @@ -88,9 +88,11 @@ IonicTask.prototype.run = function(ionic) { cordovaProcess.on('exit', function(){ if(self.isLiveReload) { - self.setConfigXml({ - resetContent: true - }); + setTimeout(function(){ + self.setConfigXml({ + resetContent: true + }); + }, 5000); } }); @@ -136,12 +138,15 @@ IonicTask.prototype.setConfigXml = function(options) { } if(jsonConfig.widget.content[0].$.src !== options.devServer) { jsonConfig.widget.content[0].$.src = options.devServer; + console.log( ('Update config.xml for live reload: content[src="' + options.devServer + '"]').yellow ); madeChange = true; } } else if(options.resetContent) { + if( jsonConfig.widget.content[0].$['original-src'] ) { jsonConfig.widget.content[0].$.src = jsonConfig.widget.content[0].$['original-src']; + console.log( ('Reset config.xml after live reload build, return to content[src="' + jsonConfig.widget.content[0].$.src + '"]').yellow ); delete jsonConfig.widget.content[0].$['original-src']; madeChange = true; } @@ -150,7 +155,6 @@ IonicTask.prototype.setConfigXml = function(options) { if(madeChange) { var xmlBuilder = new xml2js.Builder(); configString = xmlBuilder.buildObject(jsonConfig); - fs.writeFileSync(configXmlPath, configString); } @@ -178,11 +182,11 @@ IonicTask.prototype.addHooks = function() { if(err) return; for(var x=0; x -1) continue; - addcliHookDirectory( path.join(cliHooksPath, files[x]), files[x] ); + addCliHookDirectory( path.join(cliHooksPath, files[x]), files[x] ); } }); - function addcliHookDirectory(cliHookPath, hookDirectoryName) { + function addCliHookDirectory(cliHookPath, hookDirectoryName) { fs.readdir(cliHookPath, function(err, files){ // loop through each of the scripts in the ionic-cli hook directory if(err) return; @@ -218,7 +222,7 @@ IonicTask.prototype.addHooks = function() { var cliScript = path.join(cliHookPath, hookFilename); fs.createReadStream( cliScript ).pipe(fs.createWriteStream( projectScript )); } catch(e) { - console.log( ('addcliHookDirectory fs.createReadStream: ' + e).error ); + console.log( ('addCliHookDirectory fs.createReadStream: ' + e).error ); return; } } @@ -227,7 +231,7 @@ IonicTask.prototype.addHooks = function() { try { fs.chmodSync(projectScript, '755'); } catch(e) { - console.log( ('addcliHookDirectory fs.chmodSync: ' + e).error ); + console.log( ('addCliHookDirectory fs.chmodSync: ' + e).error ); } } catch(e) { From 55dbf15b96f44c2925715fe7c4671453e6bb30ec Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 5 Sep 2014 10:13:23 -0500 Subject: [PATCH 0223/1100] update logging --- lib/ionic/serve.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index a33458cf21..3d1b059081 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -120,8 +120,12 @@ IonicTask.prototype.start = function(ionic) { fs.readFile( path.resolve(path.join(platformWWW, platformUrl)), function (err, buf) { res.setHeader('Content-Type', 'application/javascript'); if (err) { - self.serverLog(req.url + ' (' + platformWWW + ')'); res.end('// mocked cordova.js response to prevent 404 errors during development'); + if(req.url == '/cordova.js') { + self.serverLog(req.url + ' (mocked)'); + } else { + self.serverLog(req.url + ' (Error ' + platformWWW + ')'); + } } else { self.serverLog(req.url + ' (' + platformWWW + ')'); res.end(buf); @@ -181,7 +185,7 @@ IonicTask.prototype.start = function(ionic) { IonicTask.prototype.serverLog = function(msg) { if(this.printServerLogs) { - console.log( ('serve: ' + msg).yellow ); + console.log( ('serve ' + msg).yellow ); } }; @@ -200,12 +204,14 @@ IonicTask.prototype.consoleLog = function(req) { var log = JSON.parse(body); var msg = log.index + ' '; - while(msg.length < 4) { + while(msg.length < 5) { msg += ' '; } + msg += ' ' + (log.ts + '').substr(7) + ' '; + msg += log.method; - while(msg.length < 14) { + while(msg.length < 24) { msg += ' '; } @@ -285,17 +291,17 @@ function insertConsoleLogScript(html) { var methods = "assert clear count debug dir dirxml error exception group groupCollapsed groupEnd info log markTimeline profile profileEnd table time timeEnd timeStamp trace warn".split(" ");\n\ var console = (window.console=window.console || {});\n\ var logCount = 0;\n\ + window.onerror = function(msg, url, line) {\n\ + if(msg && url) console.error(msg, url, (line ? "Line: " + line : ""));\n\ + };\n\ function sendConsoleLog(method, args) {\n\ try {\n\ var xhr = new XMLHttpRequest();\n\ xhr.open("POST", "/__ionic-cli/console", true);\n\ - xhr.send(JSON.stringify({ index: logCount, method: method, args: args }));\n\ + xhr.send(JSON.stringify({ index: logCount, method: method, ts: Date.now(), args: args }));\n\ logCount++;\n\ } catch(e){}\n\ }\n\ - window.onerror = function(msg, url, line) {\n\ - if(msg && url) console.error(msg, url, (line ? "Line: " + line : ""));\n\ - };\n\ for(var x=0; x Date: Fri, 5 Sep 2014 12:10:44 -0500 Subject: [PATCH 0224/1100] ask for which ip --- lib/ionic.js | 5 +++ lib/ionic/cordova.js | 69 +++++++++++++++++++++-------- lib/ionic/serve.js | 100 +++++++++++++++++++++++++++++++++++-------- 3 files changed, 139 insertions(+), 35 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index 2880ce0034..d5ef6b44c4 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -218,6 +218,11 @@ var TASKS = [ title: 'login', name: 'login', module: './ionic/login' + }, + { + title: 'ip', + name: 'ip', + module: './ionic/serve' } ]; diff --git a/lib/ionic/cordova.js b/lib/ionic/cordova.js index 61adda5350..77f15b973a 100644 --- a/lib/ionic/cordova.js +++ b/lib/ionic/cordova.js @@ -1,6 +1,7 @@ var Task = require('./task').Task, IonicStats = require('./stats').IonicStats, fs = require('fs'), + Q = require('q'), argv = require('optimist').argv, xml2js = require('xml2js'), path = require('path'), @@ -12,23 +13,34 @@ var IonicTask = function() {}; IonicTask.prototype = new Task(); IonicTask.prototype.run = function(ionic) { + this.ionic = ionic; var self = this; - self.ionic = ionic; var cmdName = process.argv[2].toLowerCase(); - var cmdArgs = (process.argv.length > 3 ? process.argv.slice(3) : []); - var cmdArg, x, y; + var q; - self.isLiveReload = ((cmdName == 'run' || cmdName == 'emulate') && (argv.livereload || argv['live-reload'] || argv.l)); + this.isLiveReload = ((cmdName == 'run' || cmdName == 'emulate') && (argv.livereload || argv['live-reload'] || argv.l)); + + if(this.isLiveReload) { + q = self.setupLiveReload(); - if(self.isLiveReload) { - self.setupLiveReload(); } else { // ensure the content node was set back to its original - self.setConfigXml({ + q = this.setConfigXml({ resetContent: true }); } + q.then(function(){ + self.runCordova(cmdName); + }) +}; + + +IonicTask.prototype.runCordova = function(cmdName) { + var self = this; + var cmdArgs = (process.argv.length > 3 ? process.argv.slice(3) : []); + var cmdArg, x, y; + // backwards compatibility prior to fully wrapping cordova cmds if(cmdName == 'platform') { // `ionic platform ` used to actually run `ionic platform add ` @@ -86,38 +98,58 @@ IonicTask.prototype.run = function(ionic) { } }); - cordovaProcess.on('exit', function(){ - if(self.isLiveReload) { + if(self.isLiveReload) { + cordovaProcess.on('exit', function(){ setTimeout(function(){ + // set it back to the original src after a few seconds self.setConfigXml({ resetContent: true }); }, 5000); - } - }); + }); + + process.on('exit', function(){ + // verify it was set back + self.setConfigXml({ + resetContent: true + }); + }); + } IonicStats.t(); }; IonicTask.prototype.setupLiveReload = function() { + var d = Q.defer(); + console.log('Setup Live Reload'.green.bold); + var self = this; var serve = new require('./serve'); var serveTask = new serve.IonicTask(); - serveTask.loadSettings(); - serveTask.runLivereload = true; - serveTask.launchBrowser = false; serveTask.isPlatformServe = true; - serveTask.start(this.ionic); - this.setConfigXml({ - devServer: serveTask.devServer + serveTask.loadSettings(function(){ + serveTask.runLivereload = true; + serveTask.launchBrowser = false; + serveTask.start(self.ionic); + + self.setConfigXml({ + devServer: serveTask.devServer + }).then(function(){ + d.resolve(); + }); + }); + + return d.promise; }; IonicTask.prototype.setConfigXml = function(options) { + var d = Q.defer(); + var self = this; var madeChange = false; @@ -158,11 +190,14 @@ IonicTask.prototype.setConfigXml = function(options) { fs.writeFileSync(configXmlPath, configString); } + d.resolve(); }); } catch(e) { return self.ionic.fail('Error updating config.xml file: ' + e); } + + return d.promise; }; diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 3d1b059081..de91740a5f 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -5,13 +5,10 @@ var fs = require('fs'), finalhandler = require('finalhandler'), http = require('http'), serveStatic = require('serve-static'), - open = require('open'), tinylr = require('tiny-lr-fork'), lr = require('connect-livereload'), vfs = require('vinyl-fs'), request = require('request'), - Q = require('q'), - spawn = require('child_process').spawn, IonicProject = require('./project'), Task = require('./task').Task; IonicStats = require('./stats').IonicStats; @@ -24,21 +21,30 @@ var IonicTask = function() {}; IonicTask.prototype = new Task(); IonicTask.prototype.run = function(ionic) { + var self = this; + this.port = argv._[1]; this.liveReloadPort = argv._[2]; - this.loadSettings(); - this.start(ionic); + this.loadSettings(function(){ + + if(argv._[0] == 'ip') { + console.log( this.ip ); + return; + } + + self.start(ionic); - if(ionic.hasFailed) return; + if(ionic.hasFailed) return; - ionic.latestVersion.promise.then(function(){ - ionic.printVersionWarning(); + ionic.latestVersion.promise.then(function(){ + ionic.printVersionWarning(); + }); }); }; -IonicTask.prototype.loadSettings = function() { +IonicTask.prototype.loadSettings = function(cb) { var project = IonicProject.load(); this.port = this.port || argv.port || argv.p || DEFAULT_HTTP_PORT; @@ -49,6 +55,8 @@ IonicTask.prototype.loadSettings = function() { this.watchSass = project.get('sass') === true && !argv.nosass && !argv.n; this.printServerLogs = argv.serverlogs || argv['server-logs'] || argv.s; this.printConsoleLogs = argv.consolelogs || argv['console-logs'] || argv.c; + + this.getIp(cb); }; IonicTask.prototype.start = function(ionic) { @@ -60,6 +68,7 @@ IonicTask.prototype.start = function(ionic) { } if(this.watchSass) { + var spawn = require('child_process').spawn; var childProcess = spawn('gulp', ['sass','watch']); childProcess.stdout.on('data', function (data) { @@ -88,6 +97,7 @@ IonicTask.prototype.start = function(ionic) { } else { console.log('Running live reload server:'.green.bold, self.liveReloadServer.bold ); if(self.launchBrowser) { + var open = require('open'); open( self.host(self.port) ); } } @@ -348,25 +358,79 @@ IonicTask.prototype._changed = function(filePath) { }); }; - -IonicTask.prototype.host = function(port) { - var addresses = []; - +IonicTask.prototype.getIp = function(cb) { try { + var self = this; + var addresses = []; var os = require('os'); var ifaces = os.networkInterfaces(); for (var dev in ifaces) { - if(!dev || dev.indexOf('box') > -1) continue; ifaces[dev].forEach(function(details){ - if (details.family == 'IPv4' && !details.internal) { - addresses.push(details.address); + if (details.family == 'IPv4' && !details.internal && details.address) { + addresses.push({ + ip: details.address, + dev: dev + }); + } + }); + } + + if(addresses.length === 1) { + this._ip = addresses[0].ip; + cb(); + + } else if(addresses.length > 1 && this.isPlatformServe) { + console.log('\nMultiple IPv4 addresses available.'.error.bold); + console.log('Please select which address to use by entering its number from the list below:'.error.bold); + + for(var x=0; x Date: Fri, 5 Sep 2014 14:10:30 -0500 Subject: [PATCH 0225/1100] no random package name for tmp --- lib/ionic/start.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 01858e3a14..ae4a41b5ec 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -377,7 +377,7 @@ IonicTask.prototype.updateConfigXml = function() { } if(!self.packageName) { - var packageName = self.appDirectory + Math.round((Math.random() * 899999) + 100000); + var packageName = self.appDirectory + (self.appDirectory !== 'tmp' ? Math.round((Math.random() * 899999) + 100000) : ''); self.packageName = 'com.ionicframework.' + packageName.replace(/\./g, ''); } From c4053583c508fd72ade002d063762e7b93f70744 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 5 Sep 2014 14:11:10 -0500 Subject: [PATCH 0226/1100] prevent bad console logs --- lib/ionic/serve.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index de91740a5f..00957213fa 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -230,7 +230,7 @@ IonicTask.prototype.consoleLog = function(req) { msgIndent += ' '; } - if(log.method == 'dir') { + if(log.method == 'dir' || log.method == 'table') { var isFirstLine = true; log.args.forEach(function(argObj){ @@ -251,7 +251,8 @@ IonicTask.prototype.consoleLog = function(req) { }); - } else { + } else if(log.args.length) { + if(log.args.length === 2 && log.args[0] === '%o' && log.args[1] == '[object Object]') return; msg += log.args.join(', '); } From 963d293984267fe4d2156c5399d08e8e5cb08232 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 5 Sep 2014 14:11:30 -0500 Subject: [PATCH 0227/1100] 1.2.1-beta2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 153ac78d0f..b2b9a6fe3f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.2.1-beta1", + "version": "1.2.1-beta2", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From da30b43d7a638fb91159d5cfdfa632c67dec14df Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 5 Sep 2014 14:38:05 -0500 Subject: [PATCH 0228/1100] fix ip --- lib/ionic/serve.js | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 00957213fa..860080d787 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -28,8 +28,8 @@ IonicTask.prototype.run = function(ionic) { this.loadSettings(function(){ - if(argv._[0] == 'ip') { - console.log( this.ip ); + if(self.isIpCmd) { + console.log( self.ip ); return; } @@ -55,8 +55,16 @@ IonicTask.prototype.loadSettings = function(cb) { this.watchSass = project.get('sass') === true && !argv.nosass && !argv.n; this.printServerLogs = argv.serverlogs || argv['server-logs'] || argv.s; this.printConsoleLogs = argv.consolelogs || argv['console-logs'] || argv.c; + this.isIpCmd = argv._[0].toLowerCase() == 'ip'; this.getIp(cb); + + process.stdin.on('readable', function() { + var chunk = process.stdin.read(); + if (chunk !== null && /exit|quit|close|stop/gi.test(chunk)) { + process.exit(); + } + }); }; IonicTask.prototype.start = function(ionic) { @@ -95,7 +103,7 @@ IonicTask.prototype.start = function(ionic) { if(err) { return ionic.fail('Unable to start live reload server:', err); } else { - console.log('Running live reload server:'.green.bold, self.liveReloadServer.bold ); + console.log('Running live reload server:'.green.bold, self.liveReloadServer ); if(self.launchBrowser) { var open = require('open'); open( self.host(self.port) ); @@ -182,14 +190,7 @@ IonicTask.prototype.start = function(ionic) { app.use(server); app.listen(this.port); - console.log('Running dev server:'.green.bold, this.devServer.bold); - - process.stdin.on('readable', function() { - var chunk = process.stdin.read(); - if (chunk !== null && /exit|quit|close|stop/gi.test(chunk)) { - process.exit(); - } - }); + console.log('Running dev server:'.green.bold, this.devServer); }; @@ -378,10 +379,10 @@ IonicTask.prototype.getIp = function(cb) { } if(addresses.length === 1) { - this._ip = addresses[0].ip; + this.ip = addresses[0].ip; cb(); - } else if(addresses.length > 1 && this.isPlatformServe) { + } else if(addresses.length > 1) { console.log('\nMultiple IPv4 addresses available.'.error.bold); console.log('Please select which address to use by entering its number from the list below:'.error.bold); @@ -412,13 +413,16 @@ IonicTask.prototype.getIp = function(cb) { for(var x=0; x Date: Fri, 5 Sep 2014 14:38:18 -0500 Subject: [PATCH 0229/1100] add livereload --- lib/ionic/stats.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/ionic/stats.js b/lib/ionic/stats.js index 7c47ecd853..5d05ffb4b5 100644 --- a/lib/ionic/stats.js +++ b/lib/ionic/stats.js @@ -542,7 +542,9 @@ exports.IonicStats = { '-w': '--no-cordova', '-b': '--nobrowser', '-r': '--nolivereload', - '-l': '--clear-signing', + '-l': '--livereload', + '-c': '--consolelogs', + '-s': '--serverlogs', '-n': '--no-email', '-s': '--sass' }; @@ -555,7 +557,7 @@ exports.IonicStats = { } var platformWhitelist = 'android ios firefoxos wp7 wp8 amazon-fireos blackberry10 tizen'.split(' '); - var argsWhitelist = 'add remove list update check debug release search --no-cordova --nobrowser --nolivereload --no-email --debug --release --device --emulator --sass'.split(' '); + var argsWhitelist = 'add remove list update check debug release search --livereload --consolelogs --serverlogs --no-cordova --nobrowser --nolivereload --no-email --debug --release --device --emulator --sass'.split(' '); // collect only certain args, skip over everything else for(x=0; x Date: Fri, 5 Sep 2014 14:38:37 -0500 Subject: [PATCH 0230/1100] 1.2.1-beta3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b2b9a6fe3f..1932a0eace 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.2.1-beta2", + "version": "1.2.1-beta3", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From a4bf801fe88f90d1ecd943ffc74076fee136e68d Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 5 Sep 2014 15:34:37 -0500 Subject: [PATCH 0231/1100] allow content src other than index.html --- lib/ionic.js | 33 +++++++++++++++++++++++++++++++++ lib/ionic/cordova.js | 1 + lib/ionic/serve.js | 32 +++++++++++++++++++------------- lib/ionic/setup.js | 2 +- 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index d5ef6b44c4..abde36a490 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -522,6 +522,39 @@ Ionic = { }); }, + getContentSrc: function() { + var self = this; + var contentSrc; + + try { + var fs = require('fs'); + var path = require('path'); + var configXmlPath = path.resolve('config.xml'); + if( !fs.existsSync(configXmlPath) ) { + return 'index.html'; + } + var configString = fs.readFileSync(configXmlPath, { encoding: 'utf8' }); + + var xml2js = require('xml2js'); + var parseString = xml2js.parseString; + parseString(configString, function (err, jsonConfig) { + if(err) { + return self.fail('Error parsing config.xml: ' + err); + } + try { + contentSrc = jsonConfig.widget.content[0].$.src; + } catch(e) { + return self.fail('Error parsing ' + configXmlPath + ': ' + e); + } + }); + + } catch(e) { + return self.fail('Error loading ' + configXmlPath + ': ' + e); + } + + return contentSrc; + }, + /** * Download a zip file, unzip it to a specific folder. */ diff --git a/lib/ionic/cordova.js b/lib/ionic/cordova.js index 77f15b973a..f302559baf 100644 --- a/lib/ionic/cordova.js +++ b/lib/ionic/cordova.js @@ -128,6 +128,7 @@ IonicTask.prototype.setupLiveReload = function() { var self = this; var serve = new require('./serve'); var serveTask = new serve.IonicTask(); + serveTask.ionic = this.ionic; serveTask.isPlatformServe = true; serveTask.loadSettings(function(){ diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 860080d787..080d76fcd5 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -22,7 +22,7 @@ IonicTask.prototype = new Task(); IonicTask.prototype.run = function(ionic) { var self = this; - + this.ionic = ionic; this.port = argv._[1]; this.liveReloadPort = argv._[2]; @@ -56,6 +56,7 @@ IonicTask.prototype.loadSettings = function(cb) { this.printServerLogs = argv.serverlogs || argv['server-logs'] || argv.s; this.printConsoleLogs = argv.consolelogs || argv['console-logs'] || argv.c; this.isIpCmd = argv._[0].toLowerCase() == 'ip'; + this.contentSrc = path.join('www', this.ionic.getContentSrc()); this.getIp(cb); @@ -165,20 +166,25 @@ IonicTask.prototype.start = function(ionic) { return; } - if(req.url === '/') { - fs.readFile( path.resolve('www/index.html'), 'utf8', function (err, buf) { - res.setHeader('Content-Type', 'text/html'); - if (err) { - self.serverLog(req.url + ' : ERROR!'); - res.end(err.toString()); - } else { - self.serverLog(req.url); + } + + if(req.url === '/') { + + fs.readFile( path.resolve(self.contentSrc), 'utf8', function (err, buf) { + res.setHeader('Content-Type', 'text/html'); + if (err) { + self.serverLog(req.url + ' : ERROR!'); + res.end(err.toString()); + } else { + self.serverLog(req.url + ' (' + self.contentSrc + ')'); + if(self.printConsoleLogs) { res.end( insertConsoleLogScript(buf.toString('utf8')) ); + } else { + res.end(buf); } - }); - return; - } - + } + }); + return; } // root www directory file diff --git a/lib/ionic/setup.js b/lib/ionic/setup.js index 0528d4990b..940fdcf079 100644 --- a/lib/ionic/setup.js +++ b/lib/ionic/setup.js @@ -37,7 +37,7 @@ IonicTask.prototype.sassSetup = function(ionic) { this.npmInstall().then(function(){ - var indexPath = path.resolve('www/index.html'); + var indexPath = path.resolve( path.join('www', ionic.getContentSrc()) ); var lines, line, keepLine, activeRemoving; var cleanedLines = []; From c197d08cd7568b918bfd84276a960672c2ff1d52 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 5 Sep 2014 15:35:08 -0500 Subject: [PATCH 0232/1100] 1.2.1-beta4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1932a0eace..37c0ede6a6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.2.1-beta3", + "version": "1.2.1-beta4", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From dd3681b70e4e532d602189046741e45f3b105582 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 5 Sep 2014 15:45:58 -0500 Subject: [PATCH 0233/1100] v1.2.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 37c0ede6a6..e758fb08cc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.2.1-beta4", + "version": "1.2.1", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 91aee1d7b61d2a59008e410198101bda3a305078 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Sat, 6 Sep 2014 16:48:06 -0500 Subject: [PATCH 0234/1100] S fix --- lib/ionic/stats.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/stats.js b/lib/ionic/stats.js index 5d05ffb4b5..230753c00f 100644 --- a/lib/ionic/stats.js +++ b/lib/ionic/stats.js @@ -596,7 +596,7 @@ exports.IonicStats = { if(statsData.cli_version.indexOf('beta') > -1) return; } catch(e2) {} - IonicStats.mp(cmdName, statsData); + this.mp(cmdName, statsData); //console.log(cmdName, statsData); } catch(e) { From 0718ed2015ec5ba6fbc3e3b21abf391d421d9b65 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Sat, 6 Sep 2014 16:48:22 -0500 Subject: [PATCH 0235/1100] Sfix --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e758fb08cc..660e31570c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.2.1", + "version": "1.2.2", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From f98ea25ab01ddc4d756798276533914e9fe64277 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sat, 6 Sep 2014 20:50:50 -0500 Subject: [PATCH 0236/1100] remove reset config log --- lib/ionic/cordova.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ionic/cordova.js b/lib/ionic/cordova.js index f302559baf..49d4c2ef3b 100644 --- a/lib/ionic/cordova.js +++ b/lib/ionic/cordova.js @@ -179,7 +179,6 @@ IonicTask.prototype.setConfigXml = function(options) { if( jsonConfig.widget.content[0].$['original-src'] ) { jsonConfig.widget.content[0].$.src = jsonConfig.widget.content[0].$['original-src']; - console.log( ('Reset config.xml after live reload build, return to content[src="' + jsonConfig.widget.content[0].$.src + '"]').yellow ); delete jsonConfig.widget.content[0].$['original-src']; madeChange = true; } From 5afe67dafd23f726557a353c6c25484c0dea8522 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sat, 6 Sep 2014 21:26:06 -0500 Subject: [PATCH 0237/1100] remove config update log --- lib/ionic/cordova.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ionic/cordova.js b/lib/ionic/cordova.js index 49d4c2ef3b..a024c7f88d 100644 --- a/lib/ionic/cordova.js +++ b/lib/ionic/cordova.js @@ -171,7 +171,6 @@ IonicTask.prototype.setConfigXml = function(options) { } if(jsonConfig.widget.content[0].$.src !== options.devServer) { jsonConfig.widget.content[0].$.src = options.devServer; - console.log( ('Update config.xml for live reload: content[src="' + options.devServer + '"]').yellow ); madeChange = true; } From 13228bdc02dc85d81e120f9cd606ef649f5d89fe Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sat, 6 Sep 2014 22:17:32 -0500 Subject: [PATCH 0238/1100] correct errors when in wrong working directory --- lib/ionic/cordova.js | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/ionic/cordova.js b/lib/ionic/cordova.js index a024c7f88d..87098d5443 100644 --- a/lib/ionic/cordova.js +++ b/lib/ionic/cordova.js @@ -26,7 +26,8 @@ IonicTask.prototype.run = function(ionic) { } else { // ensure the content node was set back to its original q = this.setConfigXml({ - resetContent: true + resetContent: true, + errorWhenNotFound: false }); } @@ -103,7 +104,8 @@ IonicTask.prototype.runCordova = function(cmdName) { setTimeout(function(){ // set it back to the original src after a few seconds self.setConfigXml({ - resetContent: true + resetContent: true, + errorWhenNotFound: true }); }, 5000); }); @@ -111,7 +113,8 @@ IonicTask.prototype.runCordova = function(cmdName) { process.on('exit', function(){ // verify it was set back self.setConfigXml({ - resetContent: true + resetContent: true, + errorWhenNotFound: false }); }); } @@ -156,12 +159,24 @@ IonicTask.prototype.setConfigXml = function(options) { try { var configXmlPath = path.resolve('config.xml'); + + if(!fs.existsSync(configXmlPath)) { + // working directory does not have the config.xml file + if(options.errorWhenNotFound) { + d.reject('Unable to locate config.xml file. Please ensure the working directory is at the root of the app where the config.xml should be located.'); + } else { + d.resolve(); + } + return d.promise; + } + var configString = fs.readFileSync(configXmlPath, { encoding: 'utf8' }); var parseString = xml2js.parseString; parseString(configString, function (err, jsonConfig) { if(err) { - return self.ionic.fail('Error parsing config.xml: ' + err); + d.reject(err); + return self.ionic.fail('Error parsing ' + configXmlPath + ': ' + err); } if(options.devServer) { @@ -193,7 +208,8 @@ IonicTask.prototype.setConfigXml = function(options) { }); } catch(e) { - return self.ionic.fail('Error updating config.xml file: ' + e); + d.reject(e); + self.ionic.fail('Error updating ' + configXmlPath + ': ' + e); } return d.promise; From ff1ee3f7af9aab9ce974a37893d30ec47e1464ec Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sat, 6 Sep 2014 22:34:42 -0500 Subject: [PATCH 0239/1100] improve IPv4 error handling --- lib/ionic/serve.js | 38 +++++++++++++++++++++++--------------- package.json | 2 +- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 080d76fcd5..12cbd532e6 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -30,7 +30,7 @@ IonicTask.prototype.run = function(ionic) { if(self.isIpCmd) { console.log( self.ip ); - return; + process.exit(); } self.start(ionic); @@ -373,15 +373,18 @@ IonicTask.prototype.getIp = function(cb) { var os = require('os'); var ifaces = os.networkInterfaces(); - for (var dev in ifaces) { - ifaces[dev].forEach(function(details){ - if (details.family == 'IPv4' && !details.internal && details.address) { - addresses.push({ - ip: details.address, - dev: dev - }); - } - }); + if(ifaces){ + for (var dev in ifaces) { + if(!dev) continue; + ifaces[dev].forEach(function(details){ + if (details && details.family == 'IPv4' && !details.internal && details.address) { + addresses.push({ + ip: details.address, + dev: dev + }); + } + }); + } } if(addresses.length === 1) { @@ -393,7 +396,7 @@ IonicTask.prototype.getIp = function(cb) { console.log('Please select which address to use by entering its number from the list below:'.error.bold); for(var x=0; x Date: Sun, 7 Sep 2014 01:46:00 -0500 Subject: [PATCH 0240/1100] go to url live reload plugin --- lib/ionic/serve.js | 100 ++++++++++++++++++++++++++++++++++++++++----- package.json | 2 +- 2 files changed, 91 insertions(+), 11 deletions(-) diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 12cbd532e6..fd0e33f2d7 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -46,6 +46,7 @@ IonicTask.prototype.run = function(ionic) { IonicTask.prototype.loadSettings = function(cb) { var project = IonicProject.load(); + var self = this; this.port = this.port || argv.port || argv.p || DEFAULT_HTTP_PORT; this.liveReloadPort = this.liveReloadPort || argv.livereloadport || argv['livereload-port'] || argv.i || DEFAULT_LIVE_RELOAD_PORT; @@ -61,8 +62,21 @@ IonicTask.prototype.loadSettings = function(cb) { this.getIp(cb); process.stdin.on('readable', function() { - var chunk = process.stdin.read(); - if (chunk !== null && /exit|quit|close|stop/gi.test(chunk)) { + var input = process.stdin.read(); + if (input === null) return; + input = (input + '').trim();; + + if(input.toLowerCase() == 'restart') { + self._goToUrl('/'); + + } else if(input.toLowerCase().indexOf('goto ') === 0) { + var url = input.replace(/goto /i, ''); + self._goToUrl(url); + + } else if(input.match(/^go\([+-]?[0-9]{1,9}\)$/)) { + self._goToHistory(input); + + } else if(/exit|quit|close|stop/i.test(input)) { process.exit(); } }); @@ -177,11 +191,14 @@ IonicTask.prototype.start = function(ionic) { res.end(err.toString()); } else { self.serverLog(req.url + ' (' + self.contentSrc + ')'); + + var html = injectGoToScript( buf.toString('utf8') ); + if(self.printConsoleLogs) { - res.end( insertConsoleLogScript(buf.toString('utf8')) ); - } else { - res.end(buf); + html = injectConsoleLogScript(html); } + + res.end(html); } }); return; @@ -298,7 +315,7 @@ function getPlatformWWW(req) { } -function insertConsoleLogScript(html) { +function injectConsoleLogScript(html) { try{ var headTag = html.match(//gi)[0]; @@ -338,34 +355,97 @@ function insertConsoleLogScript(html) { return html; } + +function injectGoToScript(html) { + try{ + var headTag = html.match(//gi)[0]; + + return html.replace(headTag, headTag + '\n\ + '); + }catch(e){} + + return html; +} + + IonicTask.prototype._changed = function(filePath) { // Cleanup the path a bit var pwd = process.cwd(); filePath = filePath.replace(pwd + '/', ''); if( filePath.indexOf('.css') > 0 ) { - console.log( (' CSS changed: ' + filePath).green ); + console.log( ('CSS changed: ' + filePath).green ); } else if( filePath.indexOf('.js') > 0 ) { - console.log( (' JS changed: ' + filePath).green ); + console.log( ('JS changed: ' + filePath).green ); } else if( filePath.indexOf('.html') > 0 ) { console.log( ('HTML changed: ' + filePath).green ); } else { console.log( ('File changed: ' + filePath).green ); } - var req = request.post('http://localhost:' + this.liveReloadPort + '/changed', { + this._postToLiveReload( [filePath] ); +}; + + +IonicTask.prototype._goToUrl = function(url) { + console.log( ('Loading: ' + url).green ); + this._postToLiveReload( ['__ionic_goto_url__' + url] ); +}; + +IonicTask.prototype._goToHistory = function(goHistory) { + goHistory = goHistory.replace('go(', '').replace(')', ''); + console.log( ('History Go: ' + goHistory).green ); + this._postToLiveReload( ['__ionic_history_go__' + goHistory] ); +}; + + +IonicTask.prototype._postToLiveReload = function(files) { + + request.post('http://localhost:' + this.liveReloadPort + '/changed', { path: '/changed', method: 'POST', body: JSON.stringify({ - files: [filePath] + files: files }) }, function(err, res, body) { if(err) { console.error('Unable to update live reload:', err); } }); + }; + IonicTask.prototype.getIp = function(cb) { try { var self = this; diff --git a/package.json b/package.json index 32fc3377d1..43aa2f33b5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.2.3-beta1", + "version": "1.2.3-beta2", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From e53dcab453c1cf56d8099993f6d9cc84ee9c54d6 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sun, 7 Sep 2014 20:34:51 -0500 Subject: [PATCH 0241/1100] index.html cache busting and `r` alias for `restart` --- lib/ionic/serve.js | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index fd0e33f2d7..b8889acc93 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -66,8 +66,8 @@ IonicTask.prototype.loadSettings = function(cb) { if (input === null) return; input = (input + '').trim();; - if(input.toLowerCase() == 'restart') { - self._goToUrl('/'); + if(input.toLowerCase() == 'restart' || input.toLowerCase() == 'r') { + self._goToUrl('/?restart=' + Math.floor((Math.random() * 899999) + 100000)); } else if(input.toLowerCase().indexOf('goto ') === 0) { var url = input.replace(/goto /i, ''); @@ -172,18 +172,13 @@ IonicTask.prototype.start = function(ionic) { return; } - if(self.printConsoleLogs) { - - if(req.url === '/__ionic-cli/console') { - self.consoleLog(req); - res.end(''); - return; - } - + if(self.printConsoleLogs && req.url === '/__ionic-cli/console') { + self.consoleLog(req); + res.end(''); + return; } - if(req.url === '/') { - + if(req.url.split('?')[0] === '/') { fs.readFile( path.resolve(self.contentSrc), 'utf8', function (err, buf) { res.setHeader('Content-Type', 'text/html'); if (err) { From 4476985af206fd3a6902ab55c38735e757908609 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sun, 7 Sep 2014 22:08:41 -0500 Subject: [PATCH 0242/1100] improve server cmds, script placement, logs --- lib/ionic/serve.js | 89 ++++++++++++++++++++++++++++++++++------------ package.json | 2 +- 2 files changed, 67 insertions(+), 24 deletions(-) diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index b8889acc93..da02c70bff 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -54,8 +54,8 @@ IonicTask.prototype.loadSettings = function(cb) { this.launchBrowser = !argv.nobrowser && !argv.b; this.runLivereload = !argv.nolivereload && !argv.r; this.watchSass = project.get('sass') === true && !argv.nosass && !argv.n; - this.printServerLogs = argv.serverlogs || argv['server-logs'] || argv.s; this.printConsoleLogs = argv.consolelogs || argv['console-logs'] || argv.c; + this.printServerLogs = argv.serverlogs || argv['server-logs'] || argv.s; this.isIpCmd = argv._[0].toLowerCase() == 'ip'; this.contentSrc = path.join('www', this.ionic.getContentSrc()); @@ -64,24 +64,48 @@ IonicTask.prototype.loadSettings = function(cb) { process.stdin.on('readable', function() { var input = process.stdin.read(); if (input === null) return; - input = (input + '').trim();; + input = (input + '').trim(); if(input.toLowerCase() == 'restart' || input.toLowerCase() == 'r') { self._goToUrl('/?restart=' + Math.floor((Math.random() * 899999) + 100000)); - } else if(input.toLowerCase().indexOf('goto ') === 0) { - var url = input.replace(/goto /i, ''); + } else if(input.toLowerCase().indexOf('goto ') === 0 || input.toLowerCase().indexOf('g ') === 0) { + var url = input.replace('goto ', '').replace('g ', ''); self._goToUrl(url); - } else if(input.match(/^go\([+-]?[0-9]{1,9}\)$/)) { + } else if(input.toLowerCase() == 'consolelogs' || input.toLowerCase() == 'c') { + self.printConsoleLogs = !self.printConsoleLogs; + console.log('Console log output: '.green + (self.printConsoleLogs ? 'enabled' : 'disabled')); + + } else if(input.toLowerCase() == 'serverlogs' || input.toLowerCase() == 's') { + self.printServerLogs = !self.printServerLogs; + console.log('Server log output: '.green + (self.printServerLogs ? 'enabled' : 'disabled')); + + } else if(input.match(/^go\([+\-]?[0-9]{1,9}\)$/)) { self._goToHistory(input); } else if(/exit|quit|close|stop/i.test(input)) { process.exit(); + } else { + console.log('Invalid server command'.error.bold); + self.printCommandTips(); } + }); }; + +IonicTask.prototype.printCommandTips = function(ionic) { + console.log('Server commands, enter:'.green.bold); + console.log(' restart' + ' or '.green + 'r' + ' to restart the app from the root'.green); + console.log(' goto' + ' or '.green + 'g' + ' and a url to have the app navigate to the given url'.green); + console.log(' consolelogs' + ' or '.green + 'c' + ' to enable/disable console log output'.green); + console.log(' serverlogs' + ' or '.green + 's' + ' to enable/disable server log output'.green); + console.log(' exit' + ' to shutdown the server and exit'.green); + console.log(''); +}; + + IonicTask.prototype.start = function(ionic) { var self = this; var app = connect(); @@ -123,6 +147,7 @@ IonicTask.prototype.start = function(ionic) { var open = require('open'); open( self.host(self.port) ); } + self.printCommandTips(); } }); @@ -131,11 +156,6 @@ IonicTask.prototype.start = function(ionic) { })); } - if(this.printConsoleLogs) { - self.consoleServer = self.host(self.consoleLogPort); - - } - this.devServer = this.host(this.port); // Serve up the www folder by default @@ -155,17 +175,17 @@ IonicTask.prototype.start = function(ionic) { if (err) { res.end('// mocked cordova.js response to prevent 404 errors during development'); if(req.url == '/cordova.js') { - self.serverLog(req.url + ' (mocked)'); + self.serverLog(req, '(mocked)'); } else { - self.serverLog(req.url + ' (Error ' + platformWWW + ')'); + self.serverLog(req, '(Error ' + platformWWW + ')'); } } else { - self.serverLog(req.url + ' (' + platformWWW + ')'); + self.serverLog(req, '(' + platformWWW + ')'); res.end(buf); } }); } else { - self.serverLog(req.url + ' (mocked)'); + self.serverLog(req, '(mocked)'); res.setHeader('Content-Type', 'application/javascript'); res.end('// mocked cordova.js response to prevent 404 errors during development'); } @@ -182,10 +202,10 @@ IonicTask.prototype.start = function(ionic) { fs.readFile( path.resolve(self.contentSrc), 'utf8', function (err, buf) { res.setHeader('Content-Type', 'text/html'); if (err) { - self.serverLog(req.url + ' : ERROR!'); + self.serverLog(req, 'ERROR!'); res.end(err.toString()); } else { - self.serverLog(req.url + ' (' + self.contentSrc + ')'); + self.serverLog(req, '(' + self.contentSrc + ')'); var html = injectGoToScript( buf.toString('utf8') ); @@ -200,7 +220,7 @@ IonicTask.prototype.start = function(ionic) { } // root www directory file - self.serverLog(req.url); + self.serverLog(req); serve(req, res, done); }); @@ -212,9 +232,26 @@ IonicTask.prototype.start = function(ionic) { }; -IonicTask.prototype.serverLog = function(msg) { +IonicTask.prototype.serverLog = function(req, msg) { if(this.printServerLogs) { - console.log( ('serve ' + msg).yellow ); + var log = 'serve '.yellow; + + log += (req.url.length > 60 ? req.url.substr(0, 57) + '...' : req.url).yellow; + + if(msg) { + log += ' ' + msg.yellow; + } + + var ua = (req.headers && req.headers['user-agent'] || ''); + if(ua.indexOf('Android') > 0) { + log += ' Android'.small; + } else if(ua.indexOf('iPhone') > -1 || ua.indexOf('iPad') > -1 || ua.indexOf('iPod') > -1) { + log += ' iOS'.small; + } else if(ua.indexOf('Windows Phone') > -1) { + log += ' Windows Phone'.small; + } + + console.log(log); } }; @@ -312,9 +349,10 @@ function getPlatformWWW(req) { function injectConsoleLogScript(html) { try{ - var headTag = html.match(//gi)[0]; + var findTags = html.match(/])(.*?)>||/gi); + var insertAfter = findTags[ findTags.length - 1 ]; - return html.replace(headTag, headTag + '\n\ + return html.replace(insertAfter, insertAfter + '\n\ + + + + + + + +
+
+

iPhone 6

+ +
+
+
+
+

Android

+ +
+
+
+ + + diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 6beb5b9e7b..0d0095c5a9 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -54,6 +54,7 @@ IonicTask.prototype.loadSettings = function(cb) { this.liveReloadPort = this.liveReloadPort || argv.livereloadport || argv['livereload-port'] || argv.i || DEFAULT_LIVE_RELOAD_PORT; this.launchBrowser = !argv.nobrowser && !argv.b; + this.launchLab = this.launchBrowser && argv.lab; this.runLivereload = !argv.nolivereload && !argv.r; this.useProxy = !argv.noproxy && !argv.x; this.proxies = project.get('proxies') || []; @@ -164,7 +165,10 @@ IonicTask.prototype.start = function(ionic) { } else { console.log('Running live reload server:'.green.bold, self.liveReloadServer ); console.log('Watching :'.green.bold, self.watchPatterns); - if(self.launchBrowser) { + if(self.launchLab) { + var open = require('open'); + open( self.host(self.port) + '/lab' ); + } else if(self.launchBrowser) { var open = require('open'); open( self.host(self.port) ); } @@ -230,6 +234,17 @@ IonicTask.prototype.start = function(ionic) { return; } + if(req.url === '/lab') { + fs.readFile(path.resolve(path.join(__dirname, 'assets/preview.html')), function(err, buf) { + if(err) { + res.end('404'); + } + res.setHeader('Content-Type', 'text/html'); + res.end(buf); + }); + return; + } + if(req.url.split('?')[0] === '/') { fs.readFile( path.resolve(self.contentSrc), 'utf8', function (err, buf) { res.setHeader('Content-Type', 'text/html'); diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 242cf60f8e..ceae9c9547 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -5,81 +5,98 @@ "archiver": { "version": "0.5.1", "from": "archiver@0.5.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-0.5.1.tgz", "dependencies": { "readable-stream": { "version": "1.1.13", - "from": "readable-stream@~1.1.9", + "from": "readable-stream@1.1.13", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", "dependencies": { "core-util-is": { "version": "1.0.1", - "from": "core-util-is@~1.0.0" + "from": "core-util-is@1.0.1", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { "version": "0.0.1", - "from": "isarray@0.0.1" + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@~0.10.x" + "from": "string_decoder@0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@~2.0.1" + "from": "inherits@2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } }, "zip-stream": { "version": "0.1.4", - "from": "zip-stream@~0.1.0", + "from": "zip-stream@0.1.4", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-0.1.4.tgz", "dependencies": { "readable-stream": { "version": "1.0.33-1", - "from": "readable-stream@>=1.0.33-1 <1.1.0-0", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", "dependencies": { "core-util-is": { "version": "1.0.1", - "from": "core-util-is@~1.0.0" + "from": "core-util-is@1.0.1", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { "version": "0.0.1", - "from": "isarray@0.0.1" + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@~0.10.x" + "from": "string_decoder@0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@~2.0.1" + "from": "inherits@2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } }, "lodash.defaults": { "version": "2.4.1", - "from": "lodash.defaults@~2.4.1", + "from": "lodash.defaults@2.4.1", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz", "dependencies": { "lodash.keys": { "version": "2.4.1", - "from": "lodash.keys@~2.4.1", + "from": "lodash.keys@2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", "dependencies": { "lodash._isnative": { "version": "2.4.1", - "from": "lodash._isnative@~2.4.1" + "from": "lodash._isnative@2.4.1", + "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz" }, "lodash.isobject": { "version": "2.4.1", - "from": "lodash.isobject@~2.4.1" + "from": "lodash.isobject@2.4.1", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz" }, "lodash._shimkeys": { "version": "2.4.1", - "from": "lodash._shimkeys@~2.4.1" + "from": "lodash._shimkeys@2.4.1", + "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz" } } }, "lodash._objecttypes": { "version": "2.4.1", - "from": "lodash._objecttypes@~2.4.1" + "from": "lodash._objecttypes@2.4.1", + "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz" } } } @@ -87,28 +104,33 @@ }, "lazystream": { "version": "0.1.0", - "from": "lazystream@~0.1.0", + "from": "lazystream@0.1.0", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-0.1.0.tgz", "dependencies": { "readable-stream": { "version": "1.0.33-1", - "from": "readable-stream@>=1.0.33-1 <1.1.0-0", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", "dependencies": { "core-util-is": { "version": "1.0.1", - "from": "core-util-is@~1.0.0" + "from": "core-util-is@1.0.1", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { "version": "0.0.1", - "from": "isarray@0.0.1" + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@~0.10.x" + "from": "string_decoder@0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@~2.0.1" + "from": "inherits@2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } } @@ -116,39 +138,48 @@ }, "file-utils": { "version": "0.1.5", - "from": "file-utils@~0.1.5", + "from": "file-utils@0.1.5", + "resolved": "https://registry.npmjs.org/file-utils/-/file-utils-0.1.5.tgz", "dependencies": { "lodash": { "version": "2.1.0", - "from": "lodash@~2.1.0" + "from": "lodash@2.1.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.1.0.tgz" }, "iconv-lite": { "version": "0.2.11", - "from": "iconv-lite@~0.2.11" + "from": "iconv-lite@0.2.11", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz" }, "rimraf": { "version": "2.2.8", - "from": "rimraf@~2.2.2" + "from": "rimraf@2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" }, "glob": { "version": "3.2.11", - "from": "glob@~3.2.6", + "from": "glob@3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", "dependencies": { "inherits": { "version": "2.0.1", - "from": "inherits@2" + "from": "inherits@2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "minimatch": { "version": "0.3.0", - "from": "minimatch@0.3", + "from": "minimatch@0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", "dependencies": { "lru-cache": { "version": "2.5.0", - "from": "lru-cache@2" + "from": "lru-cache@2.5.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" }, "sigmund": { "version": "1.0.0", - "from": "sigmund@~1.0.0" + "from": "sigmund@1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" } } } @@ -156,58 +187,65 @@ }, "minimatch": { "version": "0.2.14", - "from": "minimatch@~0.2.12", + "from": "minimatch@0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "dependencies": { "lru-cache": { "version": "2.5.0", - "from": "lru-cache@2" + "from": "lru-cache@2.5.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" }, "sigmund": { "version": "1.0.0", - "from": "sigmund@~1.0.0" + "from": "sigmund@1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" } } }, "findup-sync": { "version": "0.1.3", - "from": "findup-sync@~0.1.2", + "from": "findup-sync@0.1.3", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", "dependencies": { "lodash": { "version": "2.4.1", - "from": "lodash@~2.4.1" + "from": "lodash@2.4.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz" } } }, "isbinaryfile": { "version": "0.1.9", - "from": "isbinaryfile@~0.1.9" + "from": "isbinaryfile@0.1.9", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-0.1.9.tgz" } } }, "lodash": { "version": "2.4.1", - "from": "lodash@~2.4.1" + "from": "lodash@2.4.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz" } } }, "colors": { "version": "0.6.2", - "from": "colors@0.6.2", + "from": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz" }, "connect": { "version": "3.1.1", - "from": "connect@3.1.1", + "from": "https://registry.npmjs.org/connect/-/connect-3.1.1.tgz", "resolved": "https://registry.npmjs.org/connect/-/connect-3.1.1.tgz", "dependencies": { "debug": { "version": "1.0.4", - "from": "debug@1.0.4", + "from": "https://registry.npmjs.org/debug/-/debug-1.0.4.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-1.0.4.tgz", "dependencies": { "ms": { "version": "0.6.2", - "from": "ms@0.6.2", + "from": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz" } } @@ -215,127 +253,626 @@ "finalhandler": { "version": "0.1.0", "from": "finalhandler@0.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.1.0.tgz", "dependencies": { "escape-html": { "version": "1.0.1", - "from": "escape-html@1.0.1" + "from": "escape-html@1.0.1", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.1.tgz" } } }, "parseurl": { "version": "1.3.0", - "from": "parseurl@~1.3.0" + "from": "parseurl@1.3.0", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.0.tgz" }, "utils-merge": { "version": "1.0.0", - "from": "utils-merge@1.0.0", + "from": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz" } } }, "connect-livereload": { "version": "0.4.0", - "from": "connect-livereload@0.4.0" + "from": "connect-livereload@0.4.0", + "resolved": "https://registry.npmjs.org/connect-livereload/-/connect-livereload-0.4.0.tgz" }, "cross-spawn": { "version": "0.2.3", - "from": "cross-spawn@0.2.3", + "from": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-0.2.3.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-0.2.3.tgz", "dependencies": { "lru-cache": { "version": "2.5.0", - "from": "lru-cache@^2.5.0" + "from": "lru-cache@2.5.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" } } }, "event-stream": { "version": "3.0.20", - "from": "event-stream@3.0.x", + "from": "event-stream@3.0.20", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.0.20.tgz", "dependencies": { "through": { "version": "2.3.6", - "from": "through@~2.3.1" + "from": "through@2.3.6", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.6.tgz" }, "duplexer": { "version": "0.1.1", - "from": "duplexer@~0.1.1" + "from": "duplexer@0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz" }, "from": { "version": "0.1.3", - "from": "from@~0" + "from": "from@0.1.3", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.3.tgz" }, "map-stream": { "version": "0.0.4", - "from": "map-stream@~0.0.3" + "from": "map-stream@0.0.4", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.4.tgz" }, "pause-stream": { "version": "0.0.11", - "from": "pause-stream@0.0.11" + "from": "pause-stream@0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz" }, "split": { "version": "0.2.10", - "from": "split@0.2" + "from": "split@0.2.10", + "resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz" }, "stream-combiner": { "version": "0.0.4", - "from": "stream-combiner@~0.0.3" + "from": "stream-combiner@0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz" } } }, "finalhandler": { "version": "0.2.0", - "from": "finalhandler@0.2.0", + "from": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.2.0.tgz", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.2.0.tgz", "dependencies": { "debug": { "version": "2.0.0", - "from": "debug@~2.0.0", + "from": "debug@2.0.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.0.0.tgz", "dependencies": { "ms": { "version": "0.6.2", - "from": "ms@0.6.2", + "from": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz" } } }, "escape-html": { "version": "1.0.1", - "from": "escape-html@1.0.1" + "from": "escape-html@1.0.1", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.1.tgz" } } }, "form-data": { "version": "0.1.4", - "from": "form-data@0.1.4", + "from": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", "dependencies": { "combined-stream": { "version": "0.0.5", - "from": "combined-stream@~0.0.4", + "from": "combined-stream@0.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.5.tgz", "dependencies": { "delayed-stream": { "version": "0.0.5", - "from": "delayed-stream@0.0.5" + "from": "delayed-stream@0.0.5", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz" } } }, "mime": { "version": "1.2.11", - "from": "mime@~1.2.9" + "from": "mime@1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" }, "async": { "version": "0.9.0", - "from": "async@~0.9.0" + "from": "async@0.9.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz" + } + } + }, + "gulp": { + "version": "3.8.10", + "from": "gulp@", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-3.8.10.tgz", + "dependencies": { + "archy": { + "version": "1.0.0", + "from": "archy@^1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz" + }, + "chalk": { + "version": "0.5.1", + "from": "chalk@^0.5.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "dependencies": { + "ansi-styles": { + "version": "1.1.0", + "from": "ansi-styles@^1.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" + }, + "escape-string-regexp": { + "version": "1.0.2", + "from": "escape-string-regexp@^1.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz" + }, + "has-ansi": { + "version": "0.1.0", + "from": "has-ansi@^0.1.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "dependencies": { + "ansi-regex": { + "version": "0.2.1", + "from": "ansi-regex@^0.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" + } + } + }, + "strip-ansi": { + "version": "0.3.0", + "from": "strip-ansi@^0.3.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "dependencies": { + "ansi-regex": { + "version": "0.2.1", + "from": "ansi-regex@^0.2.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" + } + } + }, + "supports-color": { + "version": "0.2.0", + "from": "supports-color@^0.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" + } + } + }, + "deprecated": { + "version": "0.0.1", + "from": "deprecated@^0.0.1", + "resolved": "https://registry.npmjs.org/deprecated/-/deprecated-0.0.1.tgz" + }, + "gulp-util": { + "version": "3.0.1", + "from": "gulp-util@^3.0.0", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.1.tgz", + "dependencies": { + "dateformat": { + "version": "1.0.11", + "from": "dateformat@^1.0.7-1.2.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.11.tgz", + "dependencies": { + "get-stdin": { + "version": "3.0.2", + "from": "get-stdin@*", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-3.0.2.tgz" + }, + "meow": { + "version": "2.0.0", + "from": "meow@*", + "resolved": "https://registry.npmjs.org/meow/-/meow-2.0.0.tgz", + "dependencies": { + "camelcase-keys": { + "version": "1.0.0", + "from": "camelcase-keys@^1.0.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-1.0.0.tgz", + "dependencies": { + "camelcase": { + "version": "1.0.2", + "from": "camelcase@^1.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.0.2.tgz" + }, + "map-obj": { + "version": "1.0.0", + "from": "map-obj@^1.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.0.tgz" + } + } + }, + "indent-string": { + "version": "1.2.0", + "from": "indent-string@^1.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.0.tgz", + "dependencies": { + "repeating": { + "version": "1.1.0", + "from": "repeating@^1.1.0", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.0.tgz", + "dependencies": { + "is-finite": { + "version": "1.0.0", + "from": "is-finite@^1.0.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.0.tgz" + }, + "meow": { + "version": "1.0.0", + "from": "meow@^1.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-1.0.0.tgz" + } + } + } + } + }, + "object-assign": { + "version": "1.0.0", + "from": "object-assign@^1.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-1.0.0.tgz" + } + } + } + } + }, + "lodash": { + "version": "2.4.1", + "from": "lodash@^2.4.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz" + }, + "lodash._reinterpolate": { + "version": "2.4.1", + "from": "lodash._reinterpolate@^2.4.1", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-2.4.1.tgz" + }, + "lodash.template": { + "version": "2.4.1", + "from": "lodash.template@^2.4.1", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-2.4.1.tgz", + "dependencies": { + "lodash.defaults": { + "version": "2.4.1", + "from": "lodash.defaults@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz", + "dependencies": { + "lodash._objecttypes": { + "version": "2.4.1", + "from": "lodash._objecttypes@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz" + } + } + }, + "lodash.escape": { + "version": "2.4.1", + "from": "lodash.escape@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-2.4.1.tgz", + "dependencies": { + "lodash._escapehtmlchar": { + "version": "2.4.1", + "from": "lodash._escapehtmlchar@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.4.1.tgz", + "dependencies": { + "lodash._htmlescapes": { + "version": "2.4.1", + "from": "lodash._htmlescapes@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash._htmlescapes/-/lodash._htmlescapes-2.4.1.tgz" + } + } + }, + "lodash._reunescapedhtml": { + "version": "2.4.1", + "from": "lodash._reunescapedhtml@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash._reunescapedhtml/-/lodash._reunescapedhtml-2.4.1.tgz", + "dependencies": { + "lodash._htmlescapes": { + "version": "2.4.1", + "from": "lodash._htmlescapes@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash._htmlescapes/-/lodash._htmlescapes-2.4.1.tgz" + } + } + } + } + }, + "lodash._escapestringchar": { + "version": "2.4.1", + "from": "lodash._escapestringchar@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash._escapestringchar/-/lodash._escapestringchar-2.4.1.tgz" + }, + "lodash.keys": { + "version": "2.4.1", + "from": "lodash.keys@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "dependencies": { + "lodash._isnative": { + "version": "2.4.1", + "from": "lodash._isnative@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz" + }, + "lodash.isobject": { + "version": "2.4.1", + "from": "lodash.isobject@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", + "dependencies": { + "lodash._objecttypes": { + "version": "2.4.1", + "from": "lodash._objecttypes@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz" + } + } + }, + "lodash._shimkeys": { + "version": "2.4.1", + "from": "lodash._shimkeys@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz", + "dependencies": { + "lodash._objecttypes": { + "version": "2.4.1", + "from": "lodash._objecttypes@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz" + } + } + } + } + }, + "lodash.templatesettings": { + "version": "2.4.1", + "from": "lodash.templatesettings@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-2.4.1.tgz" + }, + "lodash.values": { + "version": "2.4.1", + "from": "lodash.values@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-2.4.1.tgz" + } + } + }, + "multipipe": { + "version": "0.1.2", + "from": "multipipe@^0.1.0", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "dependencies": { + "duplexer2": { + "version": "0.0.2", + "from": "duplexer2@0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "dependencies": { + "readable-stream": { + "version": "1.1.13", + "from": "readable-stream@~1.1.9", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@~1.0.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@~0.10.x", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@~2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + } + } + } + } + }, + "through2": { + "version": "0.6.3", + "from": "through2@^0.6.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.3.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@>=1.0.33-1 <1.1.0-0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@~1.0.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@~0.10.x", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@~2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <4.1.0-0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + }, + "vinyl": { + "version": "0.4.6", + "from": "vinyl@^0.4.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "dependencies": { + "clone": { + "version": "0.2.0", + "from": "clone@^0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz" + }, + "clone-stats": { + "version": "0.0.1", + "from": "clone-stats@^0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" + } + } + } + } + }, + "interpret": { + "version": "0.3.8", + "from": "interpret@^0.3.2", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-0.3.8.tgz" + }, + "liftoff": { + "version": "0.13.6", + "from": "liftoff@^0.13.2", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-0.13.6.tgz", + "dependencies": { + "findup-sync": { + "version": "0.1.3", + "from": "findup-sync@~0.1.2", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", + "dependencies": { + "glob": { + "version": "3.2.11", + "from": "glob@~3.2.9", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "from": "inherits@2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "0.3.0", + "from": "minimatch@0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.5.0", + "from": "lru-cache@2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" + }, + "sigmund": { + "version": "1.0.0", + "from": "sigmund@~1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + } + } + }, + "lodash": { + "version": "2.4.1", + "from": "lodash@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz" + } + } + }, + "resolve": { + "version": "1.0.0", + "from": "resolve@~1.0.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.0.0.tgz" + }, + "extend": { + "version": "1.3.0", + "from": "extend@~1.3.0", + "resolved": "https://registry.npmjs.org/extend/-/extend-1.3.0.tgz" + }, + "flagged-respawn": { + "version": "0.3.1", + "from": "flagged-respawn@~0.3.0", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-0.3.1.tgz" + } + } + }, + "minimist": { + "version": "1.1.0", + "from": "minimist@^1.1.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.1.0.tgz" + }, + "orchestrator": { + "version": "0.3.7", + "from": "orchestrator@^0.3.0", + "resolved": "https://registry.npmjs.org/orchestrator/-/orchestrator-0.3.7.tgz", + "dependencies": { + "end-of-stream": { + "version": "0.1.5", + "from": "end-of-stream@~0.1.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz", + "dependencies": { + "once": { + "version": "1.3.1", + "from": "once@~1.3.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.1.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@1", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "sequencify": { + "version": "0.0.7", + "from": "sequencify@~0.0.7", + "resolved": "https://registry.npmjs.org/sequencify/-/sequencify-0.0.7.tgz" + }, + "stream-consume": { + "version": "0.1.0", + "from": "stream-consume@~0.1.0", + "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz" + } + } + }, + "pretty-hrtime": { + "version": "0.2.2", + "from": "pretty-hrtime@^0.2.0", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-0.2.2.tgz" + }, + "semver": { + "version": "4.1.0", + "from": "semver@^4.1.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.1.0.tgz" + }, + "tildify": { + "version": "1.0.0", + "from": "tildify@^1.0.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-1.0.0.tgz", + "dependencies": { + "user-home": { + "version": "1.1.0", + "from": "user-home@^1.0.0", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.0.tgz" + } + } + }, + "v8flags": { + "version": "1.0.5", + "from": "v8flags@^1.0.1", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-1.0.5.tgz" } } }, "ncp": { "version": "0.4.2", - "from": "ncp@0.4.2" + "from": "ncp@0.4.2", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz" }, "npm": { "version": "2.1.3", - "from": "npm@2.1.3", + "from": "https://registry.npmjs.org/npm/-/npm-2.1.3.tgz", "resolved": "https://registry.npmjs.org/npm/-/npm-2.1.3.tgz", "dependencies": { "abbrev": { @@ -899,315 +1436,369 @@ }, "open": { "version": "0.0.5", - "from": "open@0.0.5", + "from": "https://registry.npmjs.org/open/-/open-0.0.5.tgz", "resolved": "https://registry.npmjs.org/open/-/open-0.0.5.tgz" }, "optimist": { "version": "0.6.0", - "from": "optimist@0.6.0", + "from": "https://registry.npmjs.org/optimist/-/optimist-0.6.0.tgz", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.0.tgz", "dependencies": { "wordwrap": { "version": "0.0.2", - "from": "wordwrap@~0.0.2" + "from": "wordwrap@0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz" }, "minimist": { "version": "0.0.10", - "from": "minimist@~0.0.1" + "from": "minimist@0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz" } } }, "progress": { "version": "1.1.7", - "from": "progress@1.1.7" + "from": "progress@1.1.7", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.7.tgz" }, "prompt": { "version": "0.2.12", - "from": "prompt@0.2.12", + "from": "https://registry.npmjs.org/prompt/-/prompt-0.2.12.tgz", "resolved": "https://registry.npmjs.org/prompt/-/prompt-0.2.12.tgz", "dependencies": { "pkginfo": { "version": "0.3.0", - "from": "pkginfo@0.x.x" + "from": "pkginfo@0.3.0", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.0.tgz" }, "read": { "version": "1.0.5", - "from": "read@1.0.x", + "from": "read@1.0.5", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.5.tgz", "dependencies": { "mute-stream": { "version": "0.0.4", - "from": "mute-stream@~0.0.4" + "from": "mute-stream@0.0.4", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.4.tgz" } } }, "revalidator": { "version": "0.1.8", - "from": "revalidator@0.1.x" + "from": "revalidator@0.1.8", + "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz" }, "utile": { "version": "0.2.1", - "from": "utile@0.2.x", + "from": "utile@0.2.1", + "resolved": "https://registry.npmjs.org/utile/-/utile-0.2.1.tgz", "dependencies": { "async": { "version": "0.2.10", - "from": "async@~0.2.9" + "from": "async@0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" }, "deep-equal": { "version": "0.2.1", - "from": "deep-equal@*" + "from": "deep-equal@0.2.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.2.1.tgz" }, "i": { "version": "0.3.2", - "from": "i@0.3.x" + "from": "i@0.3.2", + "resolved": "https://registry.npmjs.org/i/-/i-0.3.2.tgz" }, "mkdirp": { "version": "0.5.0", - "from": "mkdirp@0.x.x", + "from": "mkdirp@0.5.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", "dependencies": { "minimist": { "version": "0.0.8", - "from": "minimist@0.0.8", + "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } }, "rimraf": { "version": "2.2.8", - "from": "rimraf@2.x.x" + "from": "rimraf@2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" } } }, "winston": { "version": "0.6.2", - "from": "winston@0.6.x", + "from": "winston@0.6.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-0.6.2.tgz", "dependencies": { "async": { "version": "0.1.22", - "from": "async@0.1.x" + "from": "async@0.1.22", + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz" }, "cycle": { "version": "1.0.3", - "from": "cycle@1.0.x" + "from": "cycle@1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz" }, "eyes": { "version": "0.1.8", - "from": "eyes@0.1.x" + "from": "eyes@0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz" }, "pkginfo": { "version": "0.2.3", - "from": "pkginfo@0.2.x" + "from": "pkginfo@0.2.3", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.2.3.tgz" }, "request": { "version": "2.9.203", - "from": "request@2.9.x" + "from": "request@2.9.203", + "resolved": "https://registry.npmjs.org/request/-/request-2.9.203.tgz" }, "stack-trace": { "version": "0.0.9", - "from": "stack-trace@0.0.x" + "from": "stack-trace@0.0.9", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" } } } } }, + "proxy-middleware": { + "version": "0.7.0", + "from": "proxy-middleware@0.7.0", + "resolved": "https://registry.npmjs.org/proxy-middleware/-/proxy-middleware-0.7.0.tgz" + }, "q": { "version": "1.0.1", - "from": "q@1.0.1", + "from": "https://registry.npmjs.org/q/-/q-1.0.1.tgz", "resolved": "https://registry.npmjs.org/q/-/q-1.0.1.tgz" }, "request": { "version": "2.27.0", - "from": "request@2.27.0", + "from": "https://registry.npmjs.org/request/-/request-2.27.0.tgz", "resolved": "https://registry.npmjs.org/request/-/request-2.27.0.tgz", "dependencies": { "qs": { "version": "0.6.6", - "from": "qs@~0.6.0" + "from": "qs@0.6.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz" }, "json-stringify-safe": { "version": "5.0.0", - "from": "json-stringify-safe@~5.0.0" + "from": "json-stringify-safe@5.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.0.tgz" }, "forever-agent": { "version": "0.5.2", - "from": "forever-agent@~0.5.0" + "from": "forever-agent@0.5.2", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" }, "tunnel-agent": { "version": "0.3.0", - "from": "tunnel-agent@~0.3.0" + "from": "tunnel-agent@0.3.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz" }, "http-signature": { "version": "0.10.0", - "from": "http-signature@~0.10.0", + "from": "http-signature@0.10.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.0.tgz", "dependencies": { "assert-plus": { "version": "0.1.2", - "from": "assert-plus@0.1.2", + "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.2.tgz", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.2.tgz" }, "asn1": { "version": "0.1.11", - "from": "asn1@0.1.11", + "from": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz" }, "ctype": { "version": "0.5.2", - "from": "ctype@0.5.2" + "from": "ctype@0.5.2", + "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.2.tgz" } } }, "hawk": { "version": "1.0.0", - "from": "hawk@~1.0.0", + "from": "hawk@1.0.0", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", "dependencies": { "hoek": { "version": "0.9.1", - "from": "hoek@0.9.x" + "from": "hoek@0.9.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz" }, "boom": { "version": "0.4.2", - "from": "boom@0.4.x" + "from": "boom@0.4.2", + "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz" }, "cryptiles": { "version": "0.2.2", - "from": "cryptiles@0.2.x" + "from": "cryptiles@0.2.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz" }, "sntp": { "version": "0.2.4", - "from": "sntp@0.2.x" + "from": "sntp@0.2.4", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz" } } }, "aws-sign": { "version": "0.3.0", - "from": "aws-sign@~0.3.0" + "from": "aws-sign@0.3.0", + "resolved": "https://registry.npmjs.org/aws-sign/-/aws-sign-0.3.0.tgz" }, "oauth-sign": { "version": "0.3.0", - "from": "oauth-sign@~0.3.0" + "from": "oauth-sign@0.3.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz" }, "cookie-jar": { "version": "0.3.0", - "from": "cookie-jar@~0.3.0" + "from": "cookie-jar@0.3.0", + "resolved": "https://registry.npmjs.org/cookie-jar/-/cookie-jar-0.3.0.tgz" }, "node-uuid": { "version": "1.4.1", - "from": "node-uuid@~1.4.0" + "from": "node-uuid@1.4.1", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.1.tgz" }, "mime": { "version": "1.2.11", - "from": "mime@1.2.11" + "from": "mime@1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" } } }, "serve-static": { "version": "1.6.1", - "from": "serve-static@1.6.1", + "from": "https://registry.npmjs.org/serve-static/-/serve-static-1.6.1.tgz", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.6.1.tgz", "dependencies": { "escape-html": { "version": "1.0.1", - "from": "escape-html@1.0.1" + "from": "escape-html@1.0.1", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.1.tgz" }, "parseurl": { "version": "1.3.0", - "from": "parseurl@~1.3.0" + "from": "parseurl@1.3.0", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.0.tgz" }, "send": { "version": "0.9.1", - "from": "send@0.9.1", + "from": "https://registry.npmjs.org/send/-/send-0.9.1.tgz", "resolved": "https://registry.npmjs.org/send/-/send-0.9.1.tgz", "dependencies": { "debug": { "version": "2.0.0", - "from": "debug@~2.0.0" + "from": "debug@2.0.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.0.0.tgz" }, "depd": { "version": "0.4.4", - "from": "depd@0.4.4" + "from": "depd@0.4.4", + "resolved": "https://registry.npmjs.org/depd/-/depd-0.4.4.tgz" }, "destroy": { "version": "1.0.3", - "from": "destroy@1.0.3" + "from": "destroy@1.0.3", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.3.tgz" }, "etag": { "version": "1.3.1", - "from": "etag@~1.3.0", + "from": "https://registry.npmjs.org/etag/-/etag-1.3.1.tgz", "resolved": "https://registry.npmjs.org/etag/-/etag-1.3.1.tgz", "dependencies": { "crc": { "version": "3.0.0", - "from": "crc@3.0.0", + "from": "https://registry.npmjs.org/crc/-/crc-3.0.0.tgz", "resolved": "https://registry.npmjs.org/crc/-/crc-3.0.0.tgz" } } }, "fresh": { "version": "0.2.4", - "from": "fresh@0.2.4", + "from": "https://registry.npmjs.org/fresh/-/fresh-0.2.4.tgz", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.4.tgz" }, "mime": { "version": "1.2.11", - "from": "mime@1.2.11" + "from": "mime@1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" }, "ms": { "version": "0.6.2", - "from": "ms@0.6.2", + "from": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz" }, "on-finished": { "version": "2.1.0", "from": "on-finished@2.1.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.1.0.tgz", "dependencies": { "ee-first": { "version": "1.0.5", - "from": "ee-first@1.0.5" + "from": "ee-first@1.0.5", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.0.5.tgz" } } }, "range-parser": { "version": "1.0.2", - "from": "range-parser@~1.0.0", + "from": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.2.tgz", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.2.tgz" } } }, "utils-merge": { "version": "1.0.0", - "from": "utils-merge@1.0.0", + "from": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz" } } }, "shelljs": { "version": "0.2.6", - "from": "shelljs@0.2.6", + "from": "https://registry.npmjs.org/shelljs/-/shelljs-0.2.6.tgz", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.2.6.tgz" }, "tiny-lr-fork": { "version": "0.0.5", - "from": "tiny-lr-fork@0.0.5", + "from": "https://registry.npmjs.org/tiny-lr-fork/-/tiny-lr-fork-0.0.5.tgz", "resolved": "https://registry.npmjs.org/tiny-lr-fork/-/tiny-lr-fork-0.0.5.tgz", "dependencies": { "qs": { "version": "0.5.6", - "from": "qs@~0.5.2" + "from": "qs@0.5.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz" }, "faye-websocket": { "version": "0.4.4", - "from": "faye-websocket@~0.4.3" + "from": "faye-websocket@0.4.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz" }, "noptify": { "version": "0.0.3", - "from": "noptify@~0.0.3", + "from": "noptify@0.0.3", + "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz", "dependencies": { "nopt": { "version": "2.0.0", - "from": "nopt@~2.0.0", + "from": "nopt@2.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz", "dependencies": { "abbrev": { "version": "1.0.5", - "from": "abbrev@1" + "from": "abbrev@1.0.5", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz" } } } @@ -1215,115 +1806,132 @@ }, "debug": { "version": "0.7.4", - "from": "debug@~0.7.0" + "from": "debug@0.7.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz" } } }, "unzip": { "version": "0.1.9", - "from": "unzip@0.1.9", + "from": "https://registry.npmjs.org/unzip/-/unzip-0.1.9.tgz", "resolved": "https://registry.npmjs.org/unzip/-/unzip-0.1.9.tgz", "dependencies": { "fstream": { "version": "0.1.31", - "from": "fstream@~0.1.21", + "from": "fstream@0.1.31", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-0.1.31.tgz", "dependencies": { "graceful-fs": { "version": "3.0.4", - "from": "graceful-fs@~3.0.2", + "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.4.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.4.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@~2.0.0" + "from": "inherits@2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "mkdirp": { "version": "0.5.0", - "from": "mkdirp@0.5", + "from": "mkdirp@0.5.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", "dependencies": { "minimist": { "version": "0.0.8", - "from": "minimist@0.0.8", + "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } }, "rimraf": { "version": "2.2.8", - "from": "rimraf@2" + "from": "rimraf@2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" } } }, "pullstream": { "version": "0.4.1", - "from": "pullstream@~0.4.0", + "from": "https://registry.npmjs.org/pullstream/-/pullstream-0.4.1.tgz", "resolved": "https://registry.npmjs.org/pullstream/-/pullstream-0.4.1.tgz", "dependencies": { "over": { "version": "0.0.5", - "from": "over@>= 0.0.5 < 1" + "from": "over@0.0.5", + "resolved": "https://registry.npmjs.org/over/-/over-0.0.5.tgz" }, "slice-stream": { "version": "1.0.0", - "from": "slice-stream@>= 1.0.0 < 2", + "from": "https://registry.npmjs.org/slice-stream/-/slice-stream-1.0.0.tgz", "resolved": "https://registry.npmjs.org/slice-stream/-/slice-stream-1.0.0.tgz" } } }, "binary": { "version": "0.3.0", - "from": "binary@~0.3.0", + "from": "binary@0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", "dependencies": { "chainsaw": { "version": "0.1.0", - "from": "chainsaw@~0.1.0", + "from": "chainsaw@0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", "dependencies": { "traverse": { "version": "0.3.9", - "from": "traverse@>=0.3.0 <0.4" + "from": "traverse@0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz" } } }, "buffers": { "version": "0.1.1", - "from": "buffers@~0.1.1" + "from": "buffers@0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz" } } }, "readable-stream": { "version": "1.0.33-1", - "from": "readable-stream@>=1.0.33-1 <1.1.0-0", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", "dependencies": { "core-util-is": { "version": "1.0.1", - "from": "core-util-is@~1.0.0" + "from": "core-util-is@1.0.1", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { "version": "0.0.1", - "from": "isarray@0.0.1" + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@~0.10.x" + "from": "string_decoder@0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@~2.0.1" + "from": "inherits@2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } }, "setimmediate": { "version": "1.0.2", - "from": "setimmediate@~1.0.1" + "from": "setimmediate@1.0.2", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.2.tgz" }, "match-stream": { "version": "0.0.2", - "from": "match-stream@~0.0.2", + "from": "match-stream@0.0.2", + "resolved": "https://registry.npmjs.org/match-stream/-/match-stream-0.0.2.tgz", "dependencies": { "buffers": { "version": "0.1.1", - "from": "buffers@~0.1.1" + "from": "buffers@0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz" } } } @@ -1331,28 +1939,33 @@ }, "vinyl-fs": { "version": "0.3.7", - "from": "vinyl-fs@0.3.7", + "from": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-0.3.7.tgz", "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-0.3.7.tgz", "dependencies": { "glob-stream": { "version": "3.1.15", - "from": "glob-stream@^3.1.5", + "from": "glob-stream@3.1.15", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.15.tgz", "dependencies": { "glob": { "version": "4.0.6", - "from": "glob@^4.0.0", + "from": "glob@4.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.0.6.tgz", "dependencies": { "inherits": { "version": "2.0.1", - "from": "inherits@2" + "from": "inherits@2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "once": { "version": "1.3.1", - "from": "once@^1.3.0", + "from": "once@1.3.1", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.1.tgz", "dependencies": { "wrappy": { "version": "1.0.1", - "from": "wrappy@1" + "from": "wrappy@1.0.1", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" } } } @@ -1360,73 +1973,89 @@ }, "minimatch": { "version": "1.0.0", - "from": "minimatch@^1.0.0", + "from": "minimatch@1.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz", "dependencies": { "lru-cache": { "version": "2.5.0", - "from": "lru-cache@2" + "from": "lru-cache@2.5.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" }, "sigmund": { "version": "1.0.0", - "from": "sigmund@~1.0.0" + "from": "sigmund@1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" } } }, "ordered-read-streams": { "version": "0.0.8", - "from": "ordered-read-streams@0.0.8" + "from": "ordered-read-streams@0.0.8", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.0.8.tgz" }, "glob2base": { "version": "0.0.11", - "from": "glob2base@^0.0.11" + "from": "glob2base@0.0.11", + "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.11.tgz" }, "unique-stream": { "version": "1.0.0", - "from": "unique-stream@^1.0.0" + "from": "unique-stream@1.0.0", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-1.0.0.tgz" } } }, "glob-watcher": { "version": "0.0.6", - "from": "glob-watcher@^0.0.6", + "from": "glob-watcher@0.0.6", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.6.tgz", "dependencies": { "gaze": { "version": "0.5.1", - "from": "gaze@^0.5.1", + "from": "gaze@0.5.1", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.1.tgz", "dependencies": { "globule": { "version": "0.1.0", - "from": "globule@~0.1.0", + "from": "globule@0.1.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", "dependencies": { "lodash": { "version": "1.0.1", - "from": "lodash@~1.0.1" + "from": "lodash@1.0.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.1.tgz" }, "glob": { "version": "3.1.21", - "from": "glob@~3.1.21", + "from": "glob@3.1.21", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", "dependencies": { "graceful-fs": { "version": "1.2.3", - "from": "graceful-fs@~1.2.0" + "from": "graceful-fs@1.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" }, "inherits": { "version": "1.0.0", - "from": "inherits@1" + "from": "inherits@1.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz" } } }, "minimatch": { "version": "0.2.14", - "from": "minimatch@~0.2.11", + "from": "minimatch@0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "dependencies": { "lru-cache": { "version": "2.5.0", - "from": "lru-cache@2" + "from": "lru-cache@2.5.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" }, "sigmund": { "version": "1.0.0", - "from": "sigmund@~1.0.0" + "from": "sigmund@1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" } } } @@ -1438,79 +2067,91 @@ }, "graceful-fs": { "version": "3.0.4", - "from": "graceful-fs@^3.0.0", + "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.4.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.4.tgz" }, "lodash": { "version": "2.4.1", - "from": "lodash@~2.4.1" + "from": "lodash@2.4.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz" }, "mkdirp": { "version": "0.5.0", - "from": "mkdirp@0.x.x", + "from": "mkdirp@0.5.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", "dependencies": { "minimist": { "version": "0.0.8", - "from": "minimist@0.0.8", + "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } }, "strip-bom": { "version": "1.0.0", - "from": "strip-bom@^1.0.0", + "from": "strip-bom@1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz", "dependencies": { "first-chunk-stream": { "version": "1.0.0", - "from": "first-chunk-stream@^1.0.0" + "from": "first-chunk-stream@1.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz" }, "is-utf8": { "version": "0.2.0", - "from": "is-utf8@^0.2.0" + "from": "is-utf8@0.2.0", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.0.tgz" } } }, "through2": { "version": "0.6.3", - "from": "through2@^0.6.1", + "from": "https://registry.npmjs.org/through2/-/through2-0.6.3.tgz", "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.3.tgz", "dependencies": { "readable-stream": { "version": "1.0.33-1", - "from": "readable-stream@>=1.0.33-1 <1.1.0-0", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", "dependencies": { "core-util-is": { "version": "1.0.1", - "from": "core-util-is@~1.0.0" + "from": "core-util-is@1.0.1", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { "version": "0.0.1", - "from": "isarray@0.0.1" + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@~0.10.x" + "from": "string_decoder@0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@~2.0.1" + "from": "inherits@2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } }, "xtend": { "version": "4.0.0", - "from": "xtend@>=4.0.0 <4.1.0-0" + "from": "xtend@4.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" } } }, "vinyl": { "version": "0.4.3", - "from": "vinyl@^0.4.0", + "from": "vinyl@0.4.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.3.tgz", "dependencies": { "clone-stats": { "version": "0.0.1", - "from": "clone-stats@^0.0.1" + "from": "clone-stats@0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" } } } @@ -1519,19 +2160,22 @@ "xml2js": { "version": "0.4.4", "from": "xml2js@0.4.4", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.4.tgz", "dependencies": { "sax": { "version": "0.6.1", - "from": "sax@0.6.x", + "from": "https://registry.npmjs.org/sax/-/sax-0.6.1.tgz", "resolved": "https://registry.npmjs.org/sax/-/sax-0.6.1.tgz" }, "xmlbuilder": { "version": "2.4.4", - "from": "xmlbuilder@>=1.0.0", + "from": "xmlbuilder@2.4.4", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.4.4.tgz", "dependencies": { "lodash-node": { "version": "2.4.1", - "from": "lodash-node@~2.4.1" + "from": "lodash-node@2.4.1", + "resolved": "https://registry.npmjs.org/lodash-node/-/lodash-node-2.4.1.tgz" } } } From 3d077f76860f009eb467df0c34c47b5b0b52bad4 Mon Sep 17 00:00:00 2001 From: jbavari Date: Mon, 8 Dec 2014 16:53:49 -0700 Subject: [PATCH 0301/1100] Making the document easier to understand for proxying. Updated the options to be proxyUrl, as well as updating that value in serve.js --- README.md | 33 +++++++++++++++++++++++++++------ lib/ionic/serve.js | 4 ++-- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7108381aff..403ed5a361 100644 --- a/README.md +++ b/README.md @@ -61,11 +61,12 @@ $ ionic serve [options] __Proxies:__ -The `serve` command can add some proxies to the http server. This proxies are useful if your are developing in the browser and you need to make calls to an external API. With this feature you can proxy request to the external api through the ionic http server preventing the `No 'Access-Control-Allow-Origin' header is present on the requested resource` error. +The `serve` command can add some proxies to the http server. These proxies are useful if you are developing in the browser and you need to make calls to an external API. With this feature you can proxy request to the external api through the ionic http server preventing the `No 'Access-Control-Allow-Origin' header is present on the requested resource` error. In the `ionic.project` file you can add a property with an array of proxies you want to add. The proxies are object with two properties: -Say you want to intercept all calls to your server `http://localhost:8100/api` to go instead to `http://my.server.com/api` - use the following configuration: +* `path`: string that will be matched against the beginning of the incoming request URL. +* `proxyUrl`: a string with the url of where the proxied request should go. ```json { @@ -74,17 +75,37 @@ Say you want to intercept all calls to your server `http://localhost:8100/api` t "app_id": "", "proxies": [ { - "path": "/api", - "options": "http://my.server.com/api" + "path": "/v1", + "proxyUrl": "https://api.instagram.com/v1" } ] } ``` -* `path`: string that will be matched against after the host in the request URL (for example, the `/api` in `http://localhost:8100/api`) -* `options`: a string of the address to proxy to. Allows any options that are permitted on the [http](http://nodejs.org/api/http.html#http_http_request_options_callback) request options. +Using the above configuration, you can now make requests to your local server at `http://localhost:8100/v1` to have it proxy out requests to `https://api.instagram.com/v1` +For example: + +```js +angular.module('starter.controllers', []) +.constant('InstagramApiUrl', '') +// .contant('InstagramApiUrl','https://api.instagram.com') +//In production, make this the real URL + +.controller('FeedCtrl', function($scope, $http, InstagramApiUrl) { + + $scope.feed = null; + + $http.get(InstagramApiUrl + '/v1/media/search?client_id=1&lat=48&lng=2.294351').then(function(data) { + console.log('data ' , data) + $scope.feed = data; + }) + +}) +``` + +See also [this gist](https://gist.github.com/jbavari/d9c1c94058c4fdd4e935) for more help. __Command-line flags/options:__ diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 0d0095c5a9..73edccef4a 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -186,8 +186,8 @@ IonicTask.prototype.start = function(ionic) { for(var x=0; x " + url.format(proxy.options)); + app.use(proxy.path, proxyMiddleware(url.parse(proxy.proxyUrl))); + console.log('Proxy added:'.green.bold, proxy.path + " => " + url.format(proxy.proxyUrl)); } } From 03855c011a8a57d1caa4685cdc30a280a0a5742c Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 8 Dec 2014 18:09:40 -0600 Subject: [PATCH 0302/1100] Fallback platform override --- lib/ionic/serve.js | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 0d0095c5a9..d64824bf6b 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -12,7 +12,7 @@ var fs = require('fs'), IonicProject = require('./project'), Task = require('./task').Task, proxyMiddleware = require('proxy-middleware'), - url = require('url'); + url = require('url'), IonicStats = require('./stats').IonicStats; var DEFAULT_HTTP_PORT = 8100; @@ -201,6 +201,9 @@ IonicTask.prototype.start = function(ionic) { var server = http.createServer(function(req, res){ var done = finalhandler(req, res); + var urlParsed = url.parse(req.url, true); + var platformOverride = urlParsed.query.ionicplatform; + var platformUrl = getPlatformUrl(req); if(platformUrl) { var platformWWW = getPlatformWWW(req); @@ -260,6 +263,10 @@ IonicTask.prototype.start = function(ionic) { html = injectConsoleLogScript(html); } + if(platformOverride) { + html = injectPlatformScript( html, platformOverride ); + } + res.end(html); } }); @@ -494,6 +501,24 @@ function injectGoToScript(html) { return html; } +/** + * Inject the platform override for choosing Android or iOS during + * development. + */ +function injectPlatformScript(html, platformOverride) { + try { + var findTags = html.toLowerCase().indexOf(''); + if(findTags < 0) { return html; } + + return html.slice(0, findTags) + '\n' + + '\n' + + html.slice(findTags); + } catch(e) {} + + return html; +} IonicTask.prototype._changed = function(filePath) { // Cleanup the path a bit From ce1565d216950cb28bcff780a063934c6edcc126 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 8 Dec 2014 18:15:36 -0600 Subject: [PATCH 0303/1100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 403ed5a361..f8d807c0ca 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ Use `ionic serve` to start a local development server for app dev and testing. T $ ionic serve [options] ``` -__Proxies:__ +__Service Proxies:__ The `serve` command can add some proxies to the http server. These proxies are useful if you are developing in the browser and you need to make calls to an external API. With this feature you can proxy request to the external api through the ionic http server preventing the `No 'Access-Control-Allow-Origin' header is present on the requested resource` error. From 4c94afcc4aa3f49bf69591ac0d43704bf66b0399 Mon Sep 17 00:00:00 2001 From: jbavari Date: Mon, 8 Dec 2014 17:37:24 -0700 Subject: [PATCH 0304/1100] Bumping version to 1.2.9, adding myself as maintainer --- package.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 36033a77cf..5dfa933b4c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.2.8", + "version": "1.2.9", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -35,6 +35,11 @@ { "name": "Adam Bradley", "web": "https://twitter.com/adamdbradley" + }, + { + "name": "Josh Bavari", + "web": "https://twitter.com/jbavari", + "email": "josh@drifty.com" } ], "license": "MIT", From 674df4906a091305edcfcdc545e37030ebcd43fa Mon Sep 17 00:00:00 2001 From: jbavari Date: Mon, 8 Dec 2014 17:39:45 -0700 Subject: [PATCH 0305/1100] Shrinkwrapped --- npm-shrinkwrap.json | 663 +++++++++++++++++++++++--------------------- 1 file changed, 347 insertions(+), 316 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index ceae9c9547..f36e3667be 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.2.8", + "version": "1.2.9", "dependencies": { "archiver": { "version": "0.5.1", @@ -9,12 +9,12 @@ "dependencies": { "readable-stream": { "version": "1.1.13", - "from": "readable-stream@1.1.13", + "from": "readable-stream@>=1.1.9 <1.2.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", "dependencies": { "core-util-is": { "version": "1.0.1", - "from": "core-util-is@1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { @@ -24,29 +24,29 @@ }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } }, "zip-stream": { "version": "0.1.4", - "from": "zip-stream@0.1.4", + "from": "zip-stream@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-0.1.4.tgz", "dependencies": { "readable-stream": { - "version": "1.0.33-1", - "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", + "version": "1.0.33", + "from": "readable-stream@>=1.0.24 <1.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", "dependencies": { "core-util-is": { "version": "1.0.1", - "from": "core-util-is@1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { @@ -56,46 +56,46 @@ }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } }, "lodash.defaults": { "version": "2.4.1", - "from": "lodash.defaults@2.4.1", + "from": "lodash.defaults@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz", "dependencies": { "lodash.keys": { "version": "2.4.1", - "from": "lodash.keys@2.4.1", + "from": "lodash.keys@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", "dependencies": { "lodash._isnative": { "version": "2.4.1", - "from": "lodash._isnative@2.4.1", + "from": "lodash._isnative@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz" }, "lodash.isobject": { "version": "2.4.1", - "from": "lodash.isobject@2.4.1", + "from": "lodash.isobject@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz" }, "lodash._shimkeys": { "version": "2.4.1", - "from": "lodash._shimkeys@2.4.1", + "from": "lodash._shimkeys@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz" } } }, "lodash._objecttypes": { "version": "2.4.1", - "from": "lodash._objecttypes@2.4.1", + "from": "lodash._objecttypes@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz" } } @@ -104,17 +104,17 @@ }, "lazystream": { "version": "0.1.0", - "from": "lazystream@0.1.0", + "from": "lazystream@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-0.1.0.tgz", "dependencies": { "readable-stream": { - "version": "1.0.33-1", - "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", + "version": "1.0.33", + "from": "readable-stream@>=1.0.2 <1.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", "dependencies": { "core-util-is": { "version": "1.0.1", - "from": "core-util-is@1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { @@ -124,12 +124,12 @@ }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } @@ -138,47 +138,47 @@ }, "file-utils": { "version": "0.1.5", - "from": "file-utils@0.1.5", + "from": "file-utils@>=0.1.5 <0.2.0", "resolved": "https://registry.npmjs.org/file-utils/-/file-utils-0.1.5.tgz", "dependencies": { "lodash": { "version": "2.1.0", - "from": "lodash@2.1.0", + "from": "lodash@>=2.1.0 <2.2.0", "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.1.0.tgz" }, "iconv-lite": { "version": "0.2.11", - "from": "iconv-lite@0.2.11", + "from": "iconv-lite@>=0.2.11 <0.3.0", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz" }, "rimraf": { "version": "2.2.8", - "from": "rimraf@2.2.8", + "from": "rimraf@>=2.2.2 <2.3.0", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" }, "glob": { "version": "3.2.11", - "from": "glob@3.2.11", + "from": "glob@>=3.2.6 <3.3.0", "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", "dependencies": { "inherits": { "version": "2.0.1", - "from": "inherits@2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "minimatch": { "version": "0.3.0", - "from": "minimatch@0.3.0", + "from": "minimatch@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", "dependencies": { "lru-cache": { "version": "2.5.0", - "from": "lru-cache@2.5.0", + "from": "lru-cache@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" }, "sigmund": { "version": "1.0.0", - "from": "sigmund@1.0.0", + "from": "sigmund@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" } } @@ -187,65 +187,65 @@ }, "minimatch": { "version": "0.2.14", - "from": "minimatch@0.2.14", + "from": "minimatch@>=0.2.12 <0.3.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "dependencies": { "lru-cache": { "version": "2.5.0", - "from": "lru-cache@2.5.0", + "from": "lru-cache@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" }, "sigmund": { "version": "1.0.0", - "from": "sigmund@1.0.0", + "from": "sigmund@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" } } }, "findup-sync": { "version": "0.1.3", - "from": "findup-sync@0.1.3", + "from": "findup-sync@>=0.1.2 <0.2.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", "dependencies": { "lodash": { "version": "2.4.1", - "from": "lodash@2.4.1", + "from": "lodash@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz" } } }, "isbinaryfile": { "version": "0.1.9", - "from": "isbinaryfile@0.1.9", + "from": "isbinaryfile@>=0.1.9 <0.2.0", "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-0.1.9.tgz" } } }, "lodash": { "version": "2.4.1", - "from": "lodash@2.4.1", + "from": "lodash@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz" } } }, "colors": { "version": "0.6.2", - "from": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "from": "colors@0.6.2", "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz" }, "connect": { "version": "3.1.1", - "from": "https://registry.npmjs.org/connect/-/connect-3.1.1.tgz", + "from": "connect@3.1.1", "resolved": "https://registry.npmjs.org/connect/-/connect-3.1.1.tgz", "dependencies": { "debug": { "version": "1.0.4", - "from": "https://registry.npmjs.org/debug/-/debug-1.0.4.tgz", + "from": "debug@1.0.4", "resolved": "https://registry.npmjs.org/debug/-/debug-1.0.4.tgz", "dependencies": { "ms": { "version": "0.6.2", - "from": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz", + "from": "ms@0.6.2", "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz" } } @@ -264,12 +264,12 @@ }, "parseurl": { "version": "1.3.0", - "from": "parseurl@1.3.0", + "from": "parseurl@>=1.3.0 <1.4.0", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.0.tgz" }, "utils-merge": { "version": "1.0.0", - "from": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", + "from": "utils-merge@1.0.0", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz" } } @@ -281,40 +281,40 @@ }, "cross-spawn": { "version": "0.2.3", - "from": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-0.2.3.tgz", + "from": "cross-spawn@0.2.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-0.2.3.tgz", "dependencies": { "lru-cache": { "version": "2.5.0", - "from": "lru-cache@2.5.0", + "from": "lru-cache@>=2.5.0 <3.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" } } }, "event-stream": { "version": "3.0.20", - "from": "event-stream@3.0.20", + "from": "event-stream@>=3.0.0 <3.1.0", "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.0.20.tgz", "dependencies": { "through": { "version": "2.3.6", - "from": "through@2.3.6", + "from": "through@>=2.3.1 <2.4.0", "resolved": "https://registry.npmjs.org/through/-/through-2.3.6.tgz" }, "duplexer": { "version": "0.1.1", - "from": "duplexer@0.1.1", + "from": "duplexer@>=0.1.1 <0.2.0", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz" }, "from": { "version": "0.1.3", - "from": "from@0.1.3", + "from": "from@>=0.0.0 <1.0.0", "resolved": "https://registry.npmjs.org/from/-/from-0.1.3.tgz" }, "map-stream": { - "version": "0.0.4", - "from": "map-stream@0.0.4", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.4.tgz" + "version": "0.0.5", + "from": "map-stream@>=0.0.3 <0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.5.tgz" }, "pause-stream": { "version": "0.0.11", @@ -323,29 +323,29 @@ }, "split": { "version": "0.2.10", - "from": "split@0.2.10", + "from": "split@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz" }, "stream-combiner": { "version": "0.0.4", - "from": "stream-combiner@0.0.4", + "from": "stream-combiner@>=0.0.3 <0.1.0", "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz" } } }, "finalhandler": { "version": "0.2.0", - "from": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.2.0.tgz", + "from": "finalhandler@0.2.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.2.0.tgz", "dependencies": { "debug": { "version": "2.0.0", - "from": "debug@2.0.0", + "from": "debug@>=2.0.0 <2.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-2.0.0.tgz", "dependencies": { "ms": { "version": "0.6.2", - "from": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz", + "from": "ms@0.6.2", "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz" } } @@ -359,13 +359,13 @@ }, "form-data": { "version": "0.1.4", - "from": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", + "from": "form-data@0.1.4", "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", "dependencies": { "combined-stream": { - "version": "0.0.5", - "from": "combined-stream@0.0.5", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.5.tgz", + "version": "0.0.7", + "from": "combined-stream@>=0.0.4 <0.1.0", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", "dependencies": { "delayed-stream": { "version": "0.0.5", @@ -376,85 +376,85 @@ }, "mime": { "version": "1.2.11", - "from": "mime@1.2.11", + "from": "mime@>=1.2.11 <1.3.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" }, "async": { "version": "0.9.0", - "from": "async@0.9.0", + "from": "async@>=0.9.0 <0.10.0", "resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz" } } }, "gulp": { "version": "3.8.10", - "from": "gulp@", + "from": "gulp@>=3.8.8 <3.9.0", "resolved": "https://registry.npmjs.org/gulp/-/gulp-3.8.10.tgz", "dependencies": { "archy": { "version": "1.0.0", - "from": "archy@^1.0.0", + "from": "archy@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz" }, "chalk": { "version": "0.5.1", - "from": "chalk@^0.5.0", + "from": "chalk@>=0.5.0 <0.6.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", "dependencies": { "ansi-styles": { "version": "1.1.0", - "from": "ansi-styles@^1.1.0", + "from": "ansi-styles@>=1.1.0 <2.0.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" }, "escape-string-regexp": { "version": "1.0.2", - "from": "escape-string-regexp@^1.0.0", + "from": "escape-string-regexp@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz" }, "has-ansi": { "version": "0.1.0", - "from": "has-ansi@^0.1.0", + "from": "has-ansi@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", "dependencies": { "ansi-regex": { "version": "0.2.1", - "from": "ansi-regex@^0.2.0", + "from": "ansi-regex@>=0.2.1 <0.3.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" } } }, "strip-ansi": { "version": "0.3.0", - "from": "strip-ansi@^0.3.0", + "from": "strip-ansi@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", "dependencies": { "ansi-regex": { "version": "0.2.1", - "from": "ansi-regex@^0.2.1", + "from": "ansi-regex@>=0.2.1 <0.3.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" } } }, "supports-color": { "version": "0.2.0", - "from": "supports-color@^0.2.0", + "from": "supports-color@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" } } }, "deprecated": { "version": "0.0.1", - "from": "deprecated@^0.0.1", + "from": "deprecated@>=0.0.1 <0.0.2", "resolved": "https://registry.npmjs.org/deprecated/-/deprecated-0.0.1.tgz" }, "gulp-util": { "version": "3.0.1", - "from": "gulp-util@^3.0.0", + "from": "gulp-util@>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.1.tgz", "dependencies": { "dateformat": { "version": "1.0.11", - "from": "dateformat@^1.0.7-1.2.3", + "from": "dateformat@>=1.0.7-1.2.3 <2.0.0", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.11.tgz", "dependencies": { "get-stdin": { @@ -469,39 +469,39 @@ "dependencies": { "camelcase-keys": { "version": "1.0.0", - "from": "camelcase-keys@^1.0.0", + "from": "camelcase-keys@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-1.0.0.tgz", "dependencies": { "camelcase": { "version": "1.0.2", - "from": "camelcase@^1.0.1", + "from": "camelcase@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.0.2.tgz" }, "map-obj": { "version": "1.0.0", - "from": "map-obj@^1.0.0", + "from": "map-obj@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.0.tgz" } } }, "indent-string": { "version": "1.2.0", - "from": "indent-string@^1.1.0", + "from": "indent-string@>=1.1.0 <2.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.0.tgz", "dependencies": { "repeating": { "version": "1.1.0", - "from": "repeating@^1.1.0", + "from": "repeating@>=1.1.0 <2.0.0", "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.0.tgz", "dependencies": { "is-finite": { "version": "1.0.0", - "from": "is-finite@^1.0.0", + "from": "is-finite@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.0.tgz" }, "meow": { "version": "1.0.0", - "from": "meow@^1.0.0", + "from": "meow@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/meow/-/meow-1.0.0.tgz" } } @@ -510,7 +510,7 @@ }, "object-assign": { "version": "1.0.0", - "from": "object-assign@^1.0.0", + "from": "object-assign@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-1.0.0.tgz" } } @@ -519,56 +519,56 @@ }, "lodash": { "version": "2.4.1", - "from": "lodash@^2.4.1", + "from": "lodash@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz" }, "lodash._reinterpolate": { "version": "2.4.1", - "from": "lodash._reinterpolate@^2.4.1", + "from": "lodash._reinterpolate@>=2.4.1 <3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-2.4.1.tgz" }, "lodash.template": { "version": "2.4.1", - "from": "lodash.template@^2.4.1", + "from": "lodash.template@>=2.4.1 <3.0.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-2.4.1.tgz", "dependencies": { "lodash.defaults": { "version": "2.4.1", - "from": "lodash.defaults@~2.4.1", + "from": "lodash.defaults@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz", "dependencies": { "lodash._objecttypes": { "version": "2.4.1", - "from": "lodash._objecttypes@~2.4.1", + "from": "lodash._objecttypes@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz" } } }, "lodash.escape": { "version": "2.4.1", - "from": "lodash.escape@~2.4.1", + "from": "lodash.escape@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-2.4.1.tgz", "dependencies": { "lodash._escapehtmlchar": { "version": "2.4.1", - "from": "lodash._escapehtmlchar@~2.4.1", + "from": "lodash._escapehtmlchar@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.4.1.tgz", "dependencies": { "lodash._htmlescapes": { "version": "2.4.1", - "from": "lodash._htmlescapes@~2.4.1", + "from": "lodash._htmlescapes@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash._htmlescapes/-/lodash._htmlescapes-2.4.1.tgz" } } }, "lodash._reunescapedhtml": { "version": "2.4.1", - "from": "lodash._reunescapedhtml@~2.4.1", + "from": "lodash._reunescapedhtml@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash._reunescapedhtml/-/lodash._reunescapedhtml-2.4.1.tgz", "dependencies": { "lodash._htmlescapes": { "version": "2.4.1", - "from": "lodash._htmlescapes@~2.4.1", + "from": "lodash._htmlescapes@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash._htmlescapes/-/lodash._htmlescapes-2.4.1.tgz" } } @@ -577,39 +577,39 @@ }, "lodash._escapestringchar": { "version": "2.4.1", - "from": "lodash._escapestringchar@~2.4.1", + "from": "lodash._escapestringchar@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash._escapestringchar/-/lodash._escapestringchar-2.4.1.tgz" }, "lodash.keys": { "version": "2.4.1", - "from": "lodash.keys@~2.4.1", + "from": "lodash.keys@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", "dependencies": { "lodash._isnative": { "version": "2.4.1", - "from": "lodash._isnative@~2.4.1", + "from": "lodash._isnative@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz" }, "lodash.isobject": { "version": "2.4.1", - "from": "lodash.isobject@~2.4.1", + "from": "lodash.isobject@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", "dependencies": { "lodash._objecttypes": { "version": "2.4.1", - "from": "lodash._objecttypes@~2.4.1", + "from": "lodash._objecttypes@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz" } } }, "lodash._shimkeys": { "version": "2.4.1", - "from": "lodash._shimkeys@~2.4.1", + "from": "lodash._shimkeys@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz", "dependencies": { "lodash._objecttypes": { "version": "2.4.1", - "from": "lodash._objecttypes@~2.4.1", + "from": "lodash._objecttypes@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz" } } @@ -618,19 +618,19 @@ }, "lodash.templatesettings": { "version": "2.4.1", - "from": "lodash.templatesettings@~2.4.1", + "from": "lodash.templatesettings@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-2.4.1.tgz" }, "lodash.values": { "version": "2.4.1", - "from": "lodash.values@~2.4.1", + "from": "lodash.values@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-2.4.1.tgz" } } }, "multipipe": { "version": "0.1.2", - "from": "multipipe@^0.1.0", + "from": "multipipe@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", "dependencies": { "duplexer2": { @@ -640,12 +640,12 @@ "dependencies": { "readable-stream": { "version": "1.1.13", - "from": "readable-stream@~1.1.9", + "from": "readable-stream@>=1.1.9 <1.2.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", "dependencies": { "core-util-is": { "version": "1.0.1", - "from": "core-util-is@~1.0.0", + "from": "core-util-is@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { @@ -655,12 +655,12 @@ }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@~0.10.x", + "from": "string_decoder@>=0.10.0 <0.11.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@~2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } @@ -671,7 +671,7 @@ }, "through2": { "version": "0.6.3", - "from": "through2@^0.6.1", + "from": "through2@>=0.6.1 <0.7.0", "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.3.tgz", "dependencies": { "readable-stream": { @@ -681,7 +681,7 @@ "dependencies": { "core-util-is": { "version": "1.0.1", - "from": "core-util-is@~1.0.0", + "from": "core-util-is@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { @@ -691,12 +691,12 @@ }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@~0.10.x", + "from": "string_decoder@>=0.10.0 <0.11.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@~2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } @@ -710,17 +710,17 @@ }, "vinyl": { "version": "0.4.6", - "from": "vinyl@^0.4.0", + "from": "vinyl@>=0.4.0 <0.5.0", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", "dependencies": { "clone": { "version": "0.2.0", - "from": "clone@^0.2.0", + "from": "clone@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz" }, "clone-stats": { "version": "0.0.1", - "from": "clone-stats@^0.0.1", + "from": "clone-stats@>=0.0.1 <0.0.2", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" } } @@ -729,42 +729,42 @@ }, "interpret": { "version": "0.3.8", - "from": "interpret@^0.3.2", + "from": "interpret@>=0.3.2 <0.4.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-0.3.8.tgz" }, "liftoff": { "version": "0.13.6", - "from": "liftoff@^0.13.2", + "from": "liftoff@>=0.13.2 <0.14.0", "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-0.13.6.tgz", "dependencies": { "findup-sync": { "version": "0.1.3", - "from": "findup-sync@~0.1.2", + "from": "findup-sync@>=0.1.2 <0.2.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", "dependencies": { "glob": { "version": "3.2.11", - "from": "glob@~3.2.9", + "from": "glob@>=3.2.9 <3.3.0", "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", "dependencies": { "inherits": { "version": "2.0.1", - "from": "inherits@2", + "from": "inherits@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "minimatch": { "version": "0.3.0", - "from": "minimatch@0.3", + "from": "minimatch@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", "dependencies": { "lru-cache": { "version": "2.5.0", - "from": "lru-cache@2", + "from": "lru-cache@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" }, "sigmund": { "version": "1.0.0", - "from": "sigmund@~1.0.0", + "from": "sigmund@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" } } @@ -773,51 +773,51 @@ }, "lodash": { "version": "2.4.1", - "from": "lodash@~2.4.1", + "from": "lodash@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz" } } }, "resolve": { "version": "1.0.0", - "from": "resolve@~1.0.0", + "from": "resolve@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.0.0.tgz" }, "extend": { "version": "1.3.0", - "from": "extend@~1.3.0", + "from": "extend@>=1.3.0 <1.4.0", "resolved": "https://registry.npmjs.org/extend/-/extend-1.3.0.tgz" }, "flagged-respawn": { "version": "0.3.1", - "from": "flagged-respawn@~0.3.0", + "from": "flagged-respawn@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-0.3.1.tgz" } } }, "minimist": { "version": "1.1.0", - "from": "minimist@^1.1.0", + "from": "minimist@>=1.1.0 <2.0.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.1.0.tgz" }, "orchestrator": { "version": "0.3.7", - "from": "orchestrator@^0.3.0", + "from": "orchestrator@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/orchestrator/-/orchestrator-0.3.7.tgz", "dependencies": { "end-of-stream": { "version": "0.1.5", - "from": "end-of-stream@~0.1.5", + "from": "end-of-stream@>=0.1.5 <0.2.0", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz", "dependencies": { "once": { "version": "1.3.1", - "from": "once@~1.3.0", + "from": "once@>=1.3.0 <1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.3.1.tgz", "dependencies": { "wrappy": { "version": "1.0.1", - "from": "wrappy@1", + "from": "wrappy@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" } } @@ -826,41 +826,41 @@ }, "sequencify": { "version": "0.0.7", - "from": "sequencify@~0.0.7", + "from": "sequencify@>=0.0.7 <0.1.0", "resolved": "https://registry.npmjs.org/sequencify/-/sequencify-0.0.7.tgz" }, "stream-consume": { "version": "0.1.0", - "from": "stream-consume@~0.1.0", + "from": "stream-consume@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz" } } }, "pretty-hrtime": { "version": "0.2.2", - "from": "pretty-hrtime@^0.2.0", + "from": "pretty-hrtime@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-0.2.2.tgz" }, "semver": { "version": "4.1.0", - "from": "semver@^4.1.0", + "from": "semver@>=4.1.0 <5.0.0", "resolved": "https://registry.npmjs.org/semver/-/semver-4.1.0.tgz" }, "tildify": { "version": "1.0.0", - "from": "tildify@^1.0.0", + "from": "tildify@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/tildify/-/tildify-1.0.0.tgz", "dependencies": { "user-home": { "version": "1.1.0", - "from": "user-home@^1.0.0", + "from": "user-home@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.0.tgz" } } }, "v8flags": { "version": "1.0.5", - "from": "v8flags@^1.0.1", + "from": "v8flags@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-1.0.5.tgz" } } @@ -872,7 +872,7 @@ }, "npm": { "version": "2.1.3", - "from": "https://registry.npmjs.org/npm/-/npm-2.1.3.tgz", + "from": "npm@2.1.3", "resolved": "https://registry.npmjs.org/npm/-/npm-2.1.3.tgz", "dependencies": { "abbrev": { @@ -1436,22 +1436,22 @@ }, "open": { "version": "0.0.5", - "from": "https://registry.npmjs.org/open/-/open-0.0.5.tgz", + "from": "open@0.0.5", "resolved": "https://registry.npmjs.org/open/-/open-0.0.5.tgz" }, "optimist": { "version": "0.6.0", - "from": "https://registry.npmjs.org/optimist/-/optimist-0.6.0.tgz", + "from": "optimist@0.6.0", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.0.tgz", "dependencies": { "wordwrap": { "version": "0.0.2", - "from": "wordwrap@0.0.2", + "from": "wordwrap@>=0.0.2 <0.1.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz" }, "minimist": { "version": "0.0.10", - "from": "minimist@0.0.10", + "from": "minimist@>=0.0.1 <0.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz" } } @@ -1463,103 +1463,103 @@ }, "prompt": { "version": "0.2.12", - "from": "https://registry.npmjs.org/prompt/-/prompt-0.2.12.tgz", + "from": "prompt@0.2.12", "resolved": "https://registry.npmjs.org/prompt/-/prompt-0.2.12.tgz", "dependencies": { "pkginfo": { "version": "0.3.0", - "from": "pkginfo@0.3.0", + "from": "pkginfo@>=0.0.0 <1.0.0", "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.0.tgz" }, "read": { "version": "1.0.5", - "from": "read@1.0.5", + "from": "read@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/read/-/read-1.0.5.tgz", "dependencies": { "mute-stream": { "version": "0.0.4", - "from": "mute-stream@0.0.4", + "from": "mute-stream@>=0.0.4 <0.1.0", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.4.tgz" } } }, "revalidator": { "version": "0.1.8", - "from": "revalidator@0.1.8", + "from": "revalidator@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz" }, "utile": { "version": "0.2.1", - "from": "utile@0.2.1", + "from": "utile@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/utile/-/utile-0.2.1.tgz", "dependencies": { "async": { "version": "0.2.10", - "from": "async@0.2.10", + "from": "async@>=0.2.9 <0.3.0", "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" }, "deep-equal": { "version": "0.2.1", - "from": "deep-equal@0.2.1", + "from": "deep-equal@*", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.2.1.tgz" }, "i": { "version": "0.3.2", - "from": "i@0.3.2", + "from": "i@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/i/-/i-0.3.2.tgz" }, "mkdirp": { "version": "0.5.0", - "from": "mkdirp@0.5.0", + "from": "mkdirp@>=0.0.0 <1.0.0", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", "dependencies": { "minimist": { "version": "0.0.8", - "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "from": "minimist@0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } }, "rimraf": { "version": "2.2.8", - "from": "rimraf@2.2.8", + "from": "rimraf@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" } } }, "winston": { "version": "0.6.2", - "from": "winston@0.6.2", + "from": "winston@>=0.6.0 <0.7.0", "resolved": "https://registry.npmjs.org/winston/-/winston-0.6.2.tgz", "dependencies": { "async": { "version": "0.1.22", - "from": "async@0.1.22", + "from": "async@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz" }, "cycle": { "version": "1.0.3", - "from": "cycle@1.0.3", + "from": "cycle@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz" }, "eyes": { "version": "0.1.8", - "from": "eyes@0.1.8", + "from": "eyes@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz" }, "pkginfo": { "version": "0.2.3", - "from": "pkginfo@0.2.3", + "from": "pkginfo@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.2.3.tgz" }, "request": { "version": "2.9.203", - "from": "request@2.9.203", + "from": "request@>=2.9.0 <2.10.0", "resolved": "https://registry.npmjs.org/request/-/request-2.9.203.tgz" }, "stack-trace": { "version": "0.0.9", - "from": "stack-trace@0.0.9", + "from": "stack-trace@>=0.0.0 <0.1.0", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" } } @@ -1568,52 +1568,52 @@ }, "proxy-middleware": { "version": "0.7.0", - "from": "proxy-middleware@0.7.0", + "from": "proxy-middleware@>=0.7.0 <0.8.0", "resolved": "https://registry.npmjs.org/proxy-middleware/-/proxy-middleware-0.7.0.tgz" }, "q": { "version": "1.0.1", - "from": "https://registry.npmjs.org/q/-/q-1.0.1.tgz", + "from": "q@1.0.1", "resolved": "https://registry.npmjs.org/q/-/q-1.0.1.tgz" }, "request": { "version": "2.27.0", - "from": "https://registry.npmjs.org/request/-/request-2.27.0.tgz", + "from": "request@2.27.0", "resolved": "https://registry.npmjs.org/request/-/request-2.27.0.tgz", "dependencies": { "qs": { "version": "0.6.6", - "from": "qs@0.6.6", + "from": "qs@>=0.6.0 <0.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz" }, "json-stringify-safe": { "version": "5.0.0", - "from": "json-stringify-safe@5.0.0", + "from": "json-stringify-safe@>=5.0.0 <5.1.0", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.0.tgz" }, "forever-agent": { "version": "0.5.2", - "from": "forever-agent@0.5.2", + "from": "forever-agent@>=0.5.0 <0.6.0", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" }, "tunnel-agent": { "version": "0.3.0", - "from": "tunnel-agent@0.3.0", + "from": "tunnel-agent@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz" }, "http-signature": { "version": "0.10.0", - "from": "http-signature@0.10.0", + "from": "http-signature@>=0.10.0 <0.11.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.0.tgz", "dependencies": { "assert-plus": { "version": "0.1.2", - "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.2.tgz", + "from": "assert-plus@0.1.2", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.2.tgz" }, "asn1": { "version": "0.1.11", - "from": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", + "from": "asn1@0.1.11", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz" }, "ctype": { @@ -1625,62 +1625,62 @@ }, "hawk": { "version": "1.0.0", - "from": "hawk@1.0.0", + "from": "hawk@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", "dependencies": { "hoek": { "version": "0.9.1", - "from": "hoek@0.9.1", + "from": "hoek@>=0.9.0 <0.10.0", "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz" }, "boom": { "version": "0.4.2", - "from": "boom@0.4.2", + "from": "boom@>=0.4.0 <0.5.0", "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz" }, "cryptiles": { "version": "0.2.2", - "from": "cryptiles@0.2.2", + "from": "cryptiles@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz" }, "sntp": { "version": "0.2.4", - "from": "sntp@0.2.4", + "from": "sntp@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz" } } }, "aws-sign": { "version": "0.3.0", - "from": "aws-sign@0.3.0", + "from": "aws-sign@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/aws-sign/-/aws-sign-0.3.0.tgz" }, "oauth-sign": { "version": "0.3.0", - "from": "oauth-sign@0.3.0", + "from": "oauth-sign@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz" }, "cookie-jar": { "version": "0.3.0", - "from": "cookie-jar@0.3.0", + "from": "cookie-jar@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/cookie-jar/-/cookie-jar-0.3.0.tgz" }, "node-uuid": { - "version": "1.4.1", - "from": "node-uuid@1.4.1", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.1.tgz" + "version": "1.4.2", + "from": "node-uuid@>=1.4.0 <1.5.0", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.2.tgz" }, "mime": { "version": "1.2.11", - "from": "mime@1.2.11", + "from": "mime@>=1.2.9 <1.3.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" } } }, "serve-static": { - "version": "1.6.1", - "from": "https://registry.npmjs.org/serve-static/-/serve-static-1.6.1.tgz", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.6.1.tgz", + "version": "1.7.1", + "from": "serve-static@1.7.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.7.1.tgz", "dependencies": { "escape-html": { "version": "1.0.1", @@ -1689,23 +1689,23 @@ }, "parseurl": { "version": "1.3.0", - "from": "parseurl@1.3.0", + "from": "parseurl@>=1.3.0 <1.4.0", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.0.tgz" }, "send": { - "version": "0.9.1", - "from": "https://registry.npmjs.org/send/-/send-0.9.1.tgz", - "resolved": "https://registry.npmjs.org/send/-/send-0.9.1.tgz", + "version": "0.10.1", + "from": "send@0.10.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.10.1.tgz", "dependencies": { "debug": { - "version": "2.0.0", - "from": "debug@2.0.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.0.0.tgz" + "version": "2.1.0", + "from": "debug@>=2.1.0 <2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.1.0.tgz" }, "depd": { - "version": "0.4.4", - "from": "depd@0.4.4", - "resolved": "https://registry.npmjs.org/depd/-/depd-0.4.4.tgz" + "version": "1.0.0", + "from": "depd@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.0.0.tgz" }, "destroy": { "version": "1.0.3", @@ -1713,20 +1713,20 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.3.tgz" }, "etag": { - "version": "1.3.1", - "from": "https://registry.npmjs.org/etag/-/etag-1.3.1.tgz", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.3.1.tgz", + "version": "1.5.1", + "from": "etag@>=1.5.0 <1.6.0", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.5.1.tgz", "dependencies": { "crc": { - "version": "3.0.0", - "from": "https://registry.npmjs.org/crc/-/crc-3.0.0.tgz", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.0.0.tgz" + "version": "3.2.1", + "from": "crc@3.2.1", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.2.1.tgz" } } }, "fresh": { "version": "0.2.4", - "from": "https://registry.npmjs.org/fresh/-/fresh-0.2.4.tgz", + "from": "fresh@0.2.4", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.4.tgz" }, "mime": { @@ -1736,68 +1736,68 @@ }, "ms": { "version": "0.6.2", - "from": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz", + "from": "ms@0.6.2", "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz" }, "on-finished": { - "version": "2.1.0", - "from": "on-finished@2.1.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.1.0.tgz", + "version": "2.1.1", + "from": "on-finished@>=2.1.1 <2.2.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.1.1.tgz", "dependencies": { "ee-first": { - "version": "1.0.5", - "from": "ee-first@1.0.5", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.0.5.tgz" + "version": "1.1.0", + "from": "ee-first@1.1.0", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.0.tgz" } } }, "range-parser": { "version": "1.0.2", - "from": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.2.tgz", + "from": "range-parser@>=1.0.2 <1.1.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.2.tgz" } } }, "utils-merge": { "version": "1.0.0", - "from": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", + "from": "utils-merge@1.0.0", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz" } } }, "shelljs": { "version": "0.2.6", - "from": "https://registry.npmjs.org/shelljs/-/shelljs-0.2.6.tgz", + "from": "shelljs@0.2.6", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.2.6.tgz" }, "tiny-lr-fork": { "version": "0.0.5", - "from": "https://registry.npmjs.org/tiny-lr-fork/-/tiny-lr-fork-0.0.5.tgz", + "from": "tiny-lr-fork@0.0.5", "resolved": "https://registry.npmjs.org/tiny-lr-fork/-/tiny-lr-fork-0.0.5.tgz", "dependencies": { "qs": { "version": "0.5.6", - "from": "qs@0.5.6", + "from": "qs@>=0.5.2 <0.6.0", "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz" }, "faye-websocket": { "version": "0.4.4", - "from": "faye-websocket@0.4.4", + "from": "faye-websocket@>=0.4.3 <0.5.0", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz" }, "noptify": { "version": "0.0.3", - "from": "noptify@0.0.3", + "from": "noptify@>=0.0.3 <0.1.0", "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz", "dependencies": { "nopt": { "version": "2.0.0", - "from": "nopt@2.0.0", + "from": "nopt@>=2.0.0 <2.1.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz", "dependencies": { "abbrev": { "version": "1.0.5", - "from": "abbrev@1.0.5", + "from": "abbrev@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz" } } @@ -1806,99 +1806,99 @@ }, "debug": { "version": "0.7.4", - "from": "debug@0.7.4", + "from": "debug@>=0.7.0 <0.8.0", "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz" } } }, "unzip": { "version": "0.1.9", - "from": "https://registry.npmjs.org/unzip/-/unzip-0.1.9.tgz", + "from": "unzip@0.1.9", "resolved": "https://registry.npmjs.org/unzip/-/unzip-0.1.9.tgz", "dependencies": { "fstream": { "version": "0.1.31", - "from": "fstream@0.1.31", + "from": "fstream@>=0.1.21 <0.2.0", "resolved": "https://registry.npmjs.org/fstream/-/fstream-0.1.31.tgz", "dependencies": { "graceful-fs": { - "version": "3.0.4", - "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.4.tgz", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.4.tgz" + "version": "3.0.5", + "from": "graceful-fs@>=3.0.2 <3.1.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.5.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@2.0.1", + "from": "inherits@>=2.0.0 <2.1.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "mkdirp": { "version": "0.5.0", - "from": "mkdirp@0.5.0", + "from": "mkdirp@>=0.0.0 <1.0.0", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", "dependencies": { "minimist": { "version": "0.0.8", - "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "from": "minimist@0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } }, "rimraf": { "version": "2.2.8", - "from": "rimraf@2.2.8", + "from": "rimraf@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" } } }, "pullstream": { "version": "0.4.1", - "from": "https://registry.npmjs.org/pullstream/-/pullstream-0.4.1.tgz", + "from": "pullstream@>=0.4.0 <0.5.0", "resolved": "https://registry.npmjs.org/pullstream/-/pullstream-0.4.1.tgz", "dependencies": { "over": { "version": "0.0.5", - "from": "over@0.0.5", + "from": "over@>=0.0.5 <1.0.0", "resolved": "https://registry.npmjs.org/over/-/over-0.0.5.tgz" }, "slice-stream": { "version": "1.0.0", - "from": "https://registry.npmjs.org/slice-stream/-/slice-stream-1.0.0.tgz", + "from": "slice-stream@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/slice-stream/-/slice-stream-1.0.0.tgz" } } }, "binary": { "version": "0.3.0", - "from": "binary@0.3.0", + "from": "binary@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", "dependencies": { "chainsaw": { "version": "0.1.0", - "from": "chainsaw@0.1.0", + "from": "chainsaw@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", "dependencies": { "traverse": { "version": "0.3.9", - "from": "traverse@0.3.9", + "from": "traverse@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz" } } }, "buffers": { "version": "0.1.1", - "from": "buffers@0.1.1", + "from": "buffers@>=0.1.1 <0.2.0", "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz" } } }, "readable-stream": { - "version": "1.0.33-1", - "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", + "version": "1.0.33", + "from": "readable-stream@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", "dependencies": { "core-util-is": { "version": "1.0.1", - "from": "core-util-is@1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { @@ -1908,29 +1908,29 @@ }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } }, "setimmediate": { "version": "1.0.2", - "from": "setimmediate@1.0.2", + "from": "setimmediate@>=1.0.1 <1.1.0", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.2.tgz" }, "match-stream": { "version": "0.0.2", - "from": "match-stream@0.0.2", + "from": "match-stream@>=0.0.2 <0.1.0", "resolved": "https://registry.npmjs.org/match-stream/-/match-stream-0.0.2.tgz", "dependencies": { "buffers": { "version": "0.1.1", - "from": "buffers@0.1.1", + "from": "buffers@>=0.1.1 <0.2.0", "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz" } } @@ -1939,32 +1939,44 @@ }, "vinyl-fs": { "version": "0.3.7", - "from": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-0.3.7.tgz", + "from": "vinyl-fs@0.3.7", "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-0.3.7.tgz", "dependencies": { "glob-stream": { - "version": "3.1.15", - "from": "glob-stream@3.1.15", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.15.tgz", + "version": "3.1.18", + "from": "glob-stream@>=3.1.5 <4.0.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.18.tgz", "dependencies": { "glob": { - "version": "4.0.6", - "from": "glob@4.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-4.0.6.tgz", + "version": "4.3.1", + "from": "glob@>=4.3.1 <5.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.3.1.tgz", "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, "inherits": { "version": "2.0.1", - "from": "inherits@2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "once": { "version": "1.3.1", - "from": "once@1.3.1", + "from": "once@>=1.3.0 <2.0.0", "resolved": "https://registry.npmjs.org/once/-/once-1.3.1.tgz", "dependencies": { "wrappy": { "version": "1.0.1", - "from": "wrappy@1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" } } @@ -1972,89 +1984,103 @@ } }, "minimatch": { - "version": "1.0.0", - "from": "minimatch@1.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz", + "version": "2.0.1", + "from": "minimatch@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.1.tgz", "dependencies": { - "lru-cache": { - "version": "2.5.0", - "from": "lru-cache@2.5.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" - }, - "sigmund": { - "version": "1.0.0", - "from": "sigmund@1.0.0", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + "brace-expansion": { + "version": "1.0.1", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.0.1.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "from": "balanced-match@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.0", + "from": "concat-map@0.0.0", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.0.tgz" + } + } } } }, "ordered-read-streams": { - "version": "0.0.8", - "from": "ordered-read-streams@0.0.8", - "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.0.8.tgz" + "version": "0.1.0", + "from": "ordered-read-streams@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz" }, "glob2base": { - "version": "0.0.11", - "from": "glob2base@0.0.11", - "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.11.tgz" + "version": "0.0.12", + "from": "glob2base@>=0.0.12 <0.0.13", + "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", + "dependencies": { + "find-index": { + "version": "0.1.1", + "from": "find-index@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz" + } + } }, "unique-stream": { "version": "1.0.0", - "from": "unique-stream@1.0.0", + "from": "unique-stream@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-1.0.0.tgz" } } }, "glob-watcher": { "version": "0.0.6", - "from": "glob-watcher@0.0.6", + "from": "glob-watcher@>=0.0.6 <0.0.7", "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.6.tgz", "dependencies": { "gaze": { "version": "0.5.1", - "from": "gaze@0.5.1", + "from": "gaze@>=0.5.1 <0.6.0", "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.1.tgz", "dependencies": { "globule": { "version": "0.1.0", - "from": "globule@0.1.0", + "from": "globule@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", "dependencies": { "lodash": { "version": "1.0.1", - "from": "lodash@1.0.1", + "from": "lodash@>=1.0.1 <1.1.0", "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.1.tgz" }, "glob": { "version": "3.1.21", - "from": "glob@3.1.21", + "from": "glob@>=3.1.21 <3.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", "dependencies": { "graceful-fs": { "version": "1.2.3", - "from": "graceful-fs@1.2.3", + "from": "graceful-fs@>=1.2.0 <1.3.0", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" }, "inherits": { "version": "1.0.0", - "from": "inherits@1.0.0", + "from": "inherits@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz" } } }, "minimatch": { "version": "0.2.14", - "from": "minimatch@0.2.14", + "from": "minimatch@>=0.2.11 <0.3.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "dependencies": { "lru-cache": { "version": "2.5.0", - "from": "lru-cache@2.5.0", + "from": "lru-cache@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" }, "sigmund": { "version": "1.0.0", - "from": "sigmund@1.0.0", + "from": "sigmund@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" } } @@ -2066,57 +2092,57 @@ } }, "graceful-fs": { - "version": "3.0.4", - "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.4.tgz", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.4.tgz" + "version": "3.0.5", + "from": "graceful-fs@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.5.tgz" }, "lodash": { "version": "2.4.1", - "from": "lodash@2.4.1", + "from": "lodash@>=2.4.1 <3.0.0", "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz" }, "mkdirp": { "version": "0.5.0", - "from": "mkdirp@0.5.0", + "from": "mkdirp@>=0.5.0 <0.6.0", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", "dependencies": { "minimist": { "version": "0.0.8", - "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "from": "minimist@0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } }, "strip-bom": { "version": "1.0.0", - "from": "strip-bom@1.0.0", + "from": "strip-bom@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz", "dependencies": { "first-chunk-stream": { "version": "1.0.0", - "from": "first-chunk-stream@1.0.0", + "from": "first-chunk-stream@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz" }, "is-utf8": { "version": "0.2.0", - "from": "is-utf8@0.2.0", + "from": "is-utf8@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.0.tgz" } } }, "through2": { "version": "0.6.3", - "from": "https://registry.npmjs.org/through2/-/through2-0.6.3.tgz", + "from": "through2@>=0.6.1 <0.7.0", "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.3.tgz", "dependencies": { "readable-stream": { - "version": "1.0.33-1", - "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", + "version": "1.0.33", + "from": "readable-stream@>=1.0.33-1 <1.1.0-0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", "dependencies": { "core-util-is": { "version": "1.0.1", - "from": "core-util-is@1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { @@ -2126,31 +2152,36 @@ }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } }, "xtend": { "version": "4.0.0", - "from": "xtend@4.0.0", + "from": "xtend@>=4.0.0 <4.1.0-0", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" } } }, "vinyl": { - "version": "0.4.3", - "from": "vinyl@0.4.3", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.3.tgz", + "version": "0.4.6", + "from": "vinyl@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", "dependencies": { + "clone": { + "version": "0.2.0", + "from": "clone@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz" + }, "clone-stats": { "version": "0.0.1", - "from": "clone-stats@0.0.1", + "from": "clone-stats@>=0.0.1 <0.0.2", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" } } @@ -2164,17 +2195,17 @@ "dependencies": { "sax": { "version": "0.6.1", - "from": "https://registry.npmjs.org/sax/-/sax-0.6.1.tgz", + "from": "sax@>=0.6.0 <0.7.0", "resolved": "https://registry.npmjs.org/sax/-/sax-0.6.1.tgz" }, "xmlbuilder": { - "version": "2.4.4", - "from": "xmlbuilder@2.4.4", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.4.4.tgz", + "version": "2.4.5", + "from": "xmlbuilder@>=1.0.0", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.4.5.tgz", "dependencies": { "lodash-node": { "version": "2.4.1", - "from": "lodash-node@2.4.1", + "from": "lodash-node@>=2.4.1 <2.5.0", "resolved": "https://registry.npmjs.org/lodash-node/-/lodash-node-2.4.1.tgz" } } From c805f00c32117fb4c1541a060e7d1d5ba6a85c77 Mon Sep 17 00:00:00 2001 From: jbavari Date: Mon, 8 Dec 2014 19:21:42 -0700 Subject: [PATCH 0306/1100] Adding in the service/remove/add commands to assist in maintaining bower components --- lib/ionic.js | 39 ++++++++ lib/ionic/add.js | 91 +++++++++++++++++++ lib/ionic/bower.js | 29 ++++-- lib/ionic/service.js | 207 +++++++++++++++++++++++++++++++++++++++++++ package.json | 3 +- 5 files changed, 361 insertions(+), 8 deletions(-) create mode 100644 lib/ionic/add.js create mode 100644 lib/ionic/service.js diff --git a/lib/ionic.js b/lib/ionic.js index f0d8c21d70..d34d78d506 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -236,6 +236,45 @@ var TASKS = [ // '--uuid|-u': 'Mark an already uploaded version as deployed' // }, module: './ionic/app' + }, + { + title: 'service add', + name: 'service', + summary: 'Add an Ionic service package and install any required plugins', + args: { + '[options]': '', + '': 'Can be a service name or a git url' + }, + module: './ionic/service' + }, + { + title: 'add', + name: 'add', + summary: 'Add a bower component to the project', + args: { + 'component name': '' + }, + module: './ionic/add' + }, + { + title: 'remove', + name: 'remove', + summary: 'Remove a bower component from the project', + args: { + 'component name': '' + }, + module: './ionic/add', + alt: ['rm'] + }, + { + title: 'list', + name: 'list', + summary: 'List bower components from the project', + args: { + 'component name': '' + }, + module: './ionic/add', + alt: ['ls'] } ]; diff --git a/lib/ionic/add.js b/lib/ionic/add.js new file mode 100644 index 0000000000..c18a919c74 --- /dev/null +++ b/lib/ionic/add.js @@ -0,0 +1,91 @@ +require('shelljs/global') + +var Task = require('./task').Task, + IonicCordova = require('./cordova').IonicTask, + IonicStats = require('./stats').IonicStats, + argv = require('optimist').argv, + fs = require('fs'), + path = require('path'), + bower = require('./bower'); + +var IonicTask = function() {}; + +IonicTask.prototype = new Task(); + +IonicTask.prototype.installBowerComponent = function installBowerComponent(componentName) { + var bowerInstallCommand = 'bower install --save-dev ' + componentName; + + var result = exec(bowerInstallCommand); + + if (result.code != 0) { + //Error happened, report it. + var errorMessage = 'Failed to find the bower component "'.error.bold + componentName.verbose + '"'.error.bold + '.\nAre you sure it exists?'.error.bold; + this.ionic.fail(errorMessage, 'add'); + } else { + var message = 'Bower component installed - ' + componentName; + console.log(message.green) + } +} + +IonicTask.prototype.uninstallBowerComponent = function uninstallBowerComponent(componentName) { + var bowerUninstallCommand = 'bower uninstall --save-dev ' + componentName; + + var result = exec(bowerUninstallCommand); + + if (result.code != 0) { + var errorMessage = 'Failed to find the bower component "'.error.bold + componentName.verbose + '"'.error.bold + '.\nAre you sure it exists?'.error.bold; + this.ionic.fail(errorMessage, 'add') + } else { + var message = 'Bower component removed - ' + componentName; + console.log(message.red); + } +} + +IonicTask.prototype.listComponents = function listComponents() { + var bowerJson = require(path.join(process.cwd(), 'bower.json')); + console.log('Components installed: '.info) + for (var bowerCompIndex in bowerJson.devDependencies) { + console.log(bowerCompIndex.green); + } +} + +// Need to look at bower.json of package just installed and look for any cordova plugins required +// Check the directory in the projects `.bowerrc` file +// Then go to /path/to/bower/components//ionic-plugins.json - 'plugins' +// For each plugins - call 'ionic add plugin ' +IonicTask.prototype.run = function(ionic, callback) { + // console.log('running ', argv._) + this.ionic = ionic; + + IonicStats.t(); + + if (!bower.IonicBower.checkForBower()) { + this.ionic.fail(bower.IonicBower.installMessage, 'add'); + return; + } + + var action = argv._[0] + var componentName = argv._[1]; + + try { + switch (action) { + case 'add': + this.installBowerComponent(componentName); + break; + case 'remove': + case 'rm': + this.uninstallBowerComponent(componentName); + break; + case 'list': + case 'ls': + case '': + this.listComponents(); + break; + } + } catch (error) { + var errorMessage = error.message ? error.message : error; + this.ionic.fail(errorMessage, 'service') + } +} + +exports.IonicTask = IonicTask; diff --git a/lib/ionic/bower.js b/lib/ionic/bower.js index daf765bc6b..3c6657c23d 100644 --- a/lib/ionic/bower.js +++ b/lib/ionic/bower.js @@ -1,3 +1,5 @@ +require('shelljs/global') + var fs = require('fs'), path = require('path'), colors = require('colors'); @@ -6,7 +8,7 @@ exports.IonicBower = { setIonicVersion: function(newVersion) { var bowerData = this.getData(); - if(!bowerData.devDependencies) bowerData.devDependencies = {}; + if (!bowerData.devDependencies) bowerData.devDependencies = {}; bowerData.devDependencies.ionic = 'driftyco/ionic-bower#' + newVersion; this.saveData(bowerData); }, @@ -21,10 +23,10 @@ exports.IonicBower = { var bowerFilePath = path.resolve('bower.json'); try { - if( fs.existsSync(bowerFilePath) ) { + if (fs.existsSync(bowerFilePath)) { return require(bowerFilePath); } - } catch(e){} + } catch (e) {} return { "name": "HelloIonic", @@ -35,10 +37,23 @@ exports.IonicBower = { saveData: function(bowerData) { try { var bowerFilePath = path.resolve('bower.json'); - fs.writeFileSync( bowerFilePath, JSON.stringify(bowerData, null, 2) ); - } catch(e) { - console.log( ('Error saving ' + bowerFilePath + ': ' + e).error.bold ); + fs.writeFileSync(bowerFilePath, JSON.stringify(bowerData, null, 2)); + } catch (e) { + console.log(('Error saving ' + bowerFilePath + ': ' + e).error.bold); } - } + }, + + checkForBower: function checkForBower() { + var installed = false; + try { + var result = exec('bower -v', {silent: true}); + if (result.output.indexOf('command not found') == -1 && result.output.indexOf('not recognized') == -1) { + installed = true; + } + } catch (ex) { } + return installed; + }, + + installMessage: 'You must have bower installed to continue. \nType `npm install -g bower`' }; diff --git a/lib/ionic/service.js b/lib/ionic/service.js new file mode 100644 index 0000000000..47e95d82f1 --- /dev/null +++ b/lib/ionic/service.js @@ -0,0 +1,207 @@ +require('shelljs/global') + +var Task = require('./task').Task, + IonicCordova = require('./cordova').IonicTask, + IonicStats = require('./stats').IonicStats, + argv = require('optimist').argv, + fs = require('fs'), + path = require('path') + _ = require('underscore'), + bower = require('./bower'); + +var IonicTask = function() {}; + +IonicTask.prototype = new Task(); + +IonicTask.prototype.readIonicProjectJson = function readIonicProjectJson() { + var ionicProjectFile = path.join(process.cwd(), 'ionic.project'); + var ionicProjectJson = null; + try { + var content = fs.readFileSync(ionicProjectFile, 'utf8'); + ionicProjectJson = JSON.parse(content); + } catch (ex) { } + + return ionicProjectJson; +} + +IonicTask.prototype.addServiceToIonicJson = function addServiceToIonicJson(serviceName) { + var ionicProjectFile = path.join(process.cwd(), 'ionic.project'); + + try { + var ionicProjectJson = this.readIonicProjectJson() || {}; + + if (!ionicProjectJson.services) { + ionicProjectJson.services = []; + } + + var existingProject = _.findWhere(ionicProjectJson.services, {name: serviceName}); + if (typeof existingProject != 'undefined') { + return; + } + + ionicProjectJson.services.push({name: serviceName}); + fs.writeFileSync(ionicProjectFile, JSON.stringify(ionicProjectJson, null, 2), 'utf8'); + + } catch (ex) { + this.ionic.fail('Failed to update the ionic.project settings for the service', 'service'); + } +} + +IonicTask.prototype.installBowerComponent = function installBowerComponent(serviceName) { + // console.log('We are here now.'); + var bowerInstallCommand = 'bower link ionic-service-' + serviceName; + // var bowerInstallCommand = 'bower install ionic-service-' + serviceName; + + var result = exec(bowerInstallCommand); + + if (result.code != 0) { + //Error happened, report it. + var errorMessage = 'Failed to find the service "'.error.bold + serviceName.verbose + '"'.error.bold + '.\nAre you sure it exists?'.error.bold; + this.ionic.fail(errorMessage, 'service'); + } else { + this.addServiceToIonicJson(serviceName); + } +} + +IonicTask.prototype.uninstallBowerComponent = function uninstallBowerComponent(serviceName) { + var bowerUninstallCommand = 'bower unlink ionic-service-' + serviceName; + + var result = exec(bowerUninstallCommand); + + if (result.code != 0) { + var errorMessage = 'Failed to find the service "'.error.bold + serviceName.verbose + '"'.error.bold + '.\nAre you sure it exists?'.error.bold; + this.ionic.fail(errorMessage, 'service') + } else { + // console.log('Uninstalled Ionic service', serviceName); + } +} + +IonicTask.prototype.getBowerComponentsLocation = function getBowerComponentsLocation() { + var bowerRcFileLocation = path.join(process.cwd(), '.bowerrc'); + // console.log('location bowerrc: ' , bowerRcFileLocation) + var bowerRc = null; + + try { + var content = fs.readFileSync(bowerRcFileLocation, 'utf8'); + bowerRc = JSON.parse(content); + // console.log('bowerRc contents: ', bowerRc) + } catch (ex) { } + + var directory = 'www/lib'; //Default directory + + if (bowerRc && bowerRc.directory) { + directory = bowerRc.directory; + } + + return directory; +} + +IonicTask.prototype.getBowerJson = function getBowerJson(directory, serviceName) { + var bowerJsonLocation = path.join(process.cwd(), directory, 'ionic-service-' + serviceName, 'ionic-plugins.json'); + var packageBowerJson = require(bowerJsonLocation); + return packageBowerJson; +} + +IonicTask.prototype.installBowerPlugins = function installBowerPlugins(directory, serviceName) { + // var bowerJsonLocation = process.cwd() + '/' + directory + '/ionic-service-' + serviceName + '/bower.json'; + // var packageBowerJson = require(bowerJsonLocation); + var packageBowerJson = this.getBowerJson(directory, serviceName); + + // console.log('bowerjson = ', packageBowerJson.plugins); + // console.log('ionic - ', IonicCordova); + _.each(packageBowerJson.plugins, function(plugin) { + // var ic = new IonicCordova(); + // ic.run('ionic plugin add ' + plugin); + console.log('Installing cordova plugin - ' + plugin.name + ' (' + plugin.id + ')'); + var installPluginCmd = 'ionic plugin add ' + plugin.uri; + console.log(installPluginCmd); + var pluginInstallResult = exec(installPluginCmd); + + if (pluginInstallResult.code != 0) { + var errorMessage = 'Failed to find the plugin "'.error.bold + plugin.name.verbose + '"'.error.bold + '.'.error.bold; + this.ionic.fail(errorMessage, 'service'); + } + // console.log(pluginInstallResult); + }) +} + +IonicTask.prototype.uninstallBowerPlugins = function uninstallBowerPlugins(bowerJson) { + _.each(bowerJson.plugins, function(plugin) { + console.log('Uninstalling cordova plugin - ' + plugin.name); + var uninstallPluginCmd = 'ionic plugin rm ' + plugin.id; + console.log(uninstallPluginCmd); + var pluginRemoveResult = exec(uninstallPluginCmd) + + if (pluginRemoveResult.code != 0) { + var errorMessage = 'Failed to find the plugin to remove "'.error.bold + plugin.name.verbose + '"'.error.bold + '.'.error.bold; + this.ionic.fail(errorMessage, 'service'); + } + }) +} + +IonicTask.prototype.addService = function addService(serviceName) { + this.installBowerComponent(serviceName); + + var directory = this.getBowerComponentsLocation(); + // console.log('Directory we are searching for bower.json - ', directory); + + console.log('Checking for any plugins required by service package'); + + this.installBowerPlugins(directory, serviceName); + console.log('ionic service add completed'); +} + +IonicTask.prototype.removeService = function removeService(serviceName) { + var directory = this.getBowerComponentsLocation(); + var packageBowerJson = this.getBowerJson(directory, serviceName); + + this.uninstallBowerComponent(serviceName); + + this.uninstallBowerPlugins(packageBowerJson); + +} + +IonicTask.prototype.listServices = function listServices() { + // var directory = this.getBowerComponentsLocation(); + var bowerJson = require(process.cwd() + '/bower.json'); + // console.log(bowerJson); + +} + +// Need to look at bower.json of package just installed and look for any cordova plugins required +// Check the directory in the projects `.bowerrc` file +// Then go to /path/to/bower/components//ionic-plugins.json - 'plugins' +// For each plugins - call 'ionic add plugin ' +IonicTask.prototype.run = function run(ionic, callback) { + this.ionic = ionic; + + IonicStats.t(); + + + if (!bower.IonicBower.checkForBower()) { + this.ionic.fail(bower.IonicBower.installMessage, 'service'); + return; + } + + var action = argv._[1] + var serviceName = argv._[2]; + + try { + switch (action) { + case 'add': + this.addService(serviceName); + break; + case 'remove': + this.removeService(serviceName); + break; + case 'list': + this.listServices(); + break; + } + } catch (error) { + var errorMessage = error.message ? error.message : error; + this.ionic.fail(errorMessage, 'service') + } +} + +exports.IonicTask = IonicTask; diff --git a/package.json b/package.json index 5dfa933b4c..685ae01890 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "tiny-lr-fork": "0.0.5", "unzip": "0.1.9", "vinyl-fs": "0.3.7", - "xml2js": "0.4.4" + "xml2js": "0.4.4", + "underscore": "~1.7.0" } } From 562f71a080eed2bc1629a40fd14f9c14884d5a4e Mon Sep 17 00:00:00 2001 From: jbavari Date: Mon, 8 Dec 2014 19:21:42 -0700 Subject: [PATCH 0307/1100] Adding in the service/remove/add commands to assist in maintaining bower components --- lib/ionic.js | 39 ++++++++ lib/ionic/add.js | 91 +++++++++++++++++++ lib/ionic/bower.js | 29 ++++-- lib/ionic/service.js | 207 +++++++++++++++++++++++++++++++++++++++++++ npm-shrinkwrap.json | 5 ++ package.json | 3 +- 6 files changed, 366 insertions(+), 8 deletions(-) create mode 100644 lib/ionic/add.js create mode 100644 lib/ionic/service.js diff --git a/lib/ionic.js b/lib/ionic.js index f0d8c21d70..d34d78d506 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -236,6 +236,45 @@ var TASKS = [ // '--uuid|-u': 'Mark an already uploaded version as deployed' // }, module: './ionic/app' + }, + { + title: 'service add', + name: 'service', + summary: 'Add an Ionic service package and install any required plugins', + args: { + '[options]': '', + '': 'Can be a service name or a git url' + }, + module: './ionic/service' + }, + { + title: 'add', + name: 'add', + summary: 'Add a bower component to the project', + args: { + 'component name': '' + }, + module: './ionic/add' + }, + { + title: 'remove', + name: 'remove', + summary: 'Remove a bower component from the project', + args: { + 'component name': '' + }, + module: './ionic/add', + alt: ['rm'] + }, + { + title: 'list', + name: 'list', + summary: 'List bower components from the project', + args: { + 'component name': '' + }, + module: './ionic/add', + alt: ['ls'] } ]; diff --git a/lib/ionic/add.js b/lib/ionic/add.js new file mode 100644 index 0000000000..c18a919c74 --- /dev/null +++ b/lib/ionic/add.js @@ -0,0 +1,91 @@ +require('shelljs/global') + +var Task = require('./task').Task, + IonicCordova = require('./cordova').IonicTask, + IonicStats = require('./stats').IonicStats, + argv = require('optimist').argv, + fs = require('fs'), + path = require('path'), + bower = require('./bower'); + +var IonicTask = function() {}; + +IonicTask.prototype = new Task(); + +IonicTask.prototype.installBowerComponent = function installBowerComponent(componentName) { + var bowerInstallCommand = 'bower install --save-dev ' + componentName; + + var result = exec(bowerInstallCommand); + + if (result.code != 0) { + //Error happened, report it. + var errorMessage = 'Failed to find the bower component "'.error.bold + componentName.verbose + '"'.error.bold + '.\nAre you sure it exists?'.error.bold; + this.ionic.fail(errorMessage, 'add'); + } else { + var message = 'Bower component installed - ' + componentName; + console.log(message.green) + } +} + +IonicTask.prototype.uninstallBowerComponent = function uninstallBowerComponent(componentName) { + var bowerUninstallCommand = 'bower uninstall --save-dev ' + componentName; + + var result = exec(bowerUninstallCommand); + + if (result.code != 0) { + var errorMessage = 'Failed to find the bower component "'.error.bold + componentName.verbose + '"'.error.bold + '.\nAre you sure it exists?'.error.bold; + this.ionic.fail(errorMessage, 'add') + } else { + var message = 'Bower component removed - ' + componentName; + console.log(message.red); + } +} + +IonicTask.prototype.listComponents = function listComponents() { + var bowerJson = require(path.join(process.cwd(), 'bower.json')); + console.log('Components installed: '.info) + for (var bowerCompIndex in bowerJson.devDependencies) { + console.log(bowerCompIndex.green); + } +} + +// Need to look at bower.json of package just installed and look for any cordova plugins required +// Check the directory in the projects `.bowerrc` file +// Then go to /path/to/bower/components//ionic-plugins.json - 'plugins' +// For each plugins - call 'ionic add plugin ' +IonicTask.prototype.run = function(ionic, callback) { + // console.log('running ', argv._) + this.ionic = ionic; + + IonicStats.t(); + + if (!bower.IonicBower.checkForBower()) { + this.ionic.fail(bower.IonicBower.installMessage, 'add'); + return; + } + + var action = argv._[0] + var componentName = argv._[1]; + + try { + switch (action) { + case 'add': + this.installBowerComponent(componentName); + break; + case 'remove': + case 'rm': + this.uninstallBowerComponent(componentName); + break; + case 'list': + case 'ls': + case '': + this.listComponents(); + break; + } + } catch (error) { + var errorMessage = error.message ? error.message : error; + this.ionic.fail(errorMessage, 'service') + } +} + +exports.IonicTask = IonicTask; diff --git a/lib/ionic/bower.js b/lib/ionic/bower.js index daf765bc6b..3c6657c23d 100644 --- a/lib/ionic/bower.js +++ b/lib/ionic/bower.js @@ -1,3 +1,5 @@ +require('shelljs/global') + var fs = require('fs'), path = require('path'), colors = require('colors'); @@ -6,7 +8,7 @@ exports.IonicBower = { setIonicVersion: function(newVersion) { var bowerData = this.getData(); - if(!bowerData.devDependencies) bowerData.devDependencies = {}; + if (!bowerData.devDependencies) bowerData.devDependencies = {}; bowerData.devDependencies.ionic = 'driftyco/ionic-bower#' + newVersion; this.saveData(bowerData); }, @@ -21,10 +23,10 @@ exports.IonicBower = { var bowerFilePath = path.resolve('bower.json'); try { - if( fs.existsSync(bowerFilePath) ) { + if (fs.existsSync(bowerFilePath)) { return require(bowerFilePath); } - } catch(e){} + } catch (e) {} return { "name": "HelloIonic", @@ -35,10 +37,23 @@ exports.IonicBower = { saveData: function(bowerData) { try { var bowerFilePath = path.resolve('bower.json'); - fs.writeFileSync( bowerFilePath, JSON.stringify(bowerData, null, 2) ); - } catch(e) { - console.log( ('Error saving ' + bowerFilePath + ': ' + e).error.bold ); + fs.writeFileSync(bowerFilePath, JSON.stringify(bowerData, null, 2)); + } catch (e) { + console.log(('Error saving ' + bowerFilePath + ': ' + e).error.bold); } - } + }, + + checkForBower: function checkForBower() { + var installed = false; + try { + var result = exec('bower -v', {silent: true}); + if (result.output.indexOf('command not found') == -1 && result.output.indexOf('not recognized') == -1) { + installed = true; + } + } catch (ex) { } + return installed; + }, + + installMessage: 'You must have bower installed to continue. \nType `npm install -g bower`' }; diff --git a/lib/ionic/service.js b/lib/ionic/service.js new file mode 100644 index 0000000000..47e95d82f1 --- /dev/null +++ b/lib/ionic/service.js @@ -0,0 +1,207 @@ +require('shelljs/global') + +var Task = require('./task').Task, + IonicCordova = require('./cordova').IonicTask, + IonicStats = require('./stats').IonicStats, + argv = require('optimist').argv, + fs = require('fs'), + path = require('path') + _ = require('underscore'), + bower = require('./bower'); + +var IonicTask = function() {}; + +IonicTask.prototype = new Task(); + +IonicTask.prototype.readIonicProjectJson = function readIonicProjectJson() { + var ionicProjectFile = path.join(process.cwd(), 'ionic.project'); + var ionicProjectJson = null; + try { + var content = fs.readFileSync(ionicProjectFile, 'utf8'); + ionicProjectJson = JSON.parse(content); + } catch (ex) { } + + return ionicProjectJson; +} + +IonicTask.prototype.addServiceToIonicJson = function addServiceToIonicJson(serviceName) { + var ionicProjectFile = path.join(process.cwd(), 'ionic.project'); + + try { + var ionicProjectJson = this.readIonicProjectJson() || {}; + + if (!ionicProjectJson.services) { + ionicProjectJson.services = []; + } + + var existingProject = _.findWhere(ionicProjectJson.services, {name: serviceName}); + if (typeof existingProject != 'undefined') { + return; + } + + ionicProjectJson.services.push({name: serviceName}); + fs.writeFileSync(ionicProjectFile, JSON.stringify(ionicProjectJson, null, 2), 'utf8'); + + } catch (ex) { + this.ionic.fail('Failed to update the ionic.project settings for the service', 'service'); + } +} + +IonicTask.prototype.installBowerComponent = function installBowerComponent(serviceName) { + // console.log('We are here now.'); + var bowerInstallCommand = 'bower link ionic-service-' + serviceName; + // var bowerInstallCommand = 'bower install ionic-service-' + serviceName; + + var result = exec(bowerInstallCommand); + + if (result.code != 0) { + //Error happened, report it. + var errorMessage = 'Failed to find the service "'.error.bold + serviceName.verbose + '"'.error.bold + '.\nAre you sure it exists?'.error.bold; + this.ionic.fail(errorMessage, 'service'); + } else { + this.addServiceToIonicJson(serviceName); + } +} + +IonicTask.prototype.uninstallBowerComponent = function uninstallBowerComponent(serviceName) { + var bowerUninstallCommand = 'bower unlink ionic-service-' + serviceName; + + var result = exec(bowerUninstallCommand); + + if (result.code != 0) { + var errorMessage = 'Failed to find the service "'.error.bold + serviceName.verbose + '"'.error.bold + '.\nAre you sure it exists?'.error.bold; + this.ionic.fail(errorMessage, 'service') + } else { + // console.log('Uninstalled Ionic service', serviceName); + } +} + +IonicTask.prototype.getBowerComponentsLocation = function getBowerComponentsLocation() { + var bowerRcFileLocation = path.join(process.cwd(), '.bowerrc'); + // console.log('location bowerrc: ' , bowerRcFileLocation) + var bowerRc = null; + + try { + var content = fs.readFileSync(bowerRcFileLocation, 'utf8'); + bowerRc = JSON.parse(content); + // console.log('bowerRc contents: ', bowerRc) + } catch (ex) { } + + var directory = 'www/lib'; //Default directory + + if (bowerRc && bowerRc.directory) { + directory = bowerRc.directory; + } + + return directory; +} + +IonicTask.prototype.getBowerJson = function getBowerJson(directory, serviceName) { + var bowerJsonLocation = path.join(process.cwd(), directory, 'ionic-service-' + serviceName, 'ionic-plugins.json'); + var packageBowerJson = require(bowerJsonLocation); + return packageBowerJson; +} + +IonicTask.prototype.installBowerPlugins = function installBowerPlugins(directory, serviceName) { + // var bowerJsonLocation = process.cwd() + '/' + directory + '/ionic-service-' + serviceName + '/bower.json'; + // var packageBowerJson = require(bowerJsonLocation); + var packageBowerJson = this.getBowerJson(directory, serviceName); + + // console.log('bowerjson = ', packageBowerJson.plugins); + // console.log('ionic - ', IonicCordova); + _.each(packageBowerJson.plugins, function(plugin) { + // var ic = new IonicCordova(); + // ic.run('ionic plugin add ' + plugin); + console.log('Installing cordova plugin - ' + plugin.name + ' (' + plugin.id + ')'); + var installPluginCmd = 'ionic plugin add ' + plugin.uri; + console.log(installPluginCmd); + var pluginInstallResult = exec(installPluginCmd); + + if (pluginInstallResult.code != 0) { + var errorMessage = 'Failed to find the plugin "'.error.bold + plugin.name.verbose + '"'.error.bold + '.'.error.bold; + this.ionic.fail(errorMessage, 'service'); + } + // console.log(pluginInstallResult); + }) +} + +IonicTask.prototype.uninstallBowerPlugins = function uninstallBowerPlugins(bowerJson) { + _.each(bowerJson.plugins, function(plugin) { + console.log('Uninstalling cordova plugin - ' + plugin.name); + var uninstallPluginCmd = 'ionic plugin rm ' + plugin.id; + console.log(uninstallPluginCmd); + var pluginRemoveResult = exec(uninstallPluginCmd) + + if (pluginRemoveResult.code != 0) { + var errorMessage = 'Failed to find the plugin to remove "'.error.bold + plugin.name.verbose + '"'.error.bold + '.'.error.bold; + this.ionic.fail(errorMessage, 'service'); + } + }) +} + +IonicTask.prototype.addService = function addService(serviceName) { + this.installBowerComponent(serviceName); + + var directory = this.getBowerComponentsLocation(); + // console.log('Directory we are searching for bower.json - ', directory); + + console.log('Checking for any plugins required by service package'); + + this.installBowerPlugins(directory, serviceName); + console.log('ionic service add completed'); +} + +IonicTask.prototype.removeService = function removeService(serviceName) { + var directory = this.getBowerComponentsLocation(); + var packageBowerJson = this.getBowerJson(directory, serviceName); + + this.uninstallBowerComponent(serviceName); + + this.uninstallBowerPlugins(packageBowerJson); + +} + +IonicTask.prototype.listServices = function listServices() { + // var directory = this.getBowerComponentsLocation(); + var bowerJson = require(process.cwd() + '/bower.json'); + // console.log(bowerJson); + +} + +// Need to look at bower.json of package just installed and look for any cordova plugins required +// Check the directory in the projects `.bowerrc` file +// Then go to /path/to/bower/components//ionic-plugins.json - 'plugins' +// For each plugins - call 'ionic add plugin ' +IonicTask.prototype.run = function run(ionic, callback) { + this.ionic = ionic; + + IonicStats.t(); + + + if (!bower.IonicBower.checkForBower()) { + this.ionic.fail(bower.IonicBower.installMessage, 'service'); + return; + } + + var action = argv._[1] + var serviceName = argv._[2]; + + try { + switch (action) { + case 'add': + this.addService(serviceName); + break; + case 'remove': + this.removeService(serviceName); + break; + case 'list': + this.listServices(); + break; + } + } catch (error) { + var errorMessage = error.message ? error.message : error; + this.ionic.fail(errorMessage, 'service') + } +} + +exports.IonicTask = IonicTask; diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index f36e3667be..1100c36b64 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1811,6 +1811,11 @@ } } }, + "underscore": { + "version": "1.7.0", + "from": "underscore@>=1.7.0 <1.8.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + }, "unzip": { "version": "0.1.9", "from": "unzip@0.1.9", diff --git a/package.json b/package.json index 5dfa933b4c..685ae01890 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "tiny-lr-fork": "0.0.5", "unzip": "0.1.9", "vinyl-fs": "0.3.7", - "xml2js": "0.4.4" + "xml2js": "0.4.4", + "underscore": "~1.7.0" } } From bb2f5ba9f076a2e4c6222015d88ee5b522962b89 Mon Sep 17 00:00:00 2001 From: Mehdy Dara Date: Tue, 9 Dec 2014 17:06:47 +0100 Subject: [PATCH 0308/1100] spawn use option "stdio" inheritance * http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options * Less code * Color enabled --- lib/ionic/serve.js | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 55a74e9687..0c18d946a7 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -133,21 +133,10 @@ IonicTask.prototype.start = function(ionic) { // gulpStartupTasks should be an array of tasks set in the project config // watchSass is for backwards compatible sass: true project config if((this.gulpStartupTasks && this.gulpStartupTasks.length) || this.watchSass) { - var spawn = require('cross-spawn').spawn; var tasks = this.gulpStartupTasks || ['sass','watch']; console.log('Gulp startup tasks:'.green.bold, tasks); - var childProcess = spawn('gulp', tasks); - - childProcess.stdout.on('data', function (data) { - process.stdout.write(data); - }); - - childProcess.stderr.on('data', function (data) { - if(data) { - process.stderr.write(data.toString().yellow); - } - }); + require('cross-spawn').spawn('gulp', tasks, { stdio: 'inherit' }); } if(this.runLivereload) { From f9712b3be0abbb02a6585ad1f5b1cf4f201ae814 Mon Sep 17 00:00:00 2001 From: jbavari Date: Tue, 9 Dec 2014 11:12:07 -0700 Subject: [PATCH 0309/1100] Adding in the environment variable http_proxy for npm defaults, along with the PROXY env variable we suggested --- lib/ionic.js | 2 +- lib/ionic/lib.js | 2 +- lib/ionic/login.js | 2 +- lib/ionic/package.js | 2 +- lib/ionic/start.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/ionic.js b/lib/ionic.js index d34d78d506..0bd4be6f11 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -540,7 +540,7 @@ Ionic = { return; } - var proxy = process.env.PROXY || null; + var proxy = process.env.PROXY || process.env.http_proxy || null; var request = require('request'); request({ url: 'http://registry.npmjs.org/ionic/latest', proxy: proxy }, function(err, res, body) { try { diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index 460b657ede..e64ff7c9bf 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -80,7 +80,7 @@ IonicTask.prototype.getVersionData = function(version) { url += '/' + version + '/version.json'; } - var proxy = process.env.PROXY || null; + var proxy = process.env.PROXY || process.env.http_proxy || null; request({ url: url, proxy: proxy }, function(err, res, body) { try { if(err || !res || !body) { diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 9b29a04371..7710f39fa6 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -104,7 +104,7 @@ IonicTask.prototype.requestLogIn = function(ionic, callback, saveCookies) { username: self.email.toString().toLowerCase(), password: self.password }, - proxy: process.env.PROXY || null + proxy: process.env.PROXY || process.env.http_proxy || null }, function (err, response, body) { if(err) { diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 55ae310364..1234dc80b7 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -169,7 +169,7 @@ IonicTask.prototype.loadAppSigning = function(callback) { }).join("; "), cck: cck }, - proxy: process.env.PROXY || null + proxy: process.env.PROXY || process.env.http_proxy || null }; request(options, function(err, response, body) { diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 0b3b787e24..3e43ea68ac 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -247,7 +247,7 @@ IonicTask.prototype.fetchCodepen = function() { var qCSS = Q.defer(); var qJS = Q.defer(); - var proxy = process.env.PROXY || null; + var proxy = process.env.PROXY || process.env.http_proxy || null; request({ url: codepenUrl + '.html', proxy: proxy }, function(err, res, html) { if(!err && res && res.statusCode === 200) { From 0835f23c99b9ae562d604208a863173bbed18a34 Mon Sep 17 00:00:00 2001 From: jbavari Date: Tue, 9 Dec 2014 12:11:07 -0700 Subject: [PATCH 0310/1100] Adding documenation to README about working around proxies, as well as adding in the additional http_proxy in lib/ionic.js --- README.md | 17 +++++++++++++++++ lib/ionic.js | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f8d807c0ca..17bdcb1962 100644 --- a/README.md +++ b/README.md @@ -201,6 +201,23 @@ Ionic uses Cordova underneath, so you can also substitute Cordova commands to pr *Note: we occasionally send anonymous usage statistics to the Ionic team to make the tool better.* +## Working around proxies + +If you have a proxy you need to get around, you can pass that proxy with the default `http_proxy` [node environment variable](https://www.npmjs.org/doc/misc/npm-config.html#proxy) or an environment variable `proxy`. + +A few ways to set up and use the environment variable: + +```bash +export http_proxy=internal.proxy.com +# Or +export PROXY=internal.proxy.com + +ionic start my_app + +# Additionally, pass in line +PROXY=internal.proxy.com ionic start my_app +``` + ## Using Sass diff --git a/lib/ionic.js b/lib/ionic.js index 0bd4be6f11..ccc80aeae2 100644 --- a/lib/ionic.js +++ b/lib/ionic.js @@ -648,7 +648,7 @@ Ionic = { readStream.pipe(writeStream); }; - var proxy = process.env.PROXY || null; + var proxy = process.env.PROXY || process.env.http_proxy || null; var request = require('request'); request({ url: archiveUrl, rejectUnauthorized: false, encoding: null, proxy: proxy }, function(err, res, body) { if(err) { From 78e4117ae1c145ed3d8aecef56f147417d0926d7 Mon Sep 17 00:00:00 2001 From: jbavari Date: Tue, 9 Dec 2014 20:35:20 -0700 Subject: [PATCH 0311/1100] Refactoring changes to use the Ionic.fetchArchive instead of using the git clone and curl commands --- lib/ionic/CrosswalkNotes.md | 12 ++ lib/ionic/browser.js | 218 ++++++++++++++++++++++-------------- 2 files changed, 149 insertions(+), 81 deletions(-) diff --git a/lib/ionic/CrosswalkNotes.md b/lib/ionic/CrosswalkNotes.md index 089b730a1a..83af78b830 100644 --- a/lib/ionic/CrosswalkNotes.md +++ b/lib/ionic/CrosswalkNotes.md @@ -16,3 +16,15 @@ A problem occurred evaluating script. > No installed build tools found. Please install the Android build tools version 19.1.0 or higher. Had to bump the minversionnumber in platform/android/AndroidManifest.xml to 14 from 10. + + + + +## Roadmap for Crosswalk integration + +Cordova release in Jan 2015 is pushing for pluggable web views + +In the meantime, do we go ahead and release now as a beta? + +When they do release, how do we integrate with those changes? + diff --git a/lib/ionic/browser.js b/lib/ionic/browser.js index 510d8b57e8..46d65fe2b8 100644 --- a/lib/ionic/browser.js +++ b/lib/ionic/browser.js @@ -18,22 +18,29 @@ //Install the crosswalk latest files // cordova platform remove android // cordova platform add android@3.5 -var path = require('path'), - shelljs = require('shelljs'); + +var fs = require('fs'), + path = require('path'), + argv = require('optimist').argv, + request = require('request'), + Q = require('q'), + shelljs = require('shelljs'), + Task = require('./task').Task, + proxyMiddleware = require('proxy-middleware'), + unzip = require('unzip'), + Ionic = require('../ionic').Ionic, + IonicStats = require('./stats').IonicStats; + +var IonicTask = function() {}; + +IonicTask.prototype = new Task(); + + var XWALK_LIBRARY_PATH = path.join(process.cwd(), 'tmp', 'xwalk'); var ARM_DOWNLOAD_URL = "https://download.01.org/crosswalk/releases/crosswalk/android/canary/8.37.189.0/arm/crosswalk-webview-8.37.189.0-arm.zip"; var X86_DOWNLOAD_URL = "https://download.01.org/crosswalk/releases/crosswalk/android/canary/8.37.189.0/x86/crosswalk-webview-8.37.189.0-x86.zip"; -function downloadFiles() { - var tempDir = '../../tmp/crosswalk-engine'; - //Download ARM - //unzip ARM - //rm zip - var libCrossWalk = path.join(process.cwd(), 'libs', 'xwalk_core_library'); - -} - function updateCrosswalkProject() { // prepare xwalk_core_library if(fs.existsSync(XWALK_LIBRARY_PATH)) { @@ -44,34 +51,35 @@ function updateCrosswalkProject() { } } -var fs = require('fs'), - path = require('path'), - argv = require('optimist').argv, - shelljs = require('shelljs'), - Task = require('./task').Task, - proxyMiddleware = require('proxy-middleware'), - IonicStats = require('./stats').IonicStats; +IonicTask.prototype.getCordovaCrosswalkEngine = function getCordovaCrosswalkEngine() { + var q = Q.defer(); -var IonicTask = function() {}; - -IonicTask.prototype = new Task(); - -IonicTask.prototype.getCrosswalkEngine = function getCrosswalkEngine() { if(!fs.existsSync(path.join(process.cwd(), 'engine'))) { shelljs.mkdir(path.join(process.cwd(), 'engine')); } if(fs.existsSync(path.join(process.cwd(), 'engine', 'cordova-crosswalk-engine'))) { - return; //It exists, nothing to do here. + q.resolve(); + return q.promise; } - shelljs.cd(path.join(process.cwd(), 'engine')); - var downloadResult = shelljs.exec('git clone --depth=1 git@github.com:MobileChromeApps/cordova-crosswalk-engine.git'); - shelljs.cd('..') + // shelljs.cd(path.join(process.cwd(), 'engine')); + // var downloadResult = shelljs.exec('git clone --depth=1 git@github.com:MobileChromeApps/cordova-crosswalk-engine.git'); + var downloadUrl = 'https://github.com/driftyco/cordova-crosswalk-engine/archive/v0.8.zip'; - if(downloadResult.code == 1) { - //LOG ERROR - } + var tempZipFilePath = path.join(process.cwd(), 'engine', 'cordova-crosswalk-engine.zip'); + var zipOutPath = path.join(process.cwd(), 'engine'); + + Ionic.fetchArchive(zipOutPath, downloadUrl) + .then(function(data) { + console.log('downloaded') + q.resolve(); + }, function(error) { + console.log('failed to download engine - ', error); + q.reject(); + }) + + return q.promise; } IonicTask.prototype.getAndroidRuntimes = function getAndroidRuntimes() { @@ -83,78 +91,114 @@ IonicTask.prototype.getAndroidRuntimes = function getAndroidRuntimes() { } } -// download() { -// TMPDIR=cordova-crosswalk-engine-$$ -// pushd $TMPDIR > /dev/null -// echo "Fetching $1..." -// curl -# $1 -o library.zip -// unzip -q library.zip -// rm library.zip -// PACKAGENAME=$(ls|head -n 1) -// echo "Installing $PACKAGENAME into xwalk_core_library..." -// cp -a $PACKAGENAME/* ../libs/xwalk_core_library -// popd > /dev/null -// rm -r $TMPDIR -// } - -// download $ARM_DOWNLOAD -// download $X86_DOWNLOAD - -function downloadCrosswalkEngine(architecture, version) { - var command = 'curl -# https://download.01.org/crosswalk/releases/crosswalk/android/stable/' + - version + '/' + architecture + '/crosswalk-webview-' + version + '-' + architecture + '.zip -o library.zip'; +function downloadCrosswalkWebview(architecture, version) { + // var command = 'curl -# https://download.01.org/crosswalk/releases/crosswalk/android/stable/' + + // version + '/' + architecture + '/crosswalk-webview-' + version + '-' + architecture + '.zip -o library.zip'; + var q = Q.defer(); + + var downloadUrl = 'https://download.01.org/crosswalk/releases/crosswalk/android/stable/' + + version + '/' + architecture + '/crosswalk-webview-' + version + '-' + architecture + '.zip'; + + var tempZipFilePath = path.join(process.cwd(), 'engine', 'xwalk-webviews', architecture); //Ensure xwalk-webviews folder exists if (!fs.existsSync(path.join(process.cwd(), 'engine', 'xwalk-webviews'))) { shelljs.mkdir(path.join(process.cwd(), 'engine', 'xwalk-webviews')); } - shelljs.cd(path.join(process.cwd(), 'engine', 'xwalk-webviews')); + Ionic.fetchArchive(tempZipFilePath, downloadUrl) + .then(function(data) { + console.log('xwalk download good - ', data); + + //Need to go copy to android directory. + var fileName = fs.readdirSync(tempZipFilePath); + console.log('fileName for dirs ', fileName) + + var copySource = path.join(tempZipFilePath, fileName[0], '*'); + console.log('copy source - ', copySource) + + var copyDestination = path.join(process.cwd(), 'engine', 'cordova-crosswalk-engine-0.8', 'libs', 'xwalk_core_library', '/') + console.log('copyDestination - ', copyDestination); - console.log('Downloading the crosswalk webview for ' + architecture + ', version ' + version); - var resultCurl = shelljs.exec(command); - //TODO: Check for resultCurl + var cpXwalkLibsCmd = 'cp -r ' + copySource + ' ' + copyDestination; + var cpResult = shelljs.exec(cpXwalkLibsCmd) - console.log('Unzipping webview binaries') - var unzipCommand = 'unzip -q library.zip -d unzipped'; - var resultUnzip = shelljs.exec(unzipCommand); - var fileName = fs.readdirSync('unzipped')[0]; + console.log('Result from cp - ' , cpResult) + // var archDirectory = fs.readdirSync(path.join(fileName[0])); + // console.log('archDirectory - ', archDirectory); - console.log('file name: ', fileName); + q.resolve(data); + }, function(error) { + console.log('xwalk download failed - ', error) + q.reject(error); + }) - shelljs.cd('../../'); //Back at process.cwd() in ionic project root + // shelljs.cd(path.join(process.cwd(), 'engine', 'xwalk-webviews')); - var copySource = path.join(process.cwd(), 'engine', 'xwalk-webviews', 'unzipped', fileName, '*'); - console.log('copy source ', copySource) - var copyDestination = path.join(process.cwd(), 'engine', 'cordova-crosswalk-engine', 'libs', 'xwalk_core_library', '/'); - var cpArmLibsCommand = 'cp -r ' + copySource + ' ' + copyDestination; - console.log('command: ', cpArmLibsCommand); - shelljs.exec(cpArmLibsCommand); + // console.log('Downloading the crosswalk webview for ' + architecture + ', version ' + version); + // var resultCurl = shelljs.exec(command); - shelljs.rm('-rf', path.join(process.cwd(), 'engine', 'xwalk-webviews', 'unzipped')); + // console.log('Unzipping webview binaries') + // var unzipCommand = 'unzip -q library.zip -d unzipped'; + // var resultUnzip = shelljs.exec(unzipCommand); + // var fileName = fs.readdirSync('unzipped')[0]; + // console.log('file name: ', fileName); + + // shelljs.cd('../../'); //Back at process.cwd() in ionic project root + + // var copySource = path.join(process.cwd(), 'engine', 'xwalk-webviews', 'unzipped', fileName, '*'); + // console.log('copy source ', copySource) + // var copyDestination = path.join(process.cwd(), 'engine', 'cordova-crosswalk-engine', 'libs', 'xwalk_core_library', '/'); + // var cpArmLibsCommand = 'cp -r ' + copySource + ' ' + copyDestination; + // console.log('command: ', cpArmLibsCommand); + // shelljs.exec(cpArmLibsCommand); + + // shelljs.rm('-rf', path.join(process.cwd(), 'engine', 'xwalk-webviews', 'unzipped')); + return q.promise; } IonicTask.prototype.getCrosswalkWebviews = function getCrosswalkWebviews() { // var command = 'curl -# https://download.01.org/crosswalk/releases/crosswalk/android/stable/8.37.189.14/arm/crosswalk-webview-8.37.189.14-arm.zip -o arm.zip'; var version = '8.37.189.14'; - downloadCrosswalkEngine('arm', version); - downloadCrosswalkEngine('x86', version); + var armPromise = downloadCrosswalkWebview('arm', version); + var x86Promise = downloadCrosswalkWebview('x86', version); + // downloadCrosswalkWebview('x86', version); + + return Q.all[armPromise, x86Promise]; } IonicTask.prototype.getCordovaAndroid40x = function getCordovaAndroid40x() { + var q = Q.defer(); + if (fs.existsSync(path.join(process.cwd(), 'engine', 'cordova-android'))) { - return; + q.resolve(); + return q.promise; } - var command = 'git clone --depth=1 -b 4.0.x git@github.com:apache/cordova-android.git'; + // var command = 'git clone --depth=1 -b 4.0.x git@github.com:apache/cordova-android.git'; + var downloadUrl = 'https://github.com/driftyco/cordova-android/archive/v4.0.x.zip'; - shelljs.cd(path.join(process.cwd(), 'engine')); + var tempZipFilePath = path.join(process.cwd(), 'engine'); - var result = shelljs.exec(command); - shelljs.cd('..'); + Ionic.fetchArchive(tempZipFilePath, downloadUrl) + .then(function(data) { + console.log('downloaded - ', data); + q.resolve(); + }, function(error) { + console.log('download fail - ', error); + q.reject(); + }) + + // shelljs.cd(path.join(process.cwd(), 'engine')); + + // var result = shelljs.exec(command); + + // shelljs.cd('..'); + + return q.promise; } IonicTask.prototype.removeAndroidProject = function removeAndroidProject() { @@ -163,17 +207,17 @@ IonicTask.prototype.removeAndroidProject = function removeAndroidProject() { } IonicTask.prototype.addCordova40xProject = function addCordova40xProject() { - var command = 'cordova platform add ./engine/cordova-android/'; + var command = 'cordova platform add ./engine/cordova-android-4.0.x/'; var result = shelljs.exec(command); } IonicTask.prototype.addCrosswalkPlugin = function addCrosswalkPlugin() { - var command = 'cordova plugin add ./engine/cordova-crosswalk-engine'; + var command = 'cordova plugin add ./engine/cordova-crosswalk-engine-0.8'; var result = shelljs.exec(command); } IonicTask.prototype.updateAndroidProject = function updateAndroidProject() { - var xwalkLibraryPath = path.join(process.cwd(), 'engine', 'cordova-crosswalk-engine', 'libs', 'xwalk_core_library'); + var xwalkLibraryPath = path.join(process.cwd(), 'engine', 'cordova-crosswalk-engine-0.8', 'libs', 'xwalk_core_library'); shelljs.cd(path.join(process.cwd(), 'platforms', 'android')); @@ -187,13 +231,25 @@ IonicTask.prototype.run = function(ionic) { this.ionic = ionic; - this.getCrosswalkEngine(); + // this.getCordovaCrosswalkEngine() + // .then(function(data) { + // console.log('we completed the download') + // return self.getCordovaAndroid40x(); + // }, function(error) { + // console.log('Error - ', error) + // }) + // .then(function(data) { + // console.log('downloaded Android runtimes'); + + // self.getCrosswalkWebviews(); - this.getAndroidRuntimes(); + // }, function(error) { + // console.log('failed to download android runtime'); + // }) - this.getCrosswalkWebviews(); + // this.getAndroidRuntimes(); - this.getCordovaAndroid40x(); + // this.getCordovaAndroid40x(); this.removeAndroidProject(); From b684e4ab301e34f30bdaa110f0427601c4ed3cc4 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Wed, 10 Dec 2014 11:37:56 -0600 Subject: [PATCH 0312/1100] New serve --- lib/ionic/assets/preview.html | 60 +++- lib/ionic/serve.js | 45 ++- npm-shrinkwrap.json | 631 ++++++++++++++++------------------ package.json | 2 +- 4 files changed, 379 insertions(+), 359 deletions(-) diff --git a/lib/ionic/assets/preview.html b/lib/ionic/assets/preview.html index 25e11303b9..61c4b7e0ed 100644 --- a/lib/ionic/assets/preview.html +++ b/lib/ionic/assets/preview.html @@ -1,17 +1,18 @@ - + + + + + + + - + +
-

iPhone 6

+

iOS

-

Android

+

Android

@@ -140,5 +155,16 @@

Android

--> + diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 55a74e9687..a722beab5f 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -13,10 +13,12 @@ var fs = require('fs'), Task = require('./task').Task, proxyMiddleware = require('proxy-middleware'), url = require('url'), + xml2js = require('xml2js'), IonicStats = require('./stats').IonicStats; var DEFAULT_HTTP_PORT = 8100; var DEFAULT_LIVE_RELOAD_PORT = 35729; +var IONIC_LAB_URL = '/ionic-lab'; var IonicTask = function() {}; @@ -167,7 +169,7 @@ IonicTask.prototype.start = function(ionic) { console.log('Watching :'.green.bold, self.watchPatterns); if(self.launchLab) { var open = require('open'); - open( self.host(self.port) + '/lab' ); + open( self.host(self.port) + IONIC_LAB_URL ); } else if(self.launchBrowser) { var open = require('open'); open( self.host(self.port) ); @@ -202,7 +204,7 @@ IonicTask.prototype.start = function(ionic) { var done = finalhandler(req, res); var urlParsed = url.parse(req.url, true); - var platformOverride = urlParsed.query.ionicplatform; + var platformOverride = urlParsed.query && urlParsed.query.ionicplatform; var platformUrl = getPlatformUrl(req); if(platformUrl) { @@ -237,14 +239,37 @@ IonicTask.prototype.start = function(ionic) { return; } - if(req.url === '/lab') { - fs.readFile(path.resolve(path.join(__dirname, 'assets/preview.html')), function(err, buf) { - if(err) { - res.end('404'); - } - res.setHeader('Content-Type', 'text/html'); - res.end(buf); - }); + if(req.url === IONIC_LAB_URL) { + // Serve the lab page with the given object with template data + var labServeFn = function(context) { + fs.readFile(path.resolve(path.join(__dirname, 'assets/preview.html')), function(err, buf) { + var html; + if(err) { + res.end('404'); + } else { + html = buf.toString('utf8'); + html = html.replace('//INSERT_JSON_HERE', 'var BOOTSTRAP = ' + JSON.stringify(context || {})); + } + res.setHeader('Content-Type', 'text/html'); + res.end(html); + }); + }; + + // If the config.xml file exists, let's parse it for some nice features like + // showing the name of the app in the title + if(fs.existsSync('config.xml')) { + fs.readFile(path.resolve('config.xml'), function(err, buf) { + var xml = buf.toString('utf8'); + xml2js.parseString(xml, function (err, result) { + labServeFn({ + appName: result.widget.name[0] + }); + }); + }); + } else { + labServeFn(); + } + return; } diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 1100c36b64..d623d0718d 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -9,12 +9,12 @@ "dependencies": { "readable-stream": { "version": "1.1.13", - "from": "readable-stream@>=1.1.9 <1.2.0", + "from": "readable-stream@1.1.13", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", "dependencies": { "core-util-is": { "version": "1.0.1", - "from": "core-util-is@>=1.0.0 <1.1.0", + "from": "core-util-is@1.0.1", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { @@ -24,29 +24,29 @@ }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", + "from": "string_decoder@0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", + "from": "inherits@2.0.1", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } }, "zip-stream": { "version": "0.1.4", - "from": "zip-stream@>=0.1.0 <0.2.0", + "from": "zip-stream@0.1.4", "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-0.1.4.tgz", "dependencies": { "readable-stream": { - "version": "1.0.33", - "from": "readable-stream@>=1.0.24 <1.1.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "version": "1.0.33-1", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", "dependencies": { "core-util-is": { "version": "1.0.1", - "from": "core-util-is@>=1.0.0 <1.1.0", + "from": "core-util-is@1.0.1", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { @@ -56,46 +56,46 @@ }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", + "from": "string_decoder@0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", + "from": "inherits@2.0.1", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } }, "lodash.defaults": { "version": "2.4.1", - "from": "lodash.defaults@>=2.4.1 <2.5.0", + "from": "lodash.defaults@2.4.1", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz", "dependencies": { "lodash.keys": { "version": "2.4.1", - "from": "lodash.keys@>=2.4.1 <2.5.0", + "from": "lodash.keys@2.4.1", "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", "dependencies": { "lodash._isnative": { "version": "2.4.1", - "from": "lodash._isnative@>=2.4.1 <2.5.0", + "from": "lodash._isnative@2.4.1", "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz" }, "lodash.isobject": { "version": "2.4.1", - "from": "lodash.isobject@>=2.4.1 <2.5.0", + "from": "lodash.isobject@2.4.1", "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz" }, "lodash._shimkeys": { "version": "2.4.1", - "from": "lodash._shimkeys@>=2.4.1 <2.5.0", + "from": "lodash._shimkeys@2.4.1", "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz" } } }, "lodash._objecttypes": { "version": "2.4.1", - "from": "lodash._objecttypes@>=2.4.1 <2.5.0", + "from": "lodash._objecttypes@2.4.1", "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz" } } @@ -104,17 +104,17 @@ }, "lazystream": { "version": "0.1.0", - "from": "lazystream@>=0.1.0 <0.2.0", + "from": "lazystream@0.1.0", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-0.1.0.tgz", "dependencies": { "readable-stream": { - "version": "1.0.33", - "from": "readable-stream@>=1.0.2 <1.1.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "version": "1.0.33-1", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", "dependencies": { "core-util-is": { "version": "1.0.1", - "from": "core-util-is@>=1.0.0 <1.1.0", + "from": "core-util-is@1.0.1", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { @@ -124,12 +124,12 @@ }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", + "from": "string_decoder@0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", + "from": "inherits@2.0.1", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } @@ -138,47 +138,47 @@ }, "file-utils": { "version": "0.1.5", - "from": "file-utils@>=0.1.5 <0.2.0", + "from": "file-utils@0.1.5", "resolved": "https://registry.npmjs.org/file-utils/-/file-utils-0.1.5.tgz", "dependencies": { "lodash": { "version": "2.1.0", - "from": "lodash@>=2.1.0 <2.2.0", + "from": "lodash@2.1.0", "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.1.0.tgz" }, "iconv-lite": { "version": "0.2.11", - "from": "iconv-lite@>=0.2.11 <0.3.0", + "from": "iconv-lite@0.2.11", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz" }, "rimraf": { "version": "2.2.8", - "from": "rimraf@>=2.2.2 <2.3.0", + "from": "rimraf@2.2.8", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" }, "glob": { "version": "3.2.11", - "from": "glob@>=3.2.6 <3.3.0", + "from": "glob@3.2.11", "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", "dependencies": { "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.0 <3.0.0", + "from": "inherits@2.0.1", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "minimatch": { "version": "0.3.0", - "from": "minimatch@>=0.3.0 <0.4.0", + "from": "minimatch@0.3.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", "dependencies": { "lru-cache": { "version": "2.5.0", - "from": "lru-cache@>=2.0.0 <3.0.0", + "from": "lru-cache@2.5.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" }, "sigmund": { "version": "1.0.0", - "from": "sigmund@>=1.0.0 <1.1.0", + "from": "sigmund@1.0.0", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" } } @@ -187,65 +187,65 @@ }, "minimatch": { "version": "0.2.14", - "from": "minimatch@>=0.2.12 <0.3.0", + "from": "minimatch@0.2.14", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "dependencies": { "lru-cache": { "version": "2.5.0", - "from": "lru-cache@>=2.0.0 <3.0.0", + "from": "lru-cache@2.5.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" }, "sigmund": { "version": "1.0.0", - "from": "sigmund@>=1.0.0 <1.1.0", + "from": "sigmund@1.0.0", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" } } }, "findup-sync": { "version": "0.1.3", - "from": "findup-sync@>=0.1.2 <0.2.0", + "from": "findup-sync@0.1.3", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", "dependencies": { "lodash": { "version": "2.4.1", - "from": "lodash@>=2.4.1 <2.5.0", + "from": "lodash@2.4.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz" } } }, "isbinaryfile": { "version": "0.1.9", - "from": "isbinaryfile@>=0.1.9 <0.2.0", + "from": "isbinaryfile@0.1.9", "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-0.1.9.tgz" } } }, "lodash": { "version": "2.4.1", - "from": "lodash@>=2.4.1 <2.5.0", + "from": "lodash@2.4.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz" } } }, "colors": { "version": "0.6.2", - "from": "colors@0.6.2", + "from": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz" }, "connect": { "version": "3.1.1", - "from": "connect@3.1.1", + "from": "https://registry.npmjs.org/connect/-/connect-3.1.1.tgz", "resolved": "https://registry.npmjs.org/connect/-/connect-3.1.1.tgz", "dependencies": { "debug": { "version": "1.0.4", - "from": "debug@1.0.4", + "from": "https://registry.npmjs.org/debug/-/debug-1.0.4.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-1.0.4.tgz", "dependencies": { "ms": { "version": "0.6.2", - "from": "ms@0.6.2", + "from": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz" } } @@ -264,12 +264,12 @@ }, "parseurl": { "version": "1.3.0", - "from": "parseurl@>=1.3.0 <1.4.0", + "from": "parseurl@1.3.0", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.0.tgz" }, "utils-merge": { "version": "1.0.0", - "from": "utils-merge@1.0.0", + "from": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz" } } @@ -281,40 +281,40 @@ }, "cross-spawn": { "version": "0.2.3", - "from": "cross-spawn@0.2.3", + "from": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-0.2.3.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-0.2.3.tgz", "dependencies": { "lru-cache": { "version": "2.5.0", - "from": "lru-cache@>=2.5.0 <3.0.0", + "from": "lru-cache@2.5.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" } } }, "event-stream": { "version": "3.0.20", - "from": "event-stream@>=3.0.0 <3.1.0", + "from": "event-stream@3.0.20", "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.0.20.tgz", "dependencies": { "through": { "version": "2.3.6", - "from": "through@>=2.3.1 <2.4.0", + "from": "through@2.3.6", "resolved": "https://registry.npmjs.org/through/-/through-2.3.6.tgz" }, "duplexer": { "version": "0.1.1", - "from": "duplexer@>=0.1.1 <0.2.0", + "from": "duplexer@0.1.1", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz" }, "from": { "version": "0.1.3", - "from": "from@>=0.0.0 <1.0.0", + "from": "from@0.1.3", "resolved": "https://registry.npmjs.org/from/-/from-0.1.3.tgz" }, "map-stream": { - "version": "0.0.5", - "from": "map-stream@>=0.0.3 <0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.5.tgz" + "version": "0.0.4", + "from": "map-stream@0.0.4", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.4.tgz" }, "pause-stream": { "version": "0.0.11", @@ -323,29 +323,29 @@ }, "split": { "version": "0.2.10", - "from": "split@>=0.2.0 <0.3.0", + "from": "split@0.2.10", "resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz" }, "stream-combiner": { "version": "0.0.4", - "from": "stream-combiner@>=0.0.3 <0.1.0", + "from": "stream-combiner@0.0.4", "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz" } } }, "finalhandler": { "version": "0.2.0", - "from": "finalhandler@0.2.0", + "from": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.2.0.tgz", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.2.0.tgz", "dependencies": { "debug": { "version": "2.0.0", - "from": "debug@>=2.0.0 <2.1.0", + "from": "debug@2.0.0", "resolved": "https://registry.npmjs.org/debug/-/debug-2.0.0.tgz", "dependencies": { "ms": { "version": "0.6.2", - "from": "ms@0.6.2", + "from": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz" } } @@ -359,13 +359,13 @@ }, "form-data": { "version": "0.1.4", - "from": "form-data@0.1.4", + "from": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", "dependencies": { "combined-stream": { - "version": "0.0.7", - "from": "combined-stream@>=0.0.4 <0.1.0", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "version": "0.0.5", + "from": "combined-stream@0.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.5.tgz", "dependencies": { "delayed-stream": { "version": "0.0.5", @@ -376,85 +376,85 @@ }, "mime": { "version": "1.2.11", - "from": "mime@>=1.2.11 <1.3.0", + "from": "mime@1.2.11", "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" }, "async": { "version": "0.9.0", - "from": "async@>=0.9.0 <0.10.0", + "from": "async@0.9.0", "resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz" } } }, "gulp": { "version": "3.8.10", - "from": "gulp@>=3.8.8 <3.9.0", + "from": "gulp@", "resolved": "https://registry.npmjs.org/gulp/-/gulp-3.8.10.tgz", "dependencies": { "archy": { "version": "1.0.0", - "from": "archy@>=1.0.0 <2.0.0", + "from": "archy@^1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz" }, "chalk": { "version": "0.5.1", - "from": "chalk@>=0.5.0 <0.6.0", + "from": "chalk@^0.5.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", "dependencies": { "ansi-styles": { "version": "1.1.0", - "from": "ansi-styles@>=1.1.0 <2.0.0", + "from": "ansi-styles@^1.1.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" }, "escape-string-regexp": { "version": "1.0.2", - "from": "escape-string-regexp@>=1.0.0 <2.0.0", + "from": "escape-string-regexp@^1.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz" }, "has-ansi": { "version": "0.1.0", - "from": "has-ansi@>=0.1.0 <0.2.0", + "from": "has-ansi@^0.1.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", "dependencies": { "ansi-regex": { "version": "0.2.1", - "from": "ansi-regex@>=0.2.1 <0.3.0", + "from": "ansi-regex@^0.2.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" } } }, "strip-ansi": { "version": "0.3.0", - "from": "strip-ansi@>=0.3.0 <0.4.0", + "from": "strip-ansi@^0.3.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", "dependencies": { "ansi-regex": { "version": "0.2.1", - "from": "ansi-regex@>=0.2.1 <0.3.0", + "from": "ansi-regex@^0.2.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" } } }, "supports-color": { "version": "0.2.0", - "from": "supports-color@>=0.2.0 <0.3.0", + "from": "supports-color@^0.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" } } }, "deprecated": { "version": "0.0.1", - "from": "deprecated@>=0.0.1 <0.0.2", + "from": "deprecated@^0.0.1", "resolved": "https://registry.npmjs.org/deprecated/-/deprecated-0.0.1.tgz" }, "gulp-util": { "version": "3.0.1", - "from": "gulp-util@>=3.0.0 <4.0.0", + "from": "gulp-util@^3.0.0", "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.1.tgz", "dependencies": { "dateformat": { "version": "1.0.11", - "from": "dateformat@>=1.0.7-1.2.3 <2.0.0", + "from": "dateformat@^1.0.7-1.2.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.11.tgz", "dependencies": { "get-stdin": { @@ -469,39 +469,39 @@ "dependencies": { "camelcase-keys": { "version": "1.0.0", - "from": "camelcase-keys@>=1.0.0 <2.0.0", + "from": "camelcase-keys@^1.0.0", "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-1.0.0.tgz", "dependencies": { "camelcase": { "version": "1.0.2", - "from": "camelcase@>=1.0.1 <2.0.0", + "from": "camelcase@^1.0.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.0.2.tgz" }, "map-obj": { "version": "1.0.0", - "from": "map-obj@>=1.0.0 <2.0.0", + "from": "map-obj@^1.0.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.0.tgz" } } }, "indent-string": { "version": "1.2.0", - "from": "indent-string@>=1.1.0 <2.0.0", + "from": "indent-string@^1.1.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.0.tgz", "dependencies": { "repeating": { "version": "1.1.0", - "from": "repeating@>=1.1.0 <2.0.0", + "from": "repeating@^1.1.0", "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.0.tgz", "dependencies": { "is-finite": { "version": "1.0.0", - "from": "is-finite@>=1.0.0 <2.0.0", + "from": "is-finite@^1.0.0", "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.0.tgz" }, "meow": { "version": "1.0.0", - "from": "meow@>=1.0.0 <2.0.0", + "from": "meow@^1.0.0", "resolved": "https://registry.npmjs.org/meow/-/meow-1.0.0.tgz" } } @@ -510,7 +510,7 @@ }, "object-assign": { "version": "1.0.0", - "from": "object-assign@>=1.0.0 <2.0.0", + "from": "object-assign@^1.0.0", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-1.0.0.tgz" } } @@ -519,56 +519,56 @@ }, "lodash": { "version": "2.4.1", - "from": "lodash@>=2.4.1 <2.5.0", + "from": "lodash@^2.4.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz" }, "lodash._reinterpolate": { "version": "2.4.1", - "from": "lodash._reinterpolate@>=2.4.1 <3.0.0", + "from": "lodash._reinterpolate@^2.4.1", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-2.4.1.tgz" }, "lodash.template": { "version": "2.4.1", - "from": "lodash.template@>=2.4.1 <3.0.0", + "from": "lodash.template@^2.4.1", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-2.4.1.tgz", "dependencies": { "lodash.defaults": { "version": "2.4.1", - "from": "lodash.defaults@>=2.4.1 <2.5.0", + "from": "lodash.defaults@~2.4.1", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz", "dependencies": { "lodash._objecttypes": { "version": "2.4.1", - "from": "lodash._objecttypes@>=2.4.1 <2.5.0", + "from": "lodash._objecttypes@~2.4.1", "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz" } } }, "lodash.escape": { "version": "2.4.1", - "from": "lodash.escape@>=2.4.1 <2.5.0", + "from": "lodash.escape@~2.4.1", "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-2.4.1.tgz", "dependencies": { "lodash._escapehtmlchar": { "version": "2.4.1", - "from": "lodash._escapehtmlchar@>=2.4.1 <2.5.0", + "from": "lodash._escapehtmlchar@~2.4.1", "resolved": "https://registry.npmjs.org/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.4.1.tgz", "dependencies": { "lodash._htmlescapes": { "version": "2.4.1", - "from": "lodash._htmlescapes@>=2.4.1 <2.5.0", + "from": "lodash._htmlescapes@~2.4.1", "resolved": "https://registry.npmjs.org/lodash._htmlescapes/-/lodash._htmlescapes-2.4.1.tgz" } } }, "lodash._reunescapedhtml": { "version": "2.4.1", - "from": "lodash._reunescapedhtml@>=2.4.1 <2.5.0", + "from": "lodash._reunescapedhtml@~2.4.1", "resolved": "https://registry.npmjs.org/lodash._reunescapedhtml/-/lodash._reunescapedhtml-2.4.1.tgz", "dependencies": { "lodash._htmlescapes": { "version": "2.4.1", - "from": "lodash._htmlescapes@>=2.4.1 <2.5.0", + "from": "lodash._htmlescapes@~2.4.1", "resolved": "https://registry.npmjs.org/lodash._htmlescapes/-/lodash._htmlescapes-2.4.1.tgz" } } @@ -577,39 +577,39 @@ }, "lodash._escapestringchar": { "version": "2.4.1", - "from": "lodash._escapestringchar@>=2.4.1 <2.5.0", + "from": "lodash._escapestringchar@~2.4.1", "resolved": "https://registry.npmjs.org/lodash._escapestringchar/-/lodash._escapestringchar-2.4.1.tgz" }, "lodash.keys": { "version": "2.4.1", - "from": "lodash.keys@>=2.4.1 <2.5.0", + "from": "lodash.keys@~2.4.1", "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", "dependencies": { "lodash._isnative": { "version": "2.4.1", - "from": "lodash._isnative@>=2.4.1 <2.5.0", + "from": "lodash._isnative@~2.4.1", "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz" }, "lodash.isobject": { "version": "2.4.1", - "from": "lodash.isobject@>=2.4.1 <2.5.0", + "from": "lodash.isobject@~2.4.1", "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", "dependencies": { "lodash._objecttypes": { "version": "2.4.1", - "from": "lodash._objecttypes@>=2.4.1 <2.5.0", + "from": "lodash._objecttypes@~2.4.1", "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz" } } }, "lodash._shimkeys": { "version": "2.4.1", - "from": "lodash._shimkeys@>=2.4.1 <2.5.0", + "from": "lodash._shimkeys@~2.4.1", "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz", "dependencies": { "lodash._objecttypes": { "version": "2.4.1", - "from": "lodash._objecttypes@>=2.4.1 <2.5.0", + "from": "lodash._objecttypes@~2.4.1", "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz" } } @@ -618,19 +618,19 @@ }, "lodash.templatesettings": { "version": "2.4.1", - "from": "lodash.templatesettings@>=2.4.1 <2.5.0", + "from": "lodash.templatesettings@~2.4.1", "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-2.4.1.tgz" }, "lodash.values": { "version": "2.4.1", - "from": "lodash.values@>=2.4.1 <2.5.0", + "from": "lodash.values@~2.4.1", "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-2.4.1.tgz" } } }, "multipipe": { "version": "0.1.2", - "from": "multipipe@>=0.1.0 <0.2.0", + "from": "multipipe@^0.1.0", "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", "dependencies": { "duplexer2": { @@ -640,12 +640,12 @@ "dependencies": { "readable-stream": { "version": "1.1.13", - "from": "readable-stream@>=1.1.9 <1.2.0", + "from": "readable-stream@~1.1.9", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", "dependencies": { "core-util-is": { "version": "1.0.1", - "from": "core-util-is@>=1.0.0 <1.1.0", + "from": "core-util-is@~1.0.0", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { @@ -655,12 +655,12 @@ }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", + "from": "string_decoder@~0.10.x", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", + "from": "inherits@~2.0.1", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } @@ -671,7 +671,7 @@ }, "through2": { "version": "0.6.3", - "from": "through2@>=0.6.1 <0.7.0", + "from": "through2@^0.6.1", "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.3.tgz", "dependencies": { "readable-stream": { @@ -681,7 +681,7 @@ "dependencies": { "core-util-is": { "version": "1.0.1", - "from": "core-util-is@>=1.0.0 <1.1.0", + "from": "core-util-is@~1.0.0", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { @@ -691,12 +691,12 @@ }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", + "from": "string_decoder@~0.10.x", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", + "from": "inherits@~2.0.1", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } @@ -710,17 +710,17 @@ }, "vinyl": { "version": "0.4.6", - "from": "vinyl@>=0.4.0 <0.5.0", + "from": "vinyl@^0.4.0", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", "dependencies": { "clone": { "version": "0.2.0", - "from": "clone@>=0.2.0 <0.3.0", + "from": "clone@^0.2.0", "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz" }, "clone-stats": { "version": "0.0.1", - "from": "clone-stats@>=0.0.1 <0.0.2", + "from": "clone-stats@^0.0.1", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" } } @@ -729,42 +729,42 @@ }, "interpret": { "version": "0.3.8", - "from": "interpret@>=0.3.2 <0.4.0", + "from": "interpret@^0.3.2", "resolved": "https://registry.npmjs.org/interpret/-/interpret-0.3.8.tgz" }, "liftoff": { "version": "0.13.6", - "from": "liftoff@>=0.13.2 <0.14.0", + "from": "liftoff@^0.13.2", "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-0.13.6.tgz", "dependencies": { "findup-sync": { "version": "0.1.3", - "from": "findup-sync@>=0.1.2 <0.2.0", + "from": "findup-sync@~0.1.2", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", "dependencies": { "glob": { "version": "3.2.11", - "from": "glob@>=3.2.9 <3.3.0", + "from": "glob@~3.2.9", "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", "dependencies": { "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.0 <3.0.0", + "from": "inherits@2", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "minimatch": { "version": "0.3.0", - "from": "minimatch@>=0.3.0 <0.4.0", + "from": "minimatch@0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", "dependencies": { "lru-cache": { "version": "2.5.0", - "from": "lru-cache@>=2.0.0 <3.0.0", + "from": "lru-cache@2", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" }, "sigmund": { "version": "1.0.0", - "from": "sigmund@>=1.0.0 <1.1.0", + "from": "sigmund@~1.0.0", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" } } @@ -773,51 +773,51 @@ }, "lodash": { "version": "2.4.1", - "from": "lodash@>=2.4.1 <2.5.0", + "from": "lodash@~2.4.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz" } } }, "resolve": { "version": "1.0.0", - "from": "resolve@>=1.0.0 <1.1.0", + "from": "resolve@~1.0.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.0.0.tgz" }, "extend": { "version": "1.3.0", - "from": "extend@>=1.3.0 <1.4.0", + "from": "extend@~1.3.0", "resolved": "https://registry.npmjs.org/extend/-/extend-1.3.0.tgz" }, "flagged-respawn": { "version": "0.3.1", - "from": "flagged-respawn@>=0.3.0 <0.4.0", + "from": "flagged-respawn@~0.3.0", "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-0.3.1.tgz" } } }, "minimist": { "version": "1.1.0", - "from": "minimist@>=1.1.0 <2.0.0", + "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.1.0.tgz" }, "orchestrator": { "version": "0.3.7", - "from": "orchestrator@>=0.3.0 <0.4.0", + "from": "orchestrator@^0.3.0", "resolved": "https://registry.npmjs.org/orchestrator/-/orchestrator-0.3.7.tgz", "dependencies": { "end-of-stream": { "version": "0.1.5", - "from": "end-of-stream@>=0.1.5 <0.2.0", + "from": "end-of-stream@~0.1.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz", "dependencies": { "once": { "version": "1.3.1", - "from": "once@>=1.3.0 <1.4.0", + "from": "once@~1.3.0", "resolved": "https://registry.npmjs.org/once/-/once-1.3.1.tgz", "dependencies": { "wrappy": { "version": "1.0.1", - "from": "wrappy@>=1.0.0 <2.0.0", + "from": "wrappy@1", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" } } @@ -826,41 +826,41 @@ }, "sequencify": { "version": "0.0.7", - "from": "sequencify@>=0.0.7 <0.1.0", + "from": "sequencify@~0.0.7", "resolved": "https://registry.npmjs.org/sequencify/-/sequencify-0.0.7.tgz" }, "stream-consume": { "version": "0.1.0", - "from": "stream-consume@>=0.1.0 <0.2.0", + "from": "stream-consume@~0.1.0", "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz" } } }, "pretty-hrtime": { "version": "0.2.2", - "from": "pretty-hrtime@>=0.2.0 <0.3.0", + "from": "pretty-hrtime@^0.2.0", "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-0.2.2.tgz" }, "semver": { "version": "4.1.0", - "from": "semver@>=4.1.0 <5.0.0", + "from": "semver@^4.1.0", "resolved": "https://registry.npmjs.org/semver/-/semver-4.1.0.tgz" }, "tildify": { "version": "1.0.0", - "from": "tildify@>=1.0.0 <2.0.0", + "from": "tildify@^1.0.0", "resolved": "https://registry.npmjs.org/tildify/-/tildify-1.0.0.tgz", "dependencies": { "user-home": { "version": "1.1.0", - "from": "user-home@>=1.0.0 <2.0.0", + "from": "user-home@^1.0.0", "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.0.tgz" } } }, "v8flags": { "version": "1.0.5", - "from": "v8flags@>=1.0.1 <2.0.0", + "from": "v8flags@^1.0.1", "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-1.0.5.tgz" } } @@ -872,7 +872,7 @@ }, "npm": { "version": "2.1.3", - "from": "npm@2.1.3", + "from": "https://registry.npmjs.org/npm/-/npm-2.1.3.tgz", "resolved": "https://registry.npmjs.org/npm/-/npm-2.1.3.tgz", "dependencies": { "abbrev": { @@ -1436,22 +1436,22 @@ }, "open": { "version": "0.0.5", - "from": "open@0.0.5", + "from": "https://registry.npmjs.org/open/-/open-0.0.5.tgz", "resolved": "https://registry.npmjs.org/open/-/open-0.0.5.tgz" }, "optimist": { "version": "0.6.0", - "from": "optimist@0.6.0", + "from": "https://registry.npmjs.org/optimist/-/optimist-0.6.0.tgz", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.0.tgz", "dependencies": { "wordwrap": { "version": "0.0.2", - "from": "wordwrap@>=0.0.2 <0.1.0", + "from": "wordwrap@0.0.2", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz" }, "minimist": { "version": "0.0.10", - "from": "minimist@>=0.0.1 <0.1.0", + "from": "minimist@0.0.10", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz" } } @@ -1463,103 +1463,103 @@ }, "prompt": { "version": "0.2.12", - "from": "prompt@0.2.12", + "from": "https://registry.npmjs.org/prompt/-/prompt-0.2.12.tgz", "resolved": "https://registry.npmjs.org/prompt/-/prompt-0.2.12.tgz", "dependencies": { "pkginfo": { "version": "0.3.0", - "from": "pkginfo@>=0.0.0 <1.0.0", + "from": "pkginfo@0.3.0", "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.0.tgz" }, "read": { "version": "1.0.5", - "from": "read@>=1.0.0 <1.1.0", + "from": "read@1.0.5", "resolved": "https://registry.npmjs.org/read/-/read-1.0.5.tgz", "dependencies": { "mute-stream": { "version": "0.0.4", - "from": "mute-stream@>=0.0.4 <0.1.0", + "from": "mute-stream@0.0.4", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.4.tgz" } } }, "revalidator": { "version": "0.1.8", - "from": "revalidator@>=0.1.0 <0.2.0", + "from": "revalidator@0.1.8", "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz" }, "utile": { "version": "0.2.1", - "from": "utile@>=0.2.0 <0.3.0", + "from": "utile@0.2.1", "resolved": "https://registry.npmjs.org/utile/-/utile-0.2.1.tgz", "dependencies": { "async": { "version": "0.2.10", - "from": "async@>=0.2.9 <0.3.0", + "from": "async@0.2.10", "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" }, "deep-equal": { "version": "0.2.1", - "from": "deep-equal@*", + "from": "deep-equal@0.2.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.2.1.tgz" }, "i": { "version": "0.3.2", - "from": "i@>=0.3.0 <0.4.0", + "from": "i@0.3.2", "resolved": "https://registry.npmjs.org/i/-/i-0.3.2.tgz" }, "mkdirp": { "version": "0.5.0", - "from": "mkdirp@>=0.0.0 <1.0.0", + "from": "mkdirp@0.5.0", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", "dependencies": { "minimist": { "version": "0.0.8", - "from": "minimist@0.0.8", + "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } }, "rimraf": { "version": "2.2.8", - "from": "rimraf@>=2.0.0 <3.0.0", + "from": "rimraf@2.2.8", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" } } }, "winston": { "version": "0.6.2", - "from": "winston@>=0.6.0 <0.7.0", + "from": "winston@0.6.2", "resolved": "https://registry.npmjs.org/winston/-/winston-0.6.2.tgz", "dependencies": { "async": { "version": "0.1.22", - "from": "async@>=0.1.0 <0.2.0", + "from": "async@0.1.22", "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz" }, "cycle": { "version": "1.0.3", - "from": "cycle@>=1.0.0 <1.1.0", + "from": "cycle@1.0.3", "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz" }, "eyes": { "version": "0.1.8", - "from": "eyes@>=0.1.0 <0.2.0", + "from": "eyes@0.1.8", "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz" }, "pkginfo": { "version": "0.2.3", - "from": "pkginfo@>=0.2.0 <0.3.0", + "from": "pkginfo@0.2.3", "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.2.3.tgz" }, "request": { "version": "2.9.203", - "from": "request@>=2.9.0 <2.10.0", + "from": "request@2.9.203", "resolved": "https://registry.npmjs.org/request/-/request-2.9.203.tgz" }, "stack-trace": { "version": "0.0.9", - "from": "stack-trace@>=0.0.0 <0.1.0", + "from": "stack-trace@0.0.9", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" } } @@ -1568,52 +1568,52 @@ }, "proxy-middleware": { "version": "0.7.0", - "from": "proxy-middleware@>=0.7.0 <0.8.0", + "from": "proxy-middleware@0.7.0", "resolved": "https://registry.npmjs.org/proxy-middleware/-/proxy-middleware-0.7.0.tgz" }, "q": { "version": "1.0.1", - "from": "q@1.0.1", + "from": "https://registry.npmjs.org/q/-/q-1.0.1.tgz", "resolved": "https://registry.npmjs.org/q/-/q-1.0.1.tgz" }, "request": { "version": "2.27.0", - "from": "request@2.27.0", + "from": "https://registry.npmjs.org/request/-/request-2.27.0.tgz", "resolved": "https://registry.npmjs.org/request/-/request-2.27.0.tgz", "dependencies": { "qs": { "version": "0.6.6", - "from": "qs@>=0.6.0 <0.7.0", + "from": "qs@0.6.6", "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz" }, "json-stringify-safe": { "version": "5.0.0", - "from": "json-stringify-safe@>=5.0.0 <5.1.0", + "from": "json-stringify-safe@5.0.0", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.0.tgz" }, "forever-agent": { "version": "0.5.2", - "from": "forever-agent@>=0.5.0 <0.6.0", + "from": "forever-agent@0.5.2", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" }, "tunnel-agent": { "version": "0.3.0", - "from": "tunnel-agent@>=0.3.0 <0.4.0", + "from": "tunnel-agent@0.3.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz" }, "http-signature": { "version": "0.10.0", - "from": "http-signature@>=0.10.0 <0.11.0", + "from": "http-signature@0.10.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.0.tgz", "dependencies": { "assert-plus": { "version": "0.1.2", - "from": "assert-plus@0.1.2", + "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.2.tgz", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.2.tgz" }, "asn1": { "version": "0.1.11", - "from": "asn1@0.1.11", + "from": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz" }, "ctype": { @@ -1625,179 +1625,179 @@ }, "hawk": { "version": "1.0.0", - "from": "hawk@>=1.0.0 <1.1.0", + "from": "hawk@1.0.0", "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", "dependencies": { "hoek": { "version": "0.9.1", - "from": "hoek@>=0.9.0 <0.10.0", + "from": "hoek@0.9.1", "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz" }, "boom": { "version": "0.4.2", - "from": "boom@>=0.4.0 <0.5.0", + "from": "boom@0.4.2", "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz" }, "cryptiles": { "version": "0.2.2", - "from": "cryptiles@>=0.2.0 <0.3.0", + "from": "cryptiles@0.2.2", "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz" }, "sntp": { "version": "0.2.4", - "from": "sntp@>=0.2.0 <0.3.0", + "from": "sntp@0.2.4", "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz" } } }, "aws-sign": { "version": "0.3.0", - "from": "aws-sign@>=0.3.0 <0.4.0", + "from": "aws-sign@0.3.0", "resolved": "https://registry.npmjs.org/aws-sign/-/aws-sign-0.3.0.tgz" }, "oauth-sign": { "version": "0.3.0", - "from": "oauth-sign@>=0.3.0 <0.4.0", + "from": "oauth-sign@0.3.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz" }, "cookie-jar": { "version": "0.3.0", - "from": "cookie-jar@>=0.3.0 <0.4.0", + "from": "cookie-jar@0.3.0", "resolved": "https://registry.npmjs.org/cookie-jar/-/cookie-jar-0.3.0.tgz" }, "node-uuid": { - "version": "1.4.2", - "from": "node-uuid@>=1.4.0 <1.5.0", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.2.tgz" + "version": "1.4.1", + "from": "node-uuid@1.4.1", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.1.tgz" }, "mime": { "version": "1.2.11", - "from": "mime@>=1.2.9 <1.3.0", + "from": "mime@1.2.11", "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" } } }, "serve-static": { "version": "1.7.1", - "from": "serve-static@1.7.1", + "from": "https://registry.npmjs.org/serve-static/-/serve-static-1.7.1.tgz", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.7.1.tgz", "dependencies": { "escape-html": { "version": "1.0.1", - "from": "escape-html@1.0.1", + "from": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.1.tgz", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.1.tgz" }, "parseurl": { "version": "1.3.0", - "from": "parseurl@>=1.3.0 <1.4.0", + "from": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.0.tgz", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.0.tgz" }, "send": { "version": "0.10.1", - "from": "send@0.10.1", + "from": "https://registry.npmjs.org/send/-/send-0.10.1.tgz", "resolved": "https://registry.npmjs.org/send/-/send-0.10.1.tgz", "dependencies": { "debug": { "version": "2.1.0", - "from": "debug@>=2.1.0 <2.2.0", + "from": "https://registry.npmjs.org/debug/-/debug-2.1.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.1.0.tgz" }, "depd": { "version": "1.0.0", - "from": "depd@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/depd/-/depd-1.0.0.tgz", "resolved": "https://registry.npmjs.org/depd/-/depd-1.0.0.tgz" }, "destroy": { "version": "1.0.3", - "from": "destroy@1.0.3", + "from": "https://registry.npmjs.org/destroy/-/destroy-1.0.3.tgz", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.3.tgz" }, "etag": { "version": "1.5.1", - "from": "etag@>=1.5.0 <1.6.0", + "from": "https://registry.npmjs.org/etag/-/etag-1.5.1.tgz", "resolved": "https://registry.npmjs.org/etag/-/etag-1.5.1.tgz", "dependencies": { "crc": { "version": "3.2.1", - "from": "crc@3.2.1", + "from": "https://registry.npmjs.org/crc/-/crc-3.2.1.tgz", "resolved": "https://registry.npmjs.org/crc/-/crc-3.2.1.tgz" } } }, "fresh": { "version": "0.2.4", - "from": "fresh@0.2.4", + "from": "https://registry.npmjs.org/fresh/-/fresh-0.2.4.tgz", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.4.tgz" }, "mime": { "version": "1.2.11", - "from": "mime@1.2.11", + "from": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" }, "ms": { "version": "0.6.2", - "from": "ms@0.6.2", + "from": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz" }, "on-finished": { "version": "2.1.1", - "from": "on-finished@>=2.1.1 <2.2.0", + "from": "https://registry.npmjs.org/on-finished/-/on-finished-2.1.1.tgz", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.1.1.tgz", "dependencies": { "ee-first": { "version": "1.1.0", - "from": "ee-first@1.1.0", + "from": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.0.tgz", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.0.tgz" } } }, "range-parser": { "version": "1.0.2", - "from": "range-parser@>=1.0.2 <1.1.0", + "from": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.2.tgz", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.2.tgz" } } }, "utils-merge": { "version": "1.0.0", - "from": "utils-merge@1.0.0", + "from": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz" } } }, "shelljs": { "version": "0.2.6", - "from": "shelljs@0.2.6", + "from": "https://registry.npmjs.org/shelljs/-/shelljs-0.2.6.tgz", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.2.6.tgz" }, "tiny-lr-fork": { "version": "0.0.5", - "from": "tiny-lr-fork@0.0.5", + "from": "https://registry.npmjs.org/tiny-lr-fork/-/tiny-lr-fork-0.0.5.tgz", "resolved": "https://registry.npmjs.org/tiny-lr-fork/-/tiny-lr-fork-0.0.5.tgz", "dependencies": { "qs": { "version": "0.5.6", - "from": "qs@>=0.5.2 <0.6.0", + "from": "qs@0.5.6", "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz" }, "faye-websocket": { "version": "0.4.4", - "from": "faye-websocket@>=0.4.3 <0.5.0", + "from": "faye-websocket@0.4.4", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz" }, "noptify": { "version": "0.0.3", - "from": "noptify@>=0.0.3 <0.1.0", + "from": "noptify@0.0.3", "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz", "dependencies": { "nopt": { "version": "2.0.0", - "from": "nopt@>=2.0.0 <2.1.0", + "from": "nopt@2.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz", "dependencies": { "abbrev": { "version": "1.0.5", - "from": "abbrev@>=1.0.0 <2.0.0", + "from": "abbrev@1.0.5", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz" } } @@ -1806,7 +1806,7 @@ }, "debug": { "version": "0.7.4", - "from": "debug@>=0.7.0 <0.8.0", + "from": "debug@0.7.4", "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz" } } @@ -1818,92 +1818,92 @@ }, "unzip": { "version": "0.1.9", - "from": "unzip@0.1.9", + "from": "https://registry.npmjs.org/unzip/-/unzip-0.1.9.tgz", "resolved": "https://registry.npmjs.org/unzip/-/unzip-0.1.9.tgz", "dependencies": { "fstream": { "version": "0.1.31", - "from": "fstream@>=0.1.21 <0.2.0", + "from": "fstream@0.1.31", "resolved": "https://registry.npmjs.org/fstream/-/fstream-0.1.31.tgz", "dependencies": { "graceful-fs": { - "version": "3.0.5", - "from": "graceful-fs@>=3.0.2 <3.1.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.5.tgz" + "version": "3.0.4", + "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.4.tgz", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.4.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.0 <2.1.0", + "from": "inherits@2.0.1", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "mkdirp": { "version": "0.5.0", - "from": "mkdirp@>=0.0.0 <1.0.0", + "from": "mkdirp@0.5.0", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", "dependencies": { "minimist": { "version": "0.0.8", - "from": "minimist@0.0.8", + "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } }, "rimraf": { "version": "2.2.8", - "from": "rimraf@>=2.0.0 <3.0.0", + "from": "rimraf@2.2.8", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" } } }, "pullstream": { "version": "0.4.1", - "from": "pullstream@>=0.4.0 <0.5.0", + "from": "https://registry.npmjs.org/pullstream/-/pullstream-0.4.1.tgz", "resolved": "https://registry.npmjs.org/pullstream/-/pullstream-0.4.1.tgz", "dependencies": { "over": { "version": "0.0.5", - "from": "over@>=0.0.5 <1.0.0", + "from": "over@0.0.5", "resolved": "https://registry.npmjs.org/over/-/over-0.0.5.tgz" }, "slice-stream": { "version": "1.0.0", - "from": "slice-stream@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/slice-stream/-/slice-stream-1.0.0.tgz", "resolved": "https://registry.npmjs.org/slice-stream/-/slice-stream-1.0.0.tgz" } } }, "binary": { "version": "0.3.0", - "from": "binary@>=0.3.0 <0.4.0", + "from": "binary@0.3.0", "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", "dependencies": { "chainsaw": { "version": "0.1.0", - "from": "chainsaw@>=0.1.0 <0.2.0", + "from": "chainsaw@0.1.0", "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", "dependencies": { "traverse": { "version": "0.3.9", - "from": "traverse@>=0.3.0 <0.4.0", + "from": "traverse@0.3.9", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz" } } }, "buffers": { "version": "0.1.1", - "from": "buffers@>=0.1.1 <0.2.0", + "from": "buffers@0.1.1", "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz" } } }, "readable-stream": { - "version": "1.0.33", - "from": "readable-stream@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "version": "1.0.33-1", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", "dependencies": { "core-util-is": { "version": "1.0.1", - "from": "core-util-is@>=1.0.0 <1.1.0", + "from": "core-util-is@1.0.1", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { @@ -1913,29 +1913,29 @@ }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", + "from": "string_decoder@0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", + "from": "inherits@2.0.1", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } }, "setimmediate": { "version": "1.0.2", - "from": "setimmediate@>=1.0.1 <1.1.0", + "from": "setimmediate@1.0.2", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.2.tgz" }, "match-stream": { "version": "0.0.2", - "from": "match-stream@>=0.0.2 <0.1.0", + "from": "match-stream@0.0.2", "resolved": "https://registry.npmjs.org/match-stream/-/match-stream-0.0.2.tgz", "dependencies": { "buffers": { "version": "0.1.1", - "from": "buffers@>=0.1.1 <0.2.0", + "from": "buffers@0.1.1", "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz" } } @@ -1944,44 +1944,32 @@ }, "vinyl-fs": { "version": "0.3.7", - "from": "vinyl-fs@0.3.7", + "from": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-0.3.7.tgz", "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-0.3.7.tgz", "dependencies": { "glob-stream": { - "version": "3.1.18", - "from": "glob-stream@>=3.1.5 <4.0.0", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.18.tgz", + "version": "3.1.15", + "from": "glob-stream@3.1.15", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.15.tgz", "dependencies": { "glob": { - "version": "4.3.1", - "from": "glob@>=4.3.1 <5.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-4.3.1.tgz", + "version": "4.0.6", + "from": "glob@4.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.0.6.tgz", "dependencies": { - "inflight": { - "version": "1.0.4", - "from": "inflight@>=1.0.4 <2.0.0", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", - "dependencies": { - "wrappy": { - "version": "1.0.1", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" - } - } - }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", + "from": "inherits@2.0.1", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "once": { "version": "1.3.1", - "from": "once@>=1.3.0 <2.0.0", + "from": "once@1.3.1", "resolved": "https://registry.npmjs.org/once/-/once-1.3.1.tgz", "dependencies": { "wrappy": { "version": "1.0.1", - "from": "wrappy@>=1.0.0 <2.0.0", + "from": "wrappy@1.0.1", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" } } @@ -1989,103 +1977,89 @@ } }, "minimatch": { - "version": "2.0.1", - "from": "minimatch@>=2.0.1 <3.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.1.tgz", + "version": "1.0.0", + "from": "minimatch@1.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz", "dependencies": { - "brace-expansion": { - "version": "1.0.1", - "from": "brace-expansion@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.0.1.tgz", - "dependencies": { - "balanced-match": { - "version": "0.2.0", - "from": "balanced-match@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" - }, - "concat-map": { - "version": "0.0.0", - "from": "concat-map@0.0.0", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.0.tgz" - } - } + "lru-cache": { + "version": "2.5.0", + "from": "lru-cache@2.5.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" + }, + "sigmund": { + "version": "1.0.0", + "from": "sigmund@1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" } } }, "ordered-read-streams": { - "version": "0.1.0", - "from": "ordered-read-streams@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz" + "version": "0.0.8", + "from": "ordered-read-streams@0.0.8", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.0.8.tgz" }, "glob2base": { - "version": "0.0.12", - "from": "glob2base@>=0.0.12 <0.0.13", - "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", - "dependencies": { - "find-index": { - "version": "0.1.1", - "from": "find-index@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz" - } - } + "version": "0.0.11", + "from": "glob2base@0.0.11", + "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.11.tgz" }, "unique-stream": { "version": "1.0.0", - "from": "unique-stream@>=1.0.0 <2.0.0", + "from": "unique-stream@1.0.0", "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-1.0.0.tgz" } } }, "glob-watcher": { "version": "0.0.6", - "from": "glob-watcher@>=0.0.6 <0.0.7", + "from": "glob-watcher@0.0.6", "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.6.tgz", "dependencies": { "gaze": { "version": "0.5.1", - "from": "gaze@>=0.5.1 <0.6.0", + "from": "gaze@0.5.1", "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.1.tgz", "dependencies": { "globule": { "version": "0.1.0", - "from": "globule@>=0.1.0 <0.2.0", + "from": "globule@0.1.0", "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", "dependencies": { "lodash": { "version": "1.0.1", - "from": "lodash@>=1.0.1 <1.1.0", + "from": "lodash@1.0.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.1.tgz" }, "glob": { "version": "3.1.21", - "from": "glob@>=3.1.21 <3.2.0", + "from": "glob@3.1.21", "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", "dependencies": { "graceful-fs": { "version": "1.2.3", - "from": "graceful-fs@>=1.2.0 <1.3.0", + "from": "graceful-fs@1.2.3", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" }, "inherits": { "version": "1.0.0", - "from": "inherits@>=1.0.0 <2.0.0", + "from": "inherits@1.0.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz" } } }, "minimatch": { "version": "0.2.14", - "from": "minimatch@>=0.2.11 <0.3.0", + "from": "minimatch@0.2.14", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "dependencies": { "lru-cache": { "version": "2.5.0", - "from": "lru-cache@>=2.0.0 <3.0.0", + "from": "lru-cache@2.5.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" }, "sigmund": { "version": "1.0.0", - "from": "sigmund@>=1.0.0 <1.1.0", + "from": "sigmund@1.0.0", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" } } @@ -2097,57 +2071,57 @@ } }, "graceful-fs": { - "version": "3.0.5", - "from": "graceful-fs@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.5.tgz" + "version": "3.0.4", + "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.4.tgz", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.4.tgz" }, "lodash": { "version": "2.4.1", - "from": "lodash@>=2.4.1 <3.0.0", + "from": "lodash@2.4.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz" }, "mkdirp": { "version": "0.5.0", - "from": "mkdirp@>=0.5.0 <0.6.0", + "from": "mkdirp@0.5.0", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", "dependencies": { "minimist": { "version": "0.0.8", - "from": "minimist@0.0.8", + "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } }, "strip-bom": { "version": "1.0.0", - "from": "strip-bom@>=1.0.0 <2.0.0", + "from": "strip-bom@1.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz", "dependencies": { "first-chunk-stream": { "version": "1.0.0", - "from": "first-chunk-stream@>=1.0.0 <2.0.0", + "from": "first-chunk-stream@1.0.0", "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz" }, "is-utf8": { "version": "0.2.0", - "from": "is-utf8@>=0.2.0 <0.3.0", + "from": "is-utf8@0.2.0", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.0.tgz" } } }, "through2": { "version": "0.6.3", - "from": "through2@>=0.6.1 <0.7.0", + "from": "https://registry.npmjs.org/through2/-/through2-0.6.3.tgz", "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.3.tgz", "dependencies": { "readable-stream": { - "version": "1.0.33", - "from": "readable-stream@>=1.0.33-1 <1.1.0-0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "version": "1.0.33-1", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33-1.tgz", "dependencies": { "core-util-is": { "version": "1.0.1", - "from": "core-util-is@>=1.0.0 <1.1.0", + "from": "core-util-is@1.0.1", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { @@ -2157,36 +2131,31 @@ }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", + "from": "string_decoder@0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", + "from": "inherits@2.0.1", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } }, "xtend": { "version": "4.0.0", - "from": "xtend@>=4.0.0 <4.1.0-0", + "from": "xtend@4.0.0", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" } } }, "vinyl": { - "version": "0.4.6", - "from": "vinyl@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "version": "0.4.3", + "from": "vinyl@0.4.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.3.tgz", "dependencies": { - "clone": { - "version": "0.2.0", - "from": "clone@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz" - }, "clone-stats": { "version": "0.0.1", - "from": "clone-stats@>=0.0.1 <0.0.2", + "from": "clone-stats@0.0.1", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" } } @@ -2195,12 +2164,12 @@ }, "xml2js": { "version": "0.4.4", - "from": "xml2js@0.4.4", + "from": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.4.tgz", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.4.tgz", "dependencies": { "sax": { "version": "0.6.1", - "from": "sax@>=0.6.0 <0.7.0", + "from": "sax@0.6.x", "resolved": "https://registry.npmjs.org/sax/-/sax-0.6.1.tgz" }, "xmlbuilder": { @@ -2210,7 +2179,7 @@ "dependencies": { "lodash-node": { "version": "2.4.1", - "from": "lodash-node@>=2.4.1 <2.5.0", + "from": "lodash-node@~2.4.1", "resolved": "https://registry.npmjs.org/lodash-node/-/lodash-node-2.4.1.tgz" } } diff --git a/package.json b/package.json index 685ae01890..38968a4557 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.2.9", + "version": "1.2.10", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 12117df3e0e15a2abef8960a658b5487accb159f Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Wed, 10 Dec 2014 11:50:18 -0600 Subject: [PATCH 0313/1100] Nicer lab styles --- lib/ionic/assets/preview.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/ionic/assets/preview.html b/lib/ionic/assets/preview.html index 61c4b7e0ed..48ae25928f 100644 --- a/lib/ionic/assets/preview.html +++ b/lib/ionic/assets/preview.html @@ -45,7 +45,8 @@ opacity: 1; } h2 a { - color: #222; + font-size: 18px; + color: #6F7582; text-decoration: none; } .phone { @@ -53,8 +54,9 @@ margin: 20px; } .frame { - border: 6px solid #121316; + border: 1px solid transparent; border-radius: 3px; + box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.2); } #iphone-frame { top: 109px; From 8bab19c2fd5a60d67b0be7a677ca6d0c71cb0180 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Wed, 10 Dec 2014 11:50:29 -0600 Subject: [PATCH 0314/1100] vbump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 38968a4557..ef64155837 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.2.10", + "version": "1.2.11", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 0796c9039d930b41127f4779f9f7c85512e46c96 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Wed, 10 Dec 2014 12:19:58 -0600 Subject: [PATCH 0315/1100] Better lab --- lib/ionic/assets/preview.html | 42 +++++++++++++++++++++++++---------- package.json | 2 +- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/lib/ionic/assets/preview.html b/lib/ionic/assets/preview.html index 48ae25928f..aad119a41f 100644 --- a/lib/ionic/assets/preview.html +++ b/lib/ionic/assets/preview.html @@ -1,18 +1,19 @@ - + - - - - - - - - - -
- - -
- - - -
-
-

iOS

- -
-
-
-
-

Android

- -
-
-
- - - - From 704fc2d468d7f05c44fcd749389b4d138c278842 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 20 Apr 2016 13:58:40 -0500 Subject: [PATCH 0957/1100] feat(stats): update stats to track --v2 and --ts flags; add os, gulp, and nodejs to tracked metadata --- lib/ionic/stats.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/ionic/stats.js b/lib/ionic/stats.js index 5d63fb3d6f..a276fe5daf 100644 --- a/lib/ionic/stats.js +++ b/lib/ionic/stats.js @@ -4,13 +4,15 @@ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments) var path = require('path'), http = require('http'), + _ = require('underscore'); querystring = require('querystring'), crypto = require('crypto'), Buffer = require('buffer').Buffer, util = require('util'), fs = require('fs'), os = require('os'), - IonicConfig = require('ionic-app-lib').config; + IonicConfig = require('ionic-app-lib').config, + IonicInfo = require('ionic-app-lib').info; exports.getVersion = function getVersion() { var packageJson = require('../../package.json'); @@ -564,7 +566,7 @@ exports.IonicStats = { } var platformWhitelist = 'android ios firefoxos wp7 wp8 amazon-fireos blackberry10 tizen'.split(' '); - var argsWhitelist = 'add remove list update check debug release search --livereload --consolelogs --serverlogs --no-cordova --nobrowser --nolivereload --noproxy --no-email --debug --release --device --emulator --sass --splash --icon'.split(' '); + var argsWhitelist = 'add remove list update check debug release search --livereload --consolelogs --serverlogs --no-cordova --nobrowser --nolivereload --noproxy --no-email --debug --release --device --emulator --sass --splash --icon --v2 --ts'.split(' '); // collect only certain args, skip over everything else for(x=0; x Date: Wed, 20 Apr 2016 15:12:39 -0500 Subject: [PATCH 0958/1100] chore(git): update git to ignore visual studio code dir and code coverage dir. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index d0acd17ee4..8a6b50dbe4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ *.cookies node_modules/ .idea/ +coverage/ +.vscode/ From d321eba6779ae153ad54320afc88f2d11f717546 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 20 Apr 2016 15:30:21 -0500 Subject: [PATCH 0959/1100] chore(test): add test coverage and ensure circleci runs coveralls. chore(test): add test coverage and ensure circleci runs coveralls. --- .gitignore | 1 + appveyor.yml | 31 +++++++++++++++++++++++++++++++ circle.yml | 6 ++++++ package.json | 9 ++++++--- 4 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 appveyor.yml create mode 100644 circle.yml diff --git a/.gitignore b/.gitignore index b9930655d1..9799c4a1f4 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules/ .idea/ .vscode/ +coverage/ diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000000..eac8ee9a94 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,31 @@ +# http://www.appveyor.com/docs/appveyor-yml + +# Test against these versions of Node.js. +environment: + matrix: + - nodejs_version: "0.12" + - nodejs_version: "4.2" + +# Install scripts. (runs after repo cloning) +install: + - git rev-parse HEAD + # Get the latest stable version of Node 0.STABLE.latest + - ps: Install-Product node $env:nodejs_version + # Typical npm stuff. + - npm version + - npm install + +cache: + - '%APPDATA%\npm-cache' + +# Post-install test scripts. +test_script: + # Output useful info for debugging. + - npm version + - cmd: npm run test + +# Don't actually build. +build: off + +# Set build version format here instead of in the admin panel. +version: "{build}" diff --git a/circle.yml b/circle.yml new file mode 100644 index 0000000000..a0d097163d --- /dev/null +++ b/circle.yml @@ -0,0 +1,6 @@ +test: + override: + - nvm use 0.12 && npm test + - nvm use 4.0 && npm test + post: + - npm run coveralls diff --git a/package.json b/package.json index e18f01d307..0bc9fc67e0 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,9 @@ "publish-tag": "node lib/tasks/bumpversion --level beta --npmPublishTag", "full-release": "node lib/tasks/bumpversion --npmInstall --git --npmPublish", "test": "npm run jasmine", - "e2e": "jasmine-node --captureExceptions ./e2e", - "jasmine": "jasmine-node --captureExceptions ./spec" + "coveralls": "istanbul cover node_modules/jasmine-node/bin/jasmine-node --captureExceptions spec/ && cat coverage/lcov.info | node_modules/coveralls/bin/coveralls.js && rm -rf coverage", + "e2e": "jasmine-node --captureExceptions e2e/", + "jasmine": "jasmine-node --captureExceptions spec/" }, "keywords": [ "ionic", @@ -88,8 +89,10 @@ "xml2js": "0.4.4" }, "devDependencies": { + "istanbul": "^0.4.3", "jasmine-node": "^1.14.5", - "rewire": "^2.3.4" + "rewire": "^2.3.4", + "coveralls": "^2.2.0" }, "bundledDependencies": [ "async", From bfe0f1f677eb889897e5659bd46aca9734a8b624 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 20 Apr 2016 15:52:43 -0500 Subject: [PATCH 0960/1100] chore(docs): update badges on readme to include appveyor and npm --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3e5242f3b1..d2b3dc465f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -[![Circle CI](https://circleci.com/gh/driftyco/ionic-cli.svg?style=svg)](https://circleci.com/gh/driftyco/ionic-cli) +[![Build Status][circle-badge]][circle-badge-url] +[![Build status][appveyor-badge]][appveyor-badge-url] +[![npm][npm-badge]][npm-badge-url] Ionic-Cli ========= @@ -585,3 +587,9 @@ The ionic.project is a configuration for an ionic project that stores the follow * `sass` - the setting to watch sass during `ionic serve`. * `watchPatterns` - the patterns to watch and live reload during `ionic.serve`. +[circle-badge]: https://circleci.com/gh/driftyco/ionic-cli.svg?style=shield +[circle-badge-url]: https://circleci.com/gh/driftyco/ionic-cli +[appveyor-badge]: https://ci.appveyor.com/api/projects/status/oqaqa7fdc7y9mma3?svg=true +[appveyor-badge-url]: https://ci.appveyor.com/project/jthoms1/ionic-cli +[npm-badge]: https://img.shields.io/npm/v/ionic.svg +[npm-badge-url]: https://www.npmjs.com/package/ionic From ee26541ab227bf140732687f36278384144b5707 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 21 Apr 2016 19:51:11 -0500 Subject: [PATCH 0961/1100] chore(build): add eslint and remove jscs --- .editorconfig | 15 +++++++++++ .eslintrc.js | 69 +++++++++++++++++++++++++++++++++++++++++++++++ .jscs.json | 41 ---------------------------- package.json | 1 + spec/.eslintrc.js | 5 ++++ 5 files changed, 90 insertions(+), 41 deletions(-) create mode 100644 .editorconfig create mode 100644 .eslintrc.js delete mode 100644 .jscs.json create mode 100644 spec/.eslintrc.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..f1cc3ad329 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +# http://editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000000..64b5fde258 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,69 @@ +module.exports = { + "env": { + "browser": true, + "commonjs": true + }, + "extends": "eslint:recommended", + "rules": { + "indent": [ + "error", + 2 + ], + "linebreak-style": [ + "error", + "unix" + ], + "quotes": [ + "error", + "single" + ], + "semi": [ + "error", + "always" + ], + "brace-style": [ + "error", + "1tbs", + { + "allowSingleLine": true + } + ], + "camelcase": [ + "error", + { + "properties": "always" + } + ], + "comma-style": [ + "error", + "last" + ], + "consistent-this": [ + "error", + "self" + ], + "new-cap": [ + "error" + ], + "no-extra-semi": "error", + "no-lonely-if": "error", + "no-underscore-dangle": "error", + "no-spaced-func": "error", + "space-before-function-paren": [ + 2, + "never" + ], + "keyword-spacing": [ + "error", + { + "before": true, + "after": true + } + ], + "space-before-blocks": [ + 2, + "always" + ] + } +}; + diff --git a/.jscs.json b/.jscs.json deleted file mode 100644 index 4e02926354..0000000000 --- a/.jscs.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "excludeFiles": ["src/ngLocale/**"], - "disallowKeywords": ["with"], - "disallowMixedSpacesAndTabs": true, - "disallowMultipleLineStrings": true, - "disallowNewlineBeforeBlockStatements": true, - "disallowSpaceAfterObjectKeys": true, - "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"], - "disallowSpaceBeforeBinaryOperators": [","], - "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"], - "disallowSpacesInAnonymousFunctionExpression": { - "beforeOpeningRoundBrace": true - }, - "disallowSpacesInFunctionDeclaration": { - "beforeOpeningRoundBrace": true - }, - "disallowSpacesInNamedFunctionExpression": { - "beforeOpeningRoundBrace": true - }, - "disallowSpacesInsideArrayBrackets": true, - "disallowSpacesInsideParentheses": true, - "disallowTrailingComma": true, - "disallowTrailingWhitespace": true, - "requireCommaBeforeLineBreak": true, - "requireLineFeedAtFileEnd": true, - "requireSpaceAfterBinaryOperators": ["?", ":", "+", "-", "/", "*", "%", "==", "===", "!=", "!==", ">", ">=", "<", "<=", "&&", "||"], - "requireSpaceBeforeBinaryOperators": ["?", ":", "+", "-", "/", "*", "%", "==", "===", "!=", "!==", ">", ">=", "<", "<=", "&&", "||"], - "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"], - "requireSpaceBeforeBlockStatements": true, - "requireSpacesInConditionalExpression": { - "afterTest": true, - "beforeConsequent": true, - "afterConsequent": true, - "beforeAlternate": true - }, - "requireSpacesInFunction": { - "beforeOpeningCurlyBrace": true - }, - "validateLineBreaks": "LF", - "validateParameterSeparator": ", " -} diff --git a/package.json b/package.json index 27f9a18f07..2329b09943 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,7 @@ "unzip": "0.1.9" }, "devDependencies": { + "eslint": "^2.8.0", "jasmine-node": "1.14.5", "rewire": "2.5.1" }, diff --git a/spec/.eslintrc.js b/spec/.eslintrc.js new file mode 100644 index 0000000000..d1f5e00643 --- /dev/null +++ b/spec/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + env: { + jasmine: true + } +}; From 93249cccc8cd65d54f7b79bfbbb7306f2ed166b1 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 21 Apr 2016 22:10:03 -0500 Subject: [PATCH 0962/1100] chore(build): change options in eslint rules --- .eslintrc.js | 175 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 130 insertions(+), 45 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 64b5fde258..25cffd5e16 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,69 +1,154 @@ module.exports = { - "env": { - "browser": true, - "commonjs": true + env: { + node: true, + commonjs: true }, - "extends": "eslint:recommended", - "rules": { - "indent": [ - "error", + extends: 'eslint:recommended', + rules: { + indent: [ + 'error', 2 ], - "linebreak-style": [ - "error", - "unix" + 'linebreak-style': [ + 'error', + 'unix' ], - "quotes": [ - "error", - "single" + 'lines-around-comment': [ + 'error', + { + beforeLineComment: true + } ], - "semi": [ - "error", - "always" + 'spaced-comment': [ + 'error', + 'always' ], - "brace-style": [ - "error", - "1tbs", + 'new-parens': 'error', + 'newline-per-chained-call': [ + 'error', { - "allowSingleLine": true + ignoreChainWithDepth: 3 } ], - "camelcase": [ - "error", + quotes: [ + 'error', + 'single' + ], + semi: [ + 'error', + 'always' + ], + 'semi-spacing': [ + 'error', { - "properties": "always" + before: false, + after: true } ], - "comma-style": [ - "error", - "last" + 'brace-style': [ + 'error', + '1tbs', + { + allowSingleLine: true + } ], - "consistent-this": [ - "error", - "self" + camelcase: [ + 'error', + { + properties: 'always' + } ], - "new-cap": [ - "error" + 'comma-style': [ + 'error', + 'last' ], - "no-extra-semi": "error", - "no-lonely-if": "error", - "no-underscore-dangle": "error", - "no-spaced-func": "error", - "space-before-function-paren": [ - 2, - "never" + 'comma-spacing': [ + 'error', + { + before: false, + after: true + } + ], + 'computed-property-spacing': [ + 'error', + 'never' ], - "keyword-spacing": [ - "error", + 'func-style': [ + 'error', + 'declaration', { - "before": true, - "after": true + allowArrowFunctions: true } ], - "space-before-blocks": [ + 'consistent-this': [ + 'error', + 'self' + ], + 'new-cap': [ + 'error' + ], + 'no-console': 0, + 'array-bracket-spacing': [ + 'error', + 'never' + ], + 'object-curly-spacing': [ + 'error', + 'always' + ], + 'max-len': [ + 'error', + 120, + 4 + ], + 'no-new-object': 'error', + 'quote-props': [ + 'error', + 'as-needed' + ], + 'block-spacing': 'error', + 'handle-callback-err': 'error', + 'no-new-require': 'error', + 'no-path-concat': 'error', + 'callback-return': 'error', + 'no-array-constructor': 'error', + 'no-useless-escape': 'error', + 'no-extra-semi': 'error', + 'no-lonely-if': 'error', + 'no-underscore-dangle': 'error', + 'no-spaced-func': 'error', + 'no-plusplus': 'error', + 'one-var-declaration-per-line': [ + 'error', + 'always' + ], + 'no-multiple-empty-lines': [ + 'error', + { + max: 2 + } + ], + 'guard-for-in': 'error', + 'keyword-spacing': [ + 'error', + { + before: true, + after: true + } + ], + 'space-before-function-paren': [ 2, - "always" - ] + 'never' + ], + 'space-before-blocks': [ + 2, + 'always' + ], + 'space-in-parens': [ + 'error', + 'never' + ], + 'space-infix-ops': 'error' } }; From b6a80dd2aa196eacbf935f9851321023350ae05e Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Sat, 23 Apr 2016 09:08:47 -0500 Subject: [PATCH 0963/1100] Eslint massive cleanup (#952) * fix(eslint): correct eslint errors in the e2e folder * fix(eslint): correct errors from lib/cli * fix(eslint): correct errors within spec files. * fix(eslint): correct eslint errors within hooks * fix(eslint): correct errors within lib/tasks * fix(eslint): resolve errors within ionic tasks * fix(eslint): correct issues introduced by eslint changes. --- .eslintrc.js | 28 +- e2e/.eslintrc.js | 6 + e2e/e2e.spec.js | 111 -- e2e/helpers.js | 40 +- e2e/shell.spec.js | 186 +--- lib/cli.js | 323 +++--- .../after_platform_add/010_install_plugins.js | 26 +- .../after_plugin_add/010_register_plugin.js | 40 +- .../after_plugin_rm/010_deregister_plugin.js | 9 +- .../after_prepare/010_add_platform_class.js | 39 +- .../020_remove_sass_from_platforms.js | 10 +- .../before_platform_add/init_directories.js | 12 +- lib/ionic/add.js | 87 +- lib/ionic/assets/gulpfile.js | 16 +- lib/ionic/bower.js | 26 +- lib/ionic/browser.js | 8 +- lib/ionic/cordova.js | 233 +++-- lib/ionic/docs.js | 51 +- lib/ionic/generate.js | 50 +- lib/ionic/help.js | 20 +- lib/ionic/hooks.js | 44 +- lib/ionic/info.js | 16 +- lib/ionic/io-config.js | 36 +- lib/ionic/io-init.js | 30 +- lib/ionic/ionitron.js | 107 +- lib/ionic/ions.js | 33 +- lib/ionic/lib.js | 177 ++-- lib/ionic/link.js | 36 +- lib/ionic/login.js | 84 +- lib/ionic/package.js | 104 +- lib/ionic/prompt.js | 8 +- lib/ionic/push.js | 368 ++++--- lib/ionic/resources.js | 23 +- lib/ionic/security.js | 135 +-- lib/ionic/serve.js | 36 +- lib/ionic/service.js | 126 +-- lib/ionic/setup.js | 5 +- lib/ionic/share.js | 38 +- lib/ionic/start.js | 48 +- lib/ionic/state.js | 96 +- lib/ionic/stats.js | 959 +++++++++--------- lib/ionic/store.js | 23 +- lib/ionic/table.js | 12 +- lib/ionic/task.js | 5 +- lib/ionic/templates.js | 42 +- lib/ionic/upload.js | 20 +- lib/ionic/utils.js | 32 +- lib/tasks/bumpversion.js | 46 +- lib/tasks/cliTasks.js | 132 +-- lib/tasks/createDocsJson.js | 40 +- spec/.eslintrc.js | 3 + spec/cli.spec.js | 150 +-- spec/serve.spec.js | 21 +- spec/start.spec.js | 11 +- spec/stats.spec.js | 33 +- 55 files changed, 2075 insertions(+), 2325 deletions(-) create mode 100644 e2e/.eslintrc.js delete mode 100644 e2e/e2e.spec.js diff --git a/.eslintrc.js b/.eslintrc.js index 25cffd5e16..0905e44435 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,6 +5,10 @@ module.exports = { }, extends: 'eslint:recommended', rules: { + eqeqeq: [ + "error", + "smart" + ], indent: [ 'error', 2 @@ -32,7 +36,10 @@ module.exports = { ], quotes: [ 'error', - 'single' + 'single', + { + avoidEscape: true + } ], semi: [ 'error', @@ -58,6 +65,15 @@ module.exports = { properties: 'always' } ], + "no-console": 0, + "no-extra-semi": "error", + "no-lonely-if": "error", + "no-underscore-dangle": "error", + "no-spaced-func": "error", + "space-before-function-paren": [ + 2, + "never" + ], 'comma-style': [ 'error', 'last' @@ -85,7 +101,10 @@ module.exports = { 'self' ], 'new-cap': [ - 'error' + 'error', + { + capIsNewExceptions: ['Q'] + } ], 'no-console': 0, 'array-bracket-spacing': [ @@ -99,7 +118,10 @@ module.exports = { 'max-len': [ 'error', 120, - 4 + 4, + { + ignoreUrls: true + } ], 'no-new-object': 'error', 'quote-props': [ diff --git a/e2e/.eslintrc.js b/e2e/.eslintrc.js new file mode 100644 index 0000000000..4f83ced97a --- /dev/null +++ b/e2e/.eslintrc.js @@ -0,0 +1,6 @@ +module.exports = { + env: { + node: true, + jasmine: true + } +}; diff --git a/e2e/e2e.spec.js b/e2e/e2e.spec.js deleted file mode 100644 index cf12370c20..0000000000 --- a/e2e/e2e.spec.js +++ /dev/null @@ -1,111 +0,0 @@ -// var fs = require('fs'), -// helpers = require('./helpers'), -// path = require('path'), -// Q = require('q'), -// shell = require('shelljs'); - -// var IonicCli = require('../lib/cli'); -// var IonicAppLib = require('ionic-app-lib'); - -// var tmpDir = helpers.tmpDir('create_test'); -// var appName = 'TestIonic'; -// var appId = 'org.ionic.testing'; -// var project = path.join(tmpDir, appName); -// var optimist = require('optimist'); -// var start = IonicAppLib.start; -// var utils = IonicAppLib.utils; -// var optimistSpy; - - -// xdescribe('end-to-end', function() { -// beforeEach(function() { -// jasmine.getEnv().defaultTimeoutInterval = 150000; -// // if (optimistSpy) { -// // optimistSpy.reset(); -// // } -// optimistSpy = spyOn(optimist, 'boolean'); -// optimist.boolean.reset(); - -// //Mock out call to get the app directory, return our project -// spyOn(utils, 'getProjectDirectory').andReturn(project); - -// //Disable console.log statements -// // spyOn(IonicAppLib.events, 'on'); -// // spyOn(process.stdout, 'write'); -// spyOn(IonicAppLib.multibar, 'newBar').andReturn({tick: function(){}}); - - -// shell.rm('-rf', project); -// shell.mkdir('-p', tmpDir); -// }); -// afterEach(function() { -// process.chdir(path.join(__dirname, '..')); // Needed to rm the dir on Windows. -// // shell.rm('-rf', tmpDir); -// }); - -// describe('#start e2e', function() { -// it('should call start with default template and folder name', function(done) { -// console.log('default template'); -// var args = { _: ['start', 'test'], verbose: true}; -// //Mock out args from the commands. -// // optimistSpy.andReturn({argv: args}); -// optimistSpy.andCallFake(function(){ -// return {argv: args}; -// }); - -// Q() -// .then(function() { -// return IonicCli.run(args); -// }).then(function(){ -// expect(path.join(project, 'www', 'index.html')).toExist(); -// expect(path.join(project, 'www', 'templates', 'tabs.html')).toExist(); -// }) -// .catch(function(error) { -// expect('this').toBe('not this'); -// }) -// .fin(done); -// }); - -// it('should call start with sidemenu template and folder name', function(done) { -// console.log('sidemenu template'); -// var args = { _: ['start', 'test', 'sidemenu'], verbose: true}; -// //Mock out args from the commands. -// // optimistSpy.andReturn({argv: args}); -// optimistSpy.andCallFake(function(){ -// return {argv: args}; -// }); - -// Q() -// .then(function() { -// return IonicCli.run(args); -// }).then(function(){ -// expect(path.join(project, 'www', 'index.html')).toExist(); -// expect(path.join(project, 'www', 'templates', 'menu.html')).toExist(); -// }) -// .catch(function(error) { -// expect('this').toBe('not this'); -// }) -// .fin(done); -// }); - -// it('should call start with blank template and folder name', function(done) { -// var args = { _: ['start', 'test', 'blank'], verbose: true}; -// //Mock out args from the commands. -// optimistSpy.andCallFake(function(){ -// return {argv: args}; -// }); - -// Q() -// .then(function() { -// return IonicCli.run(args); -// }).then(function(){ -// expect(path.join(project, 'www', 'index.html')).toExist(); -// expect(path.join(project, 'www', 'js', 'app.js')).toExist(); -// }) -// .catch(function(error) { -// expect('this').toBe('not this'); -// }) -// .fin(done); -// }); -// }); -// }); diff --git a/e2e/helpers.js b/e2e/helpers.js index 3db75d5c96..0a9032424d 100644 --- a/e2e/helpers.js +++ b/e2e/helpers.js @@ -17,18 +17,18 @@ under the License. */ -var path = require('path'), - fs = require('fs'), - shell = require('shelljs'), - os = require('os'); +var path = require('path'); +var fs = require('fs'); +var shell = require('shelljs'); +var os = require('os'); module.exports.tmpDir = function(subdir) { - var dir = path.join(os.tmpdir(), 'e2e-test'); - if (subdir) { - dir = path.join(dir, subdir); - } - shell.mkdir('-p', dir); - return dir; + var dir = path.join(os.tmpdir(), 'e2e-test'); + if (subdir) { + dir = path.join(dir, subdir); + } + shell.mkdir('-p', dir); + return dir; }; // Returns the platform that should be used for testing on this host platform. @@ -48,17 +48,17 @@ module.exports.testPlatform = 'android'; // Add the toExist matcher. beforeEach(function() { - this.addMatchers({ - 'toExist': function() { - var notText = this.isNot ? ' not' : ''; - var self = this; + this.addMatchers({ + toExist: function() { + var notText = this.isNot ? ' not' : ''; + var self = this; - this.message = function() { - return 'Expected file ' + self.actual + notText + ' to exist.'; - }; + this.message = function() { + return 'Expected file ' + self.actual + notText + ' to exist.'; + }; - return fs.existsSync(this.actual); - } - }); + return fs.existsSync(this.actual); + } + }); }); diff --git a/e2e/shell.spec.js b/e2e/shell.spec.js index 052e7782cb..e0939f8173 100644 --- a/e2e/shell.spec.js +++ b/e2e/shell.spec.js @@ -1,22 +1,14 @@ -var fs = require('fs'), - helpers = require('./helpers'), - path = require('path'), - Q = require('q'), - request = require('request'), - shell = require('shelljs'); +var helpers = require('./helpers'); +var path = require('path'); +var shell = require('shelljs'); -var IonicCli = require('../lib/cli'); var IonicAppLib = require('ionic-app-lib'); -var Serve = IonicAppLib.serve; var tmpDir = helpers.tmpDir('create_test'); var appName = 'TestIonic'; -var appId = 'org.ionic.testing'; var project = path.join(tmpDir, appName); var optimist = require('optimist'); -var start = IonicAppLib.start; var utils = IonicAppLib.utils; -var optimistSpy; // What to test @@ -24,181 +16,47 @@ var optimistSpy; // * [ ] Start // * [ ] Serve // * [ ] Run -// * [ ] +// * [ ] -ddescribe('end-to-end', function() { +describe('end-to-end', function() { beforeEach(function() { - jasmine.getEnv().defaultTimeoutInterval = 150000; - // if (optimistSpy) { - // optimistSpy.reset(); - // } - optimistSpy = spyOn(optimist, 'boolean'); - optimist.boolean.reset(); + jasmine.getEnv().defaultTimeoutInterval = 150000; + optimist.boolean.reset(); - //Mock out call to get the app directory, return our project - spyOn(utils, 'getProjectDirectory').andReturn(project); + // Mock out call to get the app directory, return our project + spyOn(utils, 'getProjectDirectory').andReturn(project); + spyOn(IonicAppLib.multibar, 'newBar').andReturn({ tick: function() {} }); - //Disable console.log statements - // spyOn(IonicAppLib.events, 'on'); - // spyOn(process.stdout, 'write'); - spyOn(IonicAppLib.multibar, 'newBar').andReturn({tick: function(){}}); - - - console.log('Removing project', project); - shell.rm('-rf', project); - shell.mkdir('-p', tmpDir); - - //Copy over created project here. + console.log('Removing project', project); + shell.rm('-rf', project); + shell.mkdir('-p', tmpDir); + // Copy over created project here. }); afterEach(function() { - process.chdir(path.join(__dirname, '..')); // Needed to rm the dir on Windows. - // shell.rm('-rf', tmpDir); + process.chdir(path.join(__dirname, '..')); // Needed to rm the dir on Windows. + // shell.rm('-rf', tmpDir); }); - //This is working, Tim! - describe('#start', function(){ + // This is working, Tim! + describe('#start', function() { it('should start a v2 app without cordova cli', function() { console.log('Starting v2 app'); shell.cd(tmpDir); + // shell.rm('-rf', path.join(project, 'i2')); - shell.exec('ionic start ' + appName +' --v2'); + shell.exec('ionic start ' + appName + ' --v2'); console.log('Ionic v2 app start completed. Now checking files existing.'); expect(path.join(project, 'node_modules', 'ionic-framework')).toExist(); expect(path.join(project, 'www', 'index.html')).toExist(); + // expect(path.join(project, 'hooks', 'before_prepare', '01_gulp_build.js')).toExist(); console.log('v2 start completed'); + // done(); return; }); }); - - // xdescribe('#start', function(){ - // xit('should start a new app', function(done) { - // shell.cd(tmpDir); - // shell.exec('echo "yes" | ionic start s1'); - // // expect(path.join(project, 's1', 'www', 'index.html')).toExist(); - // // expect(path.join(project, 's1', 'www', 'templates', 'tabs.html')).toExist(); - // shell.cd(path.join(tmpDir, 's1')); - - // // var deferred = Q.defer(); - - // // spyOn(Serve, 'showFinishedServeMessage').andReturn(deferred.promise); - - // Q() - // .then(function() { - // // var deferred = Q.defer(); - // // shell.exec('ionic serve -b & ', {async: true}); - // // deferred.resolve(); - // // }); - // // return deferred.promise; - // }) - // .then(function(){ - // console.log('ionic serve is done'); - // var deferred = Q.defer(); - - // request({ url: 'http://0.0.0.0:8100' }, function(err, res, body) { - // try { - // console.log('body returned', body); - // deferred.resolve(body); - // } catch(e) { - // deferred.reject(body); - // } - // }); - // return deferred.promise; - // }) - // .then(function(result) { - // console.log('result:', result); - // expect(result).toBe(true); - // }) - // .catch(function(err){ - // expect('this').toBe('not this'); - // }) - // .fin(done); - - // }); - - // iit('should start a new app and run it', function() { - // shell.cd(tmpDir); - // shell.exec('echo "yes" | ionic start ' + appName); - // shell.cd(project); - - // expect(path.join(project, 'www', 'index.html')).toExist(); - // expect(path.join(project, 'www', 'templates', 'tabs.html')).toExist(); - - // shell.exec('ionic plugin add org.apache.cordova.splashscreen'); - - // expect(path.join(project, 'plugins', 'org.apache.cordova.splashscreen', 'plugin.xml')).toExist(); - - // shell.exec('ionic platform add android'); - - // expect(path.join(project, 'platforms', 'android', 'AndroidManifest.xml')).toExist(); - // expect(path.join(project, 'resources', 'icon.png')).toExist(); - - // shell.exec('ionic hooks add'); - // expect(path.join(project, 'hooks', 'after_plugin_add', '010_register_plugin.js')).toExist(); - - // shell.exec('ionic hooks remove'); - // expect(path.join(project, 'hooks', 'after_plugin_add', '010_register_plugin.js')).not.toExist(); - - // shell.exec('ionic build ios'); - // expect(path.join(project, 'platforms', 'ios', 'build', 'emulator', [appName, '.app'].join(''), 'config.xml')).toExist(); - - // shell.exec('ionic build android'); - // //NOTE this expects you're using ant to build. In cordova android 4.0.0 - gradle is used, ant-build wont be there. - // expect(path.join(project, 'platforms', 'android', 'ant-build', 'MainActivity-debug.apk')).toExist(); - // expect(path.join(project, 'platforms', 'android', 'build.xml')).toExist(); - - // shell.exec('ionic browser add crosswalk'); - // expect(path.join(project, 'engine', 'xwalk-webviews')).toExist(); - - // shell.exec('ionic build android'); - // expect(path.join(project, 'platforms', 'android', 'gradle.properties')).toExist(); - // expect(path.join(project, 'platforms', 'android', 'build', 'outputs', 'apk', 'android-armv7-debug.apk')).toExist(); - // expect(path.join(project, 'platforms', 'android', 'build', 'outputs', 'apk', 'android-x86-debug.apk')).toExist(); - - // }); - - // iit('should start new v2 app', function() { - // shell.cd(tmpDir); - // shell.exec('echo "yes" | ionic start ' + appName); - // shell.cd(project); - - // expect(path.join(project, 'www', 'index.html')).toExist(); - // expect(path.join(project, 'www', 'templates', 'tabs.html')).toExist(); - - // shell.exec('ionic plugin add org.apache.cordova.splashscreen'); - - // expect(path.join(project, 'plugins', 'org.apache.cordova.splashscreen', 'plugin.xml')).toExist(); - - // shell.exec('ionic platform add android'); - - // expect(path.join(project, 'platforms', 'android', 'AndroidManifest.xml')).toExist(); - // expect(path.join(project, 'resources', 'icon.png')).toExist(); - - // shell.exec('ionic hooks add'); - // expect(path.join(project, 'hooks', 'after_plugin_add', '010_register_plugin.js')).toExist(); - - // shell.exec('ionic hooks remove'); - // expect(path.join(project, 'hooks', 'after_plugin_add', '010_register_plugin.js')).not.toExist(); - - // shell.exec('ionic build ios'); - // expect(path.join(project, 'platforms', 'ios', 'build', 'emulator', [appName, '.app'].join(''), 'config.xml')).toExist(); - - // shell.exec('ionic build android'); - // //NOTE this expects you're using ant to build. In cordova android 4.0.0 - gradle is used, ant-build wont be there. - // expect(path.join(project, 'platforms', 'android', 'ant-build', 'MainActivity-debug.apk')).toExist(); - // expect(path.join(project, 'platforms', 'android', 'build.xml')).toExist(); - - // shell.exec('ionic browser add crosswalk'); - // expect(path.join(project, 'engine', 'xwalk-webviews')).toExist(); - - // shell.exec('ionic build android'); - // expect(path.join(project, 'platforms', 'android', 'gradle.properties')).toExist(); - // expect(path.join(project, 'platforms', 'android', 'build', 'outputs', 'apk', 'android-armv7-debug.apk')).toExist(); - // expect(path.join(project, 'platforms', 'android', 'build', 'outputs', 'apk', 'android-x86-debug.apk')).toExist(); - // }) - // }); }); diff --git a/lib/cli.js b/lib/cli.js index 871a15211b..90cda0fd16 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -1,30 +1,27 @@ -var Cli = module.exports, - colors = require('colors'), - IonicAppLib = require('ionic-app-lib'), - ConfigXml = IonicAppLib.configXml, - IonicStats = require('./ionic/stats').IonicStats, - IonicStore = require('./ionic/store').IonicStore, - IonicConfig = new IonicStore('ionic.config'), - Info = IonicAppLib.info, - IonicProject = IonicAppLib.project, - Ionitron = require('./ionic/ionitron'), - optimist = require('optimist'), - path = require('path'), - fs = require('fs'), - settings = require('../package.json'), - Tasks = require('./tasks/cliTasks'), - Utils = IonicAppLib.utils, - Logging = IonicAppLib.logging, - log = Logging.logger, - Q = require('q'), - semver = require('semver'), - gutil = require('gulp-util'), - prettyTime = require('pretty-hrtime'); - -Cli.Tasks = TASKS = Tasks; +var Cli = module.exports; +var IonicAppLib = require('ionic-app-lib'); +var IonicStats = require('./ionic/stats').IonicStats; +var IonicStore = require('./ionic/store').IonicStore; +var IonicConfig = new IonicStore('ionic.config'); +var Info = IonicAppLib.info; +var IonicProject = IonicAppLib.project; +var Ionitron = require('./ionic/ionitron'); +var optimist = require('optimist'); +var path = require('path'); +var fs = require('fs'); +var settings = require('../package.json'); +var Tasks = require('./tasks/cliTasks'); +var Utils = IonicAppLib.utils; +var Logging = IonicAppLib.logging; +var log = Logging.logger; +var Q = require('q'); +var semver = require('semver'); +var gutil = require('gulp-util'); +var prettyTime = require('pretty-hrtime'); + +var TASKS = Cli.Tasks = Tasks; Cli.IONIC_DASH = 'https://apps.ionic.io'; -//Cli.IONIC_DASH = 'http://localhost:8000'; Cli.IONIC_API = '/api/v1/'; Cli.PRIVATE_PATH = '.ionic'; @@ -48,27 +45,28 @@ log.level = 'info'; // This way, we can still test with passing in arguments. Cli.run = function run(processArgv) { try { - //First we parse out the args to use them. - //Later, we will fetch the command they are trying to - //execute, grab those options, and reparse the arguments. + + // First we parse out the args to use them. + // Later, we will fetch the command they are trying to + // execute, grab those options, and reparse the arguments. var argv = optimist(processArgv.slice(2)).argv; Cli.attachErrorHandling(); Cli.checkLatestVersion(); - process.on('exit', function(){ + process.on('exit', function() { Cli.printVersionWarning(); }); - //Before taking any more steps, lets check their - //environment and display any upgrade warnings + // Before taking any more steps, lets check their + // environment and display any upgrade warnings Cli.doRuntimeCheck(settings.version); if ((argv.version || argv.v) && !argv._.length) { return Cli.version(); } - if(argv.ionitron) { + if (argv.ionitron) { var lang = argv.es ? 'es' : 'en'; return Ionitron.print(lang); } @@ -77,11 +75,11 @@ Cli.run = function run(processArgv) { log.level = 'debug'; } - if(argv.help || argv.h) { + if (argv.help || argv.h) { return Cli.printHelpLines(); } - if(argv['stats-opt-out']) { + if (argv['stats-opt-out']) { IonicConfig.set('statsOptOut', true); IonicConfig.save(); log.info('Successful stats opt-out'); @@ -91,16 +89,16 @@ Cli.run = function run(processArgv) { } var taskSetting = Cli.tryBuildingTask(argv); - if(!taskSetting) { + if (!taskSetting) { return Cli.printAvailableTasks(); } log.debug('Task setting:', taskSetting); var booleanOptions = Cli.getBooleanOptionsForTask(taskSetting); argv = optimist(processArgv.slice(2)).boolean(booleanOptions).argv; - var taskModule = Cli.lookupTask(taskSetting.module); - var taskInstance = new taskModule(); + var TaskModule = Cli.lookupTask(taskSetting.module); + var taskInstance = new TaskModule(); - //this should be tracked by each task + // this should be tracked by each task if (!taskSetting.disableChangePwd) { var root = Utils.cdIonicRoot(); var project = IonicProject.load(root); @@ -108,7 +106,8 @@ Cli.run = function run(processArgv) { argv.v2 && log.debug('Ionic 2 project.'); if (fs.existsSync('node_modules')) { - //run with gulp hooks + + // run with gulp hooks return runWithGulp(argv, taskInstance); } else if (argv.v2) { log.warn('WARN: No node_modules directory found, do you need to run npm install?'.yellow); @@ -121,7 +120,7 @@ Cli.run = function run(processArgv) { } }; -function runWithGulp(argv, taskInstance){ +function runWithGulp(argv, taskInstance) { var cmdName = argv._[0]; var beforeHook = cmdName + ':before'; var afterHook = cmdName + ':after'; @@ -131,7 +130,8 @@ function runWithGulp(argv, taskInstance){ try { var gulp = require(path.resolve(process.cwd() + '/node_modules/gulp')); - } catch(e) { + } catch (e) { + // Empty gulpfile (or one that doesn't require gulp?), and no gulp log.error('\nGulpfile detected, but gulp is not installed'.red); log.error('Do you need to run `npm install`?\n'.red); @@ -143,31 +143,35 @@ function runWithGulp(argv, taskInstance){ log.info('\nRunning \'' + beforeHook + '\' gulp task before ' + cmdName); } return Q.nfcall(gulp.start.bind(gulp), beforeHook).then( - function(){ + function() { + // Only some commands return promises, so wrap it return Q.fcall(taskInstance.run.bind(taskInstance), Cli, argv); }, - function(e){ //task error, let gulp handle it - //if there is no hook task, but it's a v2 app and a command that usually needs a hook + function(e) { // task error, let gulp handle it + + // if there is no hook task, but it's a v2 app and a command that usually needs a hook if (e.missingTask && /serve|build|run|emulate|upload/.test(cmdName) && argv.v2) { var taskName = (cmdName === 'serve') ? 'watch' : 'build'; log.warn(('WARN: No \'' + beforeHook + '\' gulp task found!').yellow); - //The task exists, but there are no hooks for it - //They have the old config file, so we're safe to yell at them - //With new config, may intentionally not have hooks if running their own build + // The task exists, but there are no hooks for it + // They have the old config file, so we're safe to yell at them + // With new config, may intentionally not have hooks if running their own build if (gulp.tasks[taskName] && fs.existsSync('ionic.config.js')) { - log.info(('Your gulpfile contains a \'' + taskName + '\' task already! Add:\n').cyan) + log.info(('Your gulpfile contains a \'' + taskName + '\' task already! Add:\n').cyan); log.info((' gulp.task(\'' + cmdName + ':before\', [\'' + taskName + '\']);\n').cyan); log.info(('to your gulpfile to have Ionic CLI run \'' + taskName + '\' before ' + cmdName + '.\n').cyan); } else { - log.warn(('If your app requires a build step, you may want to ensure it runs before ' + cmdName + '.\n').yellow); + log.warn(('If your app requires a build step, you may want to ensure it runs before ' + + cmdName + '.\n').yellow); } } + // Only some commands return promises, so wrap it return Q.fcall(taskInstance.run.bind(taskInstance), Cli, argv); } - ).then(function(){ + ).then(function() { if (gulp.tasks[afterHook]) { log.info('\nRunning \'' + afterHook + '\' gulp task after ' + cmdName); } @@ -175,7 +179,7 @@ function runWithGulp(argv, taskInstance){ }); } - //No gulpfile + // No gulpfile if (/serve|build|run|emulate|upload/.test(cmdName) && argv.v2) { log.warn('WARN: No gulpfile found!'); log.warn('If your app requires a build step, you may want to ensure it runs before ' + cmdName + '.\n'); @@ -189,15 +193,13 @@ function runWithGulp(argv, taskInstance){ inquirer.prompt([{ type: 'confirm', name: 'downloadGulp', - message: 'Would you like to download the default gulpfile?'.cyan, + message: 'Would you like to download the default gulpfile?'.cyan }], - function(answers){ + function(answers) { answers.downloadGulp ? deferred.resolve() : deferred.reject(); }); - var downloadGulp = false; return deferred.promise.then( function() { - downloadGulp = true; return downloadDefaultGulpfile(cmdName).then( installGulpfilePackages, function(err) { @@ -205,13 +207,12 @@ function runWithGulp(argv, taskInstance){ process.exit(1); } ).then( - function(){ + function() { log.info('Npm packages installed successfully. Try running \'' + cmdName + '\' again.'); }, - function(){ + function() { log.warn('There was an error installing the packages required by the gulpfile.'); log.warn('You\'ll need to install the following packages manually: '); - log.warn(' ' + requires.join(' ')); } ); }, @@ -225,14 +226,15 @@ function runWithGulp(argv, taskInstance){ return Q.fcall(taskInstance.run.bind(taskInstance), Cli, argv); } -function loadGulpfile(){ - //TODO add babel support? +function loadGulpfile() { + + // TODO add babel support? var names = ['gulpfile.js', 'Gulpfile.js']; - for (var i = 0, ii = names.length; i < ii; i++) { + for (var i = 0, ii = names.length; i < ii; i += 1) { try { require(path.resolve(process.cwd() + '/' + names[i])); return true; - } catch(e){ + } catch (e) { if (e.code === 'MODULE_NOT_FOUND') { if (e.message.indexOf(names[i]) === -1) { log.info('Uh oh! Looks like you\'re missing a module in your gulpfile:'); @@ -240,11 +242,12 @@ function loadGulpfile(){ log.info('\nDo you need to run `npm install`?\n'); process.exit(1); } else { + // ignore missing gulpfile - continue + continue; } } - log.error('There is an error in your gulpfile: ') + log.error('There is an error in your gulpfile: '); log.error(e.stack); process.exit(1); } @@ -252,7 +255,7 @@ function loadGulpfile(){ return false; } -function downloadDefaultGulpfile(){ +function downloadDefaultGulpfile() { var https = require('https'); var fileStream = fs.createWriteStream('gulpfile.js'); var deferred = Q.defer(); @@ -262,24 +265,24 @@ function downloadDefaultGulpfile(){ log.info('\nDownloading default gulpfile from: ' + url); - fileStream.on('open', function () { - https.get(url, function (res) { - res.on('error', function (err) { + fileStream.on('open', function() { + https.get(url, function(res) { + res.on('error', function(err) { deferred.reject(err); }); res.pipe(fileStream); }); - }).on('error', function (err) { + }).on('error', function(err) { deferred.reject(err); - }).on('finish', function () { + }).on('finish', function() { deferred.resolve(); }); return deferred.promise; } -function installGulpfilePackages(cmdName){ +function installGulpfilePackages() { var detective = require('detective'); var requires = detective(fs.readFileSync('gulpfile.js')); var deferred = Q.defer(); @@ -289,8 +292,8 @@ function installGulpfilePackages(cmdName){ log.info(' ' + requires.join(' ') + '\n'); var npmInstall = require('child_process').spawn('npm', ['install', '--save-dev'].concat(requires)); - npmInstall.stdout.on('data', function(data){ log.debug(data) }); - npmInstall.stderr.on('data', function(data){ log.debug(data) }); + npmInstall.stdout.on('data', function(data) { log.debug(data); }); + npmInstall.stderr.on('data', function(data) { log.debug(data); }); npmInstall.on('error', function(err) { log.error('Error in gulp npm install: ' + err.stack); }); @@ -304,6 +307,7 @@ function installGulpfilePackages(cmdName){ function logEvents(gulpInst, finalTaskNames) { gulpInst.on('task_start', function(e) { + // TODO: batch these // so when 5 tasks start at once it only logs one time with all 5 gutil.log('Starting', '\'' + e.task.cyan + '\'...'); @@ -372,7 +376,7 @@ Cli.getBooleanOptionsForTask = function getBooleanOptionsForTask(task) { var optionWithPipe = key; var optionsSplit = optionWithPipe.split('|'); booleanOptions.push(optionsSplit[0].substring(2)); - if (optionsSplit.length == 2) { + if (optionsSplit.length === 2) { booleanOptions.push(optionsSplit[1].substring(1)); } } @@ -395,13 +399,13 @@ Cli.printVersionWarning = function printVersionWarning() { return; } - if (Cli.npmVersion && Cli.npmVersion != settings.version.trim()) { + if (Cli.npmVersion && Cli.npmVersion !== settings.version.trim()) { process.stdout.write('\n------------------------------------\n'.red); process.stdout.write('Ionic CLI is out of date:\n'.bold.yellow); - process.stdout.write( (' * Locally installed version: ' + settings.version + '\n').yellow ); - process.stdout.write( (' * Latest version: ' + Cli.npmVersion + '\n').yellow ); - process.stdout.write( (' * https://github.com/driftyco/ionic-cli/blob/master/CHANGELOG.md\n').yellow ); - process.stdout.write( ' * Run '.yellow + 'npm install -g ionic'.bold + ' to update\n'.yellow ); + process.stdout.write((' * Locally installed version: ' + settings.version + '\n').yellow); + process.stdout.write((' * Latest version: ' + Cli.npmVersion + '\n').yellow); + process.stdout.write((' * https://github.com/driftyco/ionic-cli/blob/master/CHANGELOG.md\n').yellow); + process.stdout.write(' * Run '.yellow + 'npm install -g ionic'.bold + ' to update\n'.yellow); process.stdout.write('------------------------------------\n\n'.red); Cli.npmVersion = null; } @@ -412,13 +416,15 @@ Cli.checkLatestVersion = function checkLatestVersion() { try { if (settings.version.indexOf('beta') > -1 || settings.version.indexOf('alpha') > -1) { + // don't bother checking if its a beta Cli.latestVersion.resolve(); return; } var versionCheck = IonicConfig.get('versionCheck'); - if (versionCheck && ((versionCheck + 86400000) > Date.now() )) { + if (versionCheck && ((versionCheck + 86400000) > Date.now())) { + // we've recently checked for the latest version, so don't bother again Cli.latestVersion.resolve(); return; @@ -427,11 +433,16 @@ Cli.checkLatestVersion = function checkLatestVersion() { var proxy = process.env.PROXY || process.env.http_proxy || null; var request = require('request'); request({ url: 'http://registry.npmjs.org/ionic/latest', proxy: proxy }, function(err, res, body) { + if (err) { + return console.log(err); + } try { Cli.npmVersion = JSON.parse(body).version; IonicConfig.set('versionCheck', Date.now()); IonicConfig.save(); - } catch(e) {} + } catch (e) { + console.log(e); + } Cli.latestVersion.resolve(); }); } catch (e) { @@ -451,13 +462,13 @@ Cli.tryBuildingTask = function tryBuildingTask(argv) { }; Cli.getTaskWithName = function getTaskWithName(name) { - for (var i = 0; i < TASKS.length; i++) { + for (var i = 0; i < TASKS.length; i += 1) { var t = TASKS[i]; - if(t.name === name) { + if (t.name === name) { return t; } if (t.alt) { - for(var j = 0; j < t.alt.length; j++) { + for (var j = 0; j < t.alt.length; j += 1) { var alt = t.alt[j]; if (alt === name) { return t; @@ -468,30 +479,30 @@ Cli.getTaskWithName = function getTaskWithName(name) { }; Cli.printIonic = function printIonic() { - var w = function(s) { + function w(s) { process.stdout.write(s); - }; + } w(' _ _ \n'); w(' (_) (_) \n'); w(' _ ___ _ __ _ ___ \n'); w(' | |/ _ \\| \'_ \\| |/ __|\n'); w(' | | (_) | | | | | (__ \n'); - w(' |_|\\___/|_| |_|_|\\___| CLI v'+ settings.version + '\n'); + w(' |_|\\___/|_| |_|_|\\___| CLI v' + settings.version + '\n'); }; -Cli.printAvailableTasks = function printAvailableTasks(argv) { +Cli.printAvailableTasks = function printAvailableTasks() { Cli.printIonic(); process.stderr.write('\nUsage: ionic task args\n\n=======================\n\n'); if (process.argv.length > 2) { - process.stderr.write( (process.argv[2] + ' is not a valid task\n\n').bold.red ); + process.stderr.write((process.argv[2] + ' is not a valid task\n\n').bold.red); } process.stderr.write('Available tasks: '.bold); process.stderr.write('(use --help or -h for more info)\n\n'); - for (var i = 0; i < TASKS.length; i++) { + for (var i = 0; i < TASKS.length; i += 1) { var task = TASKS[i]; if (task.summary) { var name = ' ' + task.name + ' '; @@ -511,7 +522,7 @@ Cli.printHelpLines = function printHelpLines() { Cli.printIonic(); process.stderr.write('\n=======================\n'); - for (var i = 0; i < TASKS.length; i++) { + for (var i = 0; i < TASKS.length; i += 1) { var task = TASKS[i]; if (task.summary) { Cli.printUsage(task); @@ -523,104 +534,109 @@ Cli.printHelpLines = function printHelpLines() { }; Cli.printUsage = function printUsage(d) { - var w = function(s) { + function w(s) { process.stdout.write(s); - }; + } w('\n'); var rightColumn = 45; var dots = ''; var indent = ''; - var x, arg; + var x; + var arg; var taskArgs = d.title; - for(arg in d.args) { - taskArgs += ' ' + arg; + for (arg in d.args) { + if ({}.hasOwnProperty.call(d.args, arg)) { + taskArgs += ' ' + arg; + } } w(taskArgs.green.bold); - while( (taskArgs + dots).length < rightColumn + 1) { + while ((taskArgs + dots).length < rightColumn + 1) { dots += '.'; } w(' ' + dots.grey + ' '); - if(d.summary) { + if (d.summary) { w(d.summary.bold); } - for(arg in d.args) { - if( !d.args[arg] ) continue; + for (arg in d.args) { + if (!d.args[arg]) continue; indent = ''; w('\n'); - while(indent.length < rightColumn) { + while (indent.length < rightColumn) { indent += ' '; } - w( (indent + ' ' + arg + ' ').bold ); + w((indent + ' ' + arg + ' ').bold); var argDescs = d.args[arg].split('\n'); var argIndent = indent + ' '; - for(x=0; x]*id="(.*)"', 'i'); - id = idRegEx.exec(configString)[1] - pluginToAdd = {id: id, locator: plugin}; - } catch(ex) { + id = idRegEx.exec(configString)[1]; + pluginToAdd = { id: id, locator: plugin }; + } catch (ex) { console.log('There was an error retrieving the plugin.xml filr from the 010_register_plugin.js hook', ex); } } else { pluginToAdd = plugin; } - if(typeof pluginToAdd == 'string' && packageJSON.cordovaPlugins.indexOf(pluginToAdd) == -1) { + if (typeof pluginToAdd == 'string' && packageJSON.cordovaPlugins.indexOf(pluginToAdd) === -1) { packageJSON.cordovaPlugins.push(pluginToAdd); } else if (typeof pluginToAdd == 'object') { var pluginExists = false; packageJSON.cordovaPlugins.forEach(function(checkPlugin) { - if(typeof checkPlugin == 'object' && checkPlugin.id == pluginToAdd.id) { + if (typeof checkPlugin == 'object' && checkPlugin.id === pluginToAdd.id) { pluginExists = true; } - }) - if(!pluginExists) { + }); + if (!pluginExists) { packageJSON.cordovaPlugins.push(pluginToAdd); } } diff --git a/lib/hooks/after_plugin_rm/010_deregister_plugin.js b/lib/hooks/after_plugin_rm/010_deregister_plugin.js index 4f4c6b892e..cc83632398 100644 --- a/lib/hooks/after_plugin_rm/010_deregister_plugin.js +++ b/lib/hooks/after_plugin_rm/010_deregister_plugin.js @@ -8,15 +8,16 @@ var packageJSON = require('../../package.json'); packageJSON.cordovaPlugins = packageJSON.cordovaPlugins || []; -process.env.CORDOVA_PLUGINS.split(',').forEach(function (plugin) { +process.env.CORDOVA_PLUGINS.split(',').forEach(function(plugin) { var index = packageJSON.cordovaPlugins.indexOf(plugin); if (index > -1) { packageJSON.cordovaPlugins.splice(index, 1); } else { - //If it didnt find a match, it may be listed as {id,locator} - for(var i = 0, j = packageJSON.cordovaPlugins.length; i < j; i++) { + + // If it didnt find a match, it may be listed as {id,locator} + for (var i = 0, j = packageJSON.cordovaPlugins.length; i < j; i += 1) { var packagePlugin = packageJSON.cordovaPlugins[i]; - if(typeof packagePlugin == 'object' && packagePlugin.id == plugin) { + if (typeof packagePlugin == 'object' && packagePlugin.id === plugin) { packageJSON.cordovaPlugins.splice(index, 1); break; } diff --git a/lib/hooks/after_prepare/010_add_platform_class.js b/lib/hooks/after_prepare/010_add_platform_class.js index 06049c2898..cdb7d03881 100755 --- a/lib/hooks/after_prepare/010_add_platform_class.js +++ b/lib/hooks/after_prepare/010_add_platform_class.js @@ -14,6 +14,7 @@ var path = require('path'); var rootdir = process.argv[2]; function addPlatformBodyTag(indexPath, platform) { + // add the platform class to the body tag try { var platformClass = 'platform-' + platform; @@ -24,21 +25,23 @@ function addPlatformBodyTag(indexPath, platform) { }); var bodyTag = findBodyTag(html); - if(!bodyTag) return; // no opening body tag, something's wrong + if (!bodyTag) return; // no opening body tag, something's wrong - if(bodyTag.indexOf(platformClass) > -1) return; // already added + if (bodyTag.indexOf(platformClass) > -1) return; // already added var newBodyTag = bodyTag; var classAttr = findClassAttr(bodyTag); - if(classAttr) { + if (classAttr) { + // body tag has existing class attribute, add the classname - var endingQuote = classAttr.substring(classAttr.length-1); - var newClassAttr = classAttr.substring(0, classAttr.length-1); + var endingQuote = classAttr.substring(classAttr.length - 1); + var newClassAttr = classAttr.substring(0, classAttr.length - 1); newClassAttr += ' ' + platformClass + ' ' + cordovaClass + endingQuote; newBodyTag = bodyTag.replace(classAttr, newClassAttr); } else { + // add class attribute to the body tag newBodyTag = bodyTag.replace('>', ' class="' + platformClass + ' ' + cordovaClass + '">'); } @@ -50,23 +53,29 @@ function addPlatformBodyTag(indexPath, platform) { }); process.stdout.write('add to body class: ' + platformClass + '\n'); - } catch(e) { + } catch (e) { process.stdout.write(e); } } function findBodyTag(html) { + // get the body tag - try{ + try { return html.match(/])(.*?)>/gi)[0]; - }catch(e){} + } catch (e) { + console.log('failure in findBodyTag'); + } } function findClassAttr(bodyTag) { + // get the body tag's class attribute - try{ + try { return bodyTag.match(/ class=["|'](.*?)["|']/gi)[0]; - }catch(e){} + } catch (e) { + console.log('failure in findClassAttr'); + } } if (rootdir) { @@ -74,25 +83,25 @@ if (rootdir) { // go through each of the platform directories that have been prepared var platforms = (process.env.CORDOVA_PLATFORMS ? process.env.CORDOVA_PLATFORMS.split(',') : []); - for(var x=0; x 3 ? process.argv.slice(3) : []); - var cmdArg, x, y; + var cmdArg; + var x; + var y; // backwards compatibility prior to fully wrapping cordova cmds - if(cmdName == 'platform' && cmdArgs.length > 0 ) { + if (cmdName === 'platform' && cmdArgs.length > 0) { + // `ionic platform ` used to actually run `ionic platform add ` // if a cordova platform cmd isn't the cmd then automatically insert `add` var hasCordovaCmd = false; var validCommands = 'add remove rm list ls update up check'.split(' '); - for(x=0; x= COLUMN_LIMIT) { + if (count >= COLUMN_LIMIT) { table.push(row); row = []; count = 0; } row.push(addDoc); - count++; + count += 1; }); table.push(row); log.info(table.toString()); @@ -66,8 +63,9 @@ Docs.openDefault = function openDefault() { }; Docs.lookUpCommand = function lookUpCommand(helpDoc) { + // log.info('Going thru doc commands', helpDoc) - //Go through all the different help topics + // Go through all the different help topics var docsList = IonicDocs.api; helpDoc = helpDoc.replace(/-[a-zA-Z]/g, function(match) { return match[1].toUpperCase(); @@ -83,7 +81,7 @@ Docs.lookUpCommand = function lookUpCommand(helpDoc) { }); docs = _.flatten(docs); - var match = _.find(docs, { doc: helpDoc }) || _.find(docs, { doc: '$' + helpDoc}); + var match = _.find(docs, { doc: helpDoc }) || _.find(docs, { doc: '$' + helpDoc }); if (match) { return openDoc(match.topic, match.doc); @@ -96,10 +94,10 @@ Docs.lookUpCommand = function lookUpCommand(helpDoc) { return lowerDoc.indexOf(lowerHelp) > -1 || lowerHelp.indexOf(lowerDoc) > -1; }) - .sort(function(a,b) { + .sort(function(a, b) { return a.doc.length < b.doc.length ? -1 : 1; }) - .slice(0,3); + .slice(0, 3); if (matches.length === 0) return log.info('No matching docs found for "' + helpDoc.cyan + '".'); @@ -124,10 +122,13 @@ Docs.lookUpCommand = function lookUpCommand(helpDoc) { prompt.start(); prompt.get({ name: 'choice', - default: matches.length === 1 ? 'yes' : '1', + default: matches.length === 1 ? 'yes' : '1' }, function(err, result) { + if (err) { + throw err; + } var num; - if (matches.length == 1) { + if (matches.length === 1) { if (result.choice.match(/^y/i, result.choice.trim())) { num = 1; } @@ -135,7 +136,7 @@ Docs.lookUpCommand = function lookUpCommand(helpDoc) { num = parseInt(result.choice.trim()); } if (!isNaN(num) && num > 0 && num <= matches.length) { - num--; // chioce (1-based) to index (0-based) + num -= 1; // chioce (1-based) to index (0-based) return openDoc(matches[num].topic, matches[num].doc); } }); @@ -150,20 +151,22 @@ Docs.lookUpCommand = function lookUpCommand(helpDoc) { }; IonicTask.prototype.run = function run(ionic) { - var self = this; this.ionic = ionic; var docCmd = argv._[1]; + // log.info('docCmd', docCmd); - if(docCmd == 'ls') { + if (docCmd === 'ls') { Docs.list(); } else if (!docCmd) { + // log.info('openDefault') Docs.openDefault(); } else { + // log.info('look up command', docCmd) - //Look up what it was + // Look up what it was Docs.lookUpCommand(docCmd); } diff --git a/lib/ionic/generate.js b/lib/ionic/generate.js index 294cd1019e..d54a7b8e45 100644 --- a/lib/ionic/generate.js +++ b/lib/ionic/generate.js @@ -1,37 +1,35 @@ -var fs = require('fs'), - path = require('path'), - Task = require('./task').Task, - ionicAppLib = require('ionic-app-lib'), - Q = require('q'), - Utils = ionicAppLib.utils, - log = ionicAppLib.logging.logger, - Info = ionicAppLib.info; - Project = ionicAppLib.project; - -var IonicTask = function() {}; +var path = require('path'); +var Task = require('./task').Task; +var ionicAppLib = require('ionic-app-lib'); +var Utils = ionicAppLib.utils; +var log = ionicAppLib.logging.logger; +var Project = ionicAppLib.project; + +function IonicTask() {} IonicTask.prototype = new Task(); IonicTask.prototype.run = function(ionic, argv) { try { if (!argv.list && argv._.length < 3) { - //TODO should have a mechanism for printing usage on invalid tasks + + // TODO should have a mechanism for printing usage on invalid tasks Utils.fail('Invalid arguments. Usage: ionic generate '); } if (!argv.v2) { return Utils.fail('Generators are only available for Ionic 2 projects'); } - + var project; try { project = Project.load(process.cwd()); - } catch (err){ + } catch (err) { return Utils.fail(err); } var generator = argv._[1]; - var name = argv._[2] //TODO support multiple names + var name = argv._[2]; // TODO support multiple names var isTS = project.get('typescript') || argv.ts; var ionicModule = loadToolingModule(); @@ -43,12 +41,12 @@ IonicTask.prototype.run = function(ionic, argv) { var promise; try { - promise = ionicModule.generate({appDirectory: process.cwd(), generator: generator, name: name, isTS: isTS}); + promise = ionicModule.generate({ appDirectory: process.cwd(), generator: generator, name: name, isTS: isTS }); } catch (err) { - if (err.message.indexOf('no generator available') != -1) { + if (err.message.indexOf('no generator available') !== -1) { log.error(err.message); ionicModule.Generate.printAvailableGenerators(); - return + return; } return Utils.fail(err); } @@ -57,22 +55,26 @@ IonicTask.prototype.run = function(ionic, argv) { } catch (err) { Utils.fail('There was an error generating your item:', err); } -} +}; + +function loadToolingModule() { + + // First try node_modules/ionic-angular/tooling + var toolingPath; + var ionicModule; -function loadToolingModule(){ - //First try node_modules/ionic-angular/tooling - var toolingPath, ionicModule; try { toolingPath = path.join(process.cwd(), 'node_modules', 'ionic-angular', 'tooling'); ionicModule = require(toolingPath); } catch (err) { + // if this isn't found, that's fine, check for ionic-framework if (err.code !== 'MODULE_NOT_FOUND') { Utils.fail('Error when requiring ' + toolingPath + ':\n ' + err); } } - //Then try node_modules/ionic-framework/tooling + // Then try node_modules/ionic-framework/tooling if (!ionicModule) { try { ionicModule = require(path.join(process.cwd(), 'node_modules', 'ionic-framework', 'tooling')); @@ -83,7 +85,7 @@ function loadToolingModule(){ } } - //Last, try node_modules/ionic-framework (beta.1 and below) + // Last, try node_modules/ionic-framework (beta.1 and below) if (!ionicModule) { try { ionicModule = require(path.join(process.cwd(), 'node_modules', 'ionic-framework')); diff --git a/lib/ionic/help.js b/lib/ionic/help.js index 4f725633b2..ddc18b699d 100644 --- a/lib/ionic/help.js +++ b/lib/ionic/help.js @@ -1,12 +1,12 @@ -var IonicTask = require('./task').IonicTask, - argv = require('optimist').argv, - Task = require('./task').Task; +var argv = require('optimist').argv; +var Task = require('./task').Task; -var IonicTask = function() {}; +function IonicTask() {} IonicTask.prototype = new Task(); IonicTask.prototype.run = function(ionic) { + // var self = this; // self.ionic = ionic; @@ -15,22 +15,20 @@ IonicTask.prototype.run = function(ionic) { var task = ionic.getTaskWithName(command); - if(command == '') { + if (command === '') { ionic.printHelpLines(); return; - } - else if (!task) { - console.log('Command not found.'.red.bold) - return + } else if (!task) { + console.log('Command not found.'.red.bold); + return; } ionic.printIonic(); process.stderr.write('\n=======================\n'); - ionic.printUsage(task); process.stderr.write('\n\n'); -} +}; exports.IonicTask = IonicTask; diff --git a/lib/ionic/hooks.js b/lib/ionic/hooks.js index 9b60e03cba..01bea3c577 100644 --- a/lib/ionic/hooks.js +++ b/lib/ionic/hooks.js @@ -1,17 +1,13 @@ -var fs = require('fs'), - path = require('path'), - argv = require('optimist').argv, - Q = require('q'), - shelljs = require('shelljs'), - Task = require('./task').Task, - IonicInfo = require('./info').IonicTask, - IonicAppLib = require('ionic-app-lib'), - log = IonicAppLib.logging.logger, - Hooks = IonicAppLib.hooks; +var argv = require('optimist').argv; +var shelljs = require('shelljs'); +var Task = require('./task').Task; +var IonicAppLib = require('ionic-app-lib'); +var log = IonicAppLib.logging.logger; +var Hooks = IonicAppLib.hooks; shelljs.config.silent = true; -var IonicTask = function() {}; +function IonicTask() {} IonicTask.prototype = new Task(); @@ -21,19 +17,19 @@ IonicTask.prototype.run = function run(ionic) { var cmd = argv._[1]; try { - switch(cmd) { - case 'add': - Hooks.add(process.cwd()); - break; - case 'remove': - Hooks.remove(process.cwd()); - break; - case 'perm': - case 'permissions': - Hooks.setHooksPermission(process.cwd()); - break; - default: - log.error('Please supply a command - either add or remove'); + switch (cmd) { + case 'add': + Hooks.add(process.cwd()); + break; + case 'remove': + Hooks.remove(process.cwd()); + break; + case 'perm': + case 'permissions': + Hooks.setHooksPermission(process.cwd()); + break; + default: + log.error('Please supply a command - either add or remove'); } } catch (ex) { log.error('There was an error running the hooks command: ', ex.stack); diff --git a/lib/ionic/info.js b/lib/ionic/info.js index bc91bef8e0..6ebcde1a98 100644 --- a/lib/ionic/info.js +++ b/lib/ionic/info.js @@ -1,11 +1,9 @@ -var fs = require('fs'), - path = require('path'), - argv = require('optimist').argv, - Task = require('./task').Task, - ionicAppLib = require('ionic-app-lib'), - Info = ionicAppLib.info; +var path = require('path'); +var Task = require('./task').Task; +var ionicAppLib = require('ionic-app-lib'); +var Info = ionicAppLib.info; -var IonicTask = function() {}; +function IonicTask() {} IonicTask.prototype = new Task(); @@ -15,7 +13,7 @@ IonicTask.prototype.run = function(ionic) { try { var info = Info.gatherInfo(); - + Info.getIonicVersion(info, process.cwd()); Info.getIonicCliVersion(info, path.join(__dirname, '../../')); @@ -25,6 +23,6 @@ IonicTask.prototype.run = function(ionic) { } catch (ex) { this.ionic.fail('There was an error retrieving your environment information:', ex); } -} +}; exports.IonicTask = IonicTask; diff --git a/lib/ionic/io-config.js b/lib/ionic/io-config.js index a8a5492e42..4de448bfc1 100644 --- a/lib/ionic/io-config.js +++ b/lib/ionic/io-config.js @@ -1,14 +1,10 @@ -var IonicAppLib = require('ionic-app-lib'), - ioLib = IonicAppLib.ioConfig, - argv = require('optimist').argv, - fs = require('fs'), - prompt = require('prompt'), - IonicProject = IonicAppLib.project, - IonicTask = require('./task').IonicTask, - Task = require('./task').Task, - IonicLoginTask = require('./login').IonicTask; - -var IonicTask = function() {}; +var IonicAppLib = require('ionic-app-lib'); +var ioLib = IonicAppLib.ioConfig; +var argv = require('optimist').argv; +var IonicProject = IonicAppLib.project; +var Task = require('./task').Task; + +function IonicTask() {} IonicTask.prototype = new Task(); @@ -22,23 +18,23 @@ IonicTask.prototype.run = function(ionic) { self.project = IonicProject.load(); } catch (ex) { self.ionic.fail(ex.message); - return + return; } - var set = false, - write = false, - key = '', - val = ''; + var set = false; + var write = false; + var key = ''; + var val = ''; - if (argv['_'][1] == 'set' || argv['_'][1] == 'unset' || argv['_'][1] == 'build' || argv['_'][1] == 'info') { - if (argv['_'][1] == 'build') { + if (argv['_'][1] === 'set' || argv['_'][1] === 'unset' || argv['_'][1] === 'build' || argv['_'][1] === 'info') { + if (argv['_'][1] === 'build') { write = true; ioLib.writeIoConfig(false, false, true); - } else if (argv['_'][1] == 'info') { + } else if (argv['_'][1] === 'info') { write = true; ioLib.listConfig(); } else { - if (argv['_'][1] == 'set') { + if (argv['_'][1] === 'set') { set = true; } if (argv['_'][2]) { diff --git a/lib/ionic/io-init.js b/lib/ionic/io-init.js index 866465652b..7fe31ee03b 100644 --- a/lib/ionic/io-init.js +++ b/lib/ionic/io-init.js @@ -1,16 +1,14 @@ -var IonicAppLib = require('ionic-app-lib'), - ioLib = IonicAppLib.ioConfig, - argv = require('optimist').argv, - prompt = require('prompt'), - IonicProject = IonicAppLib.project, - log = IonicAppLib.logging.logger, - IonicTask = require('./task').IonicTask, - Task = require('./task').Task, - Login = IonicAppLib.login, - LoginTask = require('./login'), - Utils = require('./utils'); +var IonicAppLib = require('ionic-app-lib'); +var ioLib = IonicAppLib.ioConfig; +var argv = require('optimist').argv; +var IonicProject = IonicAppLib.project; +var log = IonicAppLib.logging.logger; +var Task = require('./task').Task; +var Login = IonicAppLib.login; +var LoginTask = require('./login'); +var Utils = require('./utils'); -var IonicTask = function() {}; +function IonicTask() {} IonicTask.prototype = new Task(); @@ -24,11 +22,11 @@ IonicTask.prototype.run = function(ionic) { self.project = IonicProject.load(); } catch (ex) { self.ionic.fail(ex.message); - return + return; } Login.retrieveLogin() - .then(function(jar){ + .then(function(jar) { if (!jar) { log.info('No previous login existed. Attempting to log in now.'); return LoginTask.login(argv); @@ -36,10 +34,10 @@ IonicTask.prototype.run = function(ionic) { return jar; }) .then(function(jar) { - if (argv['_'][1] == 'init') { + if (argv['_'][1] === 'init') { ioLib.initIoPlatform(process.cwd(), jar); } else { - self.ionic.fail("Invalid command"); + self.ionic.fail('Invalid command'); } }) .catch(function(ex) { diff --git a/lib/ionic/ionitron.js b/lib/ionic/ionitron.js index 4ed7726c8c..3befddc537 100644 --- a/lib/ionic/ionitron.js +++ b/lib/ionic/ionitron.js @@ -1,59 +1,72 @@ -var colors = require('colors'), - path = require('path'), - fs = require('fs'); +require('colors'); + +var path = require('path'); +var fs = require('fs'); var ionitronStatements = [ - 'Hello human, what shall we build today?', - '*BEEP BEEP* ALL YOUR BASE ARE BELONG TO US *BEEP BEEP*', - 'Prepare to dominate your hybrid app. Engaging now.', - 'My sensors indicate you have an undying love for ionic, |\n | or is it just me?\t\t\t\t\t\t', - 'That\'s a nice looking app you have there. \t\t\t |\n | Definitely one of the better human made apps I\'ve seen.\t', - 'Oh, hi there. I was just not indexing your hard drive, |\n | definitely not doing that. ', - 'That would need bee\'s approval', - 'Fork you! Oh, I\'m sorry, wrong branch.' + 'Hello human, what shall we build today?', + '*BEEP BEEP* ALL YOUR BASE ARE BELONG TO US *BEEP BEEP*', + 'Prepare to dominate your hybrid app. Engaging now.', + 'My sensors indicate you have an undying love for ionic, |\n ' + + '| or is it just me?\t\t\t\t\t\t', + 'That\'s a nice looking app you have there. \t\t\t |\n | Definitely ' + + 'one of the better human made apps I\'ve seen.\t', + 'Oh, hi there. I was just not indexing your hard drive, |\n ' + + '| definitely not doing that. ', + 'That would need bee\'s approval', + 'Fork you! Oh, I\'m sorry, wrong branch.' ]; -var ionitronStatements_es = [ - '\u0021Hola humano! \u00BFQu\u00E9 vamos a construir hoy?', - '*BEEP BEEP* TU BASE NOS PERTENECE *BEEP BEEP*', - 'Prep\u00E1rate para dominar las aplicaciones h\u00EDbridas. |\n | Participa ahora.\t\t\t\t\t\t', - 'Mis sensores indican que sientes amor eterno hacia Ionic, |\n | \u00BFo es solo hacia m\u00ED?\t\t\t\t\t\t', - 'Es una bonita aplicaci\u00F3n esa que tienes. \t\t\t |\n | Eres el mejor desarrollador humano que he visto.\t\t', - 'Oh, hola. No estaba indexando tu disco duro, no hago eso.', - 'Es necesitaria la aprobaci\u00F3n de las abejas.', - 'Bif\u00Farcate! Oh, Lo siento, rama equivocada. ' +var ionitronStatementsEs = [ + '\u0021Hola humano! \u00BFQu\u00E9 vamos a construir hoy?', + '*BEEP BEEP* TU BASE NOS PERTENECE *BEEP BEEP*', + 'Prep\u00E1rate para dominar las aplicaciones h\u00EDbridas. ' + + '|\n | Participa ahora.\t\t\t\t\t\t', + 'Mis sensores indican que sientes amor eterno hacia Ionic, |\n ' + + '| \u00BFo es solo hacia m\u00ED?\t\t\t\t\t\t', + 'Es una bonita aplicaci\u00F3n esa que tienes. \t\t\t |\n | Eres el ' + + 'mejor desarrollador humano que he visto.\t\t', + 'Oh, hola. No estaba indexando tu disco duro, no hago eso.', + 'Es necesitaria la aprobaci\u00F3n de las abejas.', + 'Bif\u00Farcate! Oh, Lo siento, rama equivocada. ' ]; var ionictronAsciiFile = 'ionitron.txt'; module.exports.print = function print(lang) { - var ionitronPath = path.join(__dirname, 'assets', ionictronAsciiFile); - var contents = fs.readFileSync(ionitronPath, 'utf8'); - var messageContent; - - switch (lang) { - case 'es': - //Replace the 'h' (antenna) by Ñ hahaha' - contents = contents.replace('h', '\u00D1'); - messageContent = [ionitronStatements_es[Math.floor(Math.random() * (ionitronStatements_es.length - 0) + 0)]].join(''); - break; - default: - messageContent = [ionitronStatements[Math.floor(Math.random() * (ionitronStatements.length - 0) + 0)]].join(''); - } + var ionitronPath = path.join(__dirname, 'assets', ionictronAsciiFile); + var contents = fs.readFileSync(ionitronPath, 'utf8'); + var messageContent; + + switch (lang) { + case 'es': + + // Replace the 'h' (antenna) by Ñ hahaha' + contents = contents.replace('h', '\u00D1'); + messageContent = [ionitronStatementsEs[Math.floor(Math.random() * + (ionitronStatementsEs.length - 0) + 0)]].join(''); + break; + default: + messageContent = [ionitronStatements[Math.floor(Math.random() * + (ionitronStatements.length - 0) + 0)]].join(''); + } - var replaceString = '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'; - // console.log(messageContent.length, replaceString.length) - if (messageContent.length < replaceString.length) { - var diff = (replaceString.length) - messageContent.length; - var i = 0; - // console.log(diff, i) - while (i < diff) { - messageContent = messageContent + ' '; - i++; - } + var replaceString = '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'; + + // console.log(messageContent.length, replaceString.length) + if (messageContent.length < replaceString.length) { + var diff = (replaceString.length) - messageContent.length; + var i = 0; + + // console.log(diff, i) + while (i < diff) { + messageContent = messageContent + ' '; + i += 1; } - contents = contents.replace(replaceString, messageContent) - console.log(contents.cyan); - return; -} + } + contents = contents.replace(replaceString, messageContent); + console.log(contents.cyan); + + return; +}; diff --git a/lib/ionic/ions.js b/lib/ionic/ions.js index 6464ed3ff0..d04c4eb4d1 100644 --- a/lib/ionic/ions.js +++ b/lib/ionic/ions.js @@ -1,8 +1,6 @@ -var argv = require('optimist').argv, - IonicProject = require('./project'), - log = require('ionic-app-lib').logging.logger, - Task = require('./task').Task, - _ = require('underscore'); +var log = require('ionic-app-lib').logging.logger; +var Task = require('./task').Task; +var _ = require('underscore'); var ions = [ { @@ -30,36 +28,33 @@ var ions = [ name: 'Tinder Cards', description: 'Tinder style card swiping interaction' } -] +]; -var IonicTask = function() {}; +function IonicTask() {} IonicTask.prototype = new Task(); -IonicTask.prototype.run = function(ionic) { +IonicTask.prototype.run = function() { log.info('Ionic Ions - ions power your app to be awesome.'); - log.info('A curated collection of useful addons, components, and ux interactions for extending ionic.') - log.info('\n') + log.info('A curated collection of useful addons, components, and ux interactions for extending ionic.'); + log.info('\n'); _.each(ions, function(ion) { var rightColumn = 20; var dots = ''; - var indent = ''; - var x, arg; - var startStr = ion.name; - while( (startStr + dots).length < rightColumn + 1) { + while ((startStr + dots).length < rightColumn + 1) { dots += '.'; } // Header Shrink ... A shrinking header effect like Facebook\'s .. - var installStr = ['\'ionic add ', ion.ion, '\''].join('') - log.info(ion.name, dots, installStr) - log.info(ion.description, '\n') - }) -} + var installStr = ['\'ionic add ', ion.ion, '\''].join(''); + log.info(ion.name, dots, installStr); + log.info(ion.description, '\n'); + }); +}; exports.IonicTask = IonicTask; diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index 15223b933a..1b3b018d8f 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -1,18 +1,18 @@ -var fs = require('fs'), - os = require('os'), - shelljs = require('shelljs/global'), - request = require('request'), - path = require('path'), - unzip = require('unzip'), - argv = require('optimist').argv, - prompt = require('prompt'), - colors = require('colors'), - exec = require('child_process').exec, - Q = require('q'), - log = require('ionic-app-lib').logging.logger, - Task = require('./task').Task; - -var IonicTask = function() {}; +require('colors'); + +var fs = require('fs'); +var path = require('path'); +var shell = require('shelljs'); +var request = require('request'); +var unzip = require('unzip'); +var argv = require('optimist').argv; +var prompt = require('prompt'); +var exec = require('child_process').exec; +var Q = require('q'); +var log = require('ionic-app-lib').logging.logger; +var Task = require('./task').Task; + +function IonicTask() {} IonicTask.prototype = new Task(); @@ -22,15 +22,17 @@ IonicTask.prototype.run = function(ionic) { self.versionData = {}; self.usesBower = false; - if (!fs.existsSync( path.resolve('www') )) { - return ionic.fail('"www" directory cannot be found. Make sure the working directory is at the top level of an Ionic project.', 'lib'); + if (!fs.existsSync(path.resolve('www'))) { + return ionic.fail('"www" directory cannot be found. Make sure the working directory ' + + 'is at the top level of an Ionic project.', 'lib'); } this.loadVersionData(); - if(argv._.length > 1 && (argv._[1].toLowerCase() == 'update' || argv._[1].toLowerCase() == 'up')) { + if (argv._.length > 1 && (argv._[1].toLowerCase() === 'update' || argv._[1].toLowerCase() === 'up')) { this.updateLibVersion(); } else { + // just version info this.printLibVersions(); } @@ -40,14 +42,15 @@ IonicTask.prototype.run = function(ionic) { IonicTask.prototype.loadVersionData = function() { this.versionFilePath = path.resolve('www/lib/ionic/version.json'); - if( !fs.existsSync(this.versionFilePath) ) { + + if (!fs.existsSync(this.versionFilePath)) { this.versionFilePath = path.resolve('www/lib/ionic/bower.json'); this.usesBower = fs.existsSync(this.versionFilePath); } try { this.local = require(this.versionFilePath); - } catch(e) { + } catch (e) { log.error('Unable to load ionic lib version information'); } }; @@ -58,13 +61,14 @@ IonicTask.prototype.printLibVersions = function() { log.info('Local Ionic version: '.bold.green + this.local.version + ' (' + this.versionFilePath + ')'); - this.getVersionData('latest').then(function(){ - log.info('Latest Ionic version: '.bold.green + self.versionData.version_number + ' (released ' + self.versionData.release_date + ')'); + this.getVersionData('latest').then(function() { + log.info('Latest Ionic version: '.bold.green + self.versionData.version_number + + ' (released ' + self.versionData.release_date + ')'); - if(self.local.version != self.versionData.version_number) { - log.info(' * Local version is out of date'.yellow ); + if (self.local.version !== self.versionData.version_number) { + log.info(' * Local version is out of date'.yellow); } else { - log.info(' * Local version up to date'.green ); + log.info(' * Local version up to date'.green); } }); }; @@ -75,7 +79,7 @@ IonicTask.prototype.getVersionData = function(version) { var self = this; var url = 'http://code.ionicframework.com'; - if(version == 'latest') { + if (version === 'latest') { url += '/latest.json'; } else { url += '/' + version + '/version.json'; @@ -84,28 +88,28 @@ IonicTask.prototype.getVersionData = function(version) { var proxy = process.env.PROXY || process.env.http_proxy || null; request({ url: url, proxy: proxy }, function(err, res, body) { try { - if(err || !res || !body) { + if (err || !res || !body) { log.error('Unable to receive version data'); q.reject(); return; } - if(res.statusCode == 404) { + if (res.statusCode === 404) { log.error('Invalid version: ' + version); q.reject(); return; } - if(res.statusCode >= 400) { + if (res.statusCode >= 400) { log.error('Unable to load version data'); q.reject(); return; } self.versionData = JSON.parse(body); - self.versionData.version_number = self.versionData.version_number || self.versionData.version; - self.versionData.version_codename = self.versionData.version_codename || self.versionData.codename; - self.versionData.release_date = self.versionData.release_date || self.versionData.date; + self.versionData['version_number'] = self.versionData.version_number || self.versionData.version; + self.versionData['version_codename'] = self.versionData.version_codename || self.versionData.codename; + self.versionData['release_date'] = self.versionData.release_date || self.versionData.date; q.resolve(); - } catch(e) { + } catch (e) { log.error('Error loading ' + version.bold + ' version information'); q.reject(); } @@ -118,15 +122,15 @@ IonicTask.prototype.getVersionData = function(version) { IonicTask.prototype.updateLibVersion = function() { var self = this; - if(self.usesBower) { + if (self.usesBower) { var bowerCmd = exec('bower update ionic'); - bowerCmd.stdout.on('data', function (data) { + bowerCmd.stdout.on('data', function(data) { process.stdout.write(data); }); - bowerCmd.stderr.on('data', function (data) { - if(data) { + bowerCmd.stderr.on('data', function(data) { + if (data) { process.stderr.write(data.toString().red.bold); } }); @@ -140,7 +144,8 @@ IonicTask.prototype.updateLibVersion = function() { var libPath = path.resolve('www/lib/ionic/'); - log.info('Are you sure you want to replace '.green.bold + libPath.bold + ' with an updated version of Ionic?'.green.bold); + log.info('Are you sure you want to replace '.green.bold + libPath.bold + + ' with an updated version of Ionic?'.green.bold); var promptProperties = { areYouSure: { @@ -150,13 +155,13 @@ IonicTask.prototype.updateLibVersion = function() { } }; - prompt.get({properties: promptProperties}, function (err, promptResult) { - if(err) { + prompt.get({ properties: promptProperties }, function(err, promptResult) { + if (err) { return log.error(err); } var areYouSure = promptResult.areYouSure.toLowerCase().trim(); - if(areYouSure == 'yes' || areYouSure == 'y') { + if (areYouSure === 'yes' || areYouSure === 'y') { self.getLatest(); } }); @@ -167,25 +172,27 @@ IonicTask.prototype.getLatest = function() { var self = this; var dirPath = path.resolve('www/lib/'); - if(!fs.existsSync(dirPath)) { + if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath); } dirPath = path.resolve('www/lib/ionic/'); - if(!fs.existsSync(dirPath)) { + if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath); } var version = argv.version || argv.v; - if(version === true || !version) { + if (version === true || !version) { version = 'latest'; } - this.getVersionData(version).then(function(){ - if(version == 'latest') { - log.info('Latest version: '.bold.green + self.versionData.version_number + ' (released ' + self.versionData.release_date + ')'); + this.getVersionData(version).then(function() { + if (version === 'latest') { + log.info('Latest version: '.bold.green + self.versionData.version_number + + ' (released ' + self.versionData.release_date + ')'); } else { - log.info('Version: '.bold.green + self.versionData.version_number + ' (released ' + self.versionData.release_date + ')'); + log.info('Version: '.bold.green + self.versionData.version_number + + ' (released ' + self.versionData.release_date + ')'); } self.downloadZip(self.versionData.version_number); }); @@ -201,7 +208,7 @@ IonicTask.prototype.downloadZip = function(version) { self.tmpExtractPath = path.resolve('www/lib/.ionic'); self.tmpZipPath = path.join(self.tmpExtractPath, 'ionic.zip'); - if( !fs.existsSync(self.tmpExtractPath) ) { + if (!fs.existsSync(self.tmpExtractPath)) { fs.mkdirSync(self.tmpExtractPath); } @@ -209,25 +216,25 @@ IonicTask.prototype.downloadZip = function(version) { var proxy = process.env.PROXY || null; request({ url: archivePath, rejectUnauthorized: false, encoding: null, proxy: proxy }, function(err, res, body) { - if(err) { + if (err) { log.error(err); return; } - if(res.statusCode == 404 || res.statusCode == 406) { + if (res.statusCode === 404 || res.statusCode === 406) { log.error('Invalid version: ' + version); return; } - if(res.statusCode >= 400) { + if (res.statusCode >= 400) { log.error('Unable to download zip (' + res.statusCode + ')'); return; } try { fs.writeFileSync(self.tmpZipPath, body); self.updateFiles(); - } catch(e) { + } catch (e) { log.error(e); } - }).on('response', function(res){ + }).on('response', function(res) { var ProgressBar = require('progress'); var bar = new ProgressBar('[:bar] :percent :etas', { @@ -237,16 +244,18 @@ IonicTask.prototype.downloadZip = function(version) { total: parseInt(res.headers['content-length'], 10) }); - res.on('data', function (chunk) { + res.on('data', function(chunk) { try { bar.tick(chunk.length); - } catch(e){} + } catch (e) { + log.error(e); + } }); }).on('error', function(err) { try { fs.unlink(self.tmpZipPath); - } catch(e) { + } catch (e) { log.error(e); } log.error(err); @@ -255,7 +264,7 @@ IonicTask.prototype.downloadZip = function(version) { }; -IonicTask.prototype.updateFiles = function(version) { +IonicTask.prototype.updateFiles = function() { var self = this; var ionicLibDir = path.resolve('www/lib/ionic'); @@ -265,47 +274,55 @@ IonicTask.prototype.updateFiles = function(version) { log.debug('updateFiles readStream error: ' + err); }); - var writeStream = unzip.Extract({ path: self.tmpExtractPath }); + var writeStream = unzip.Extract({ path: self.tmpExtractPath }); // eslint-disable-line new-cap writeStream.on('close', function() { try { - rm('-rf', self.tmpZipPath) - } catch(e){} + shell.rm('-rf', self.tmpZipPath); + } catch (e) { + log.error(e); + } try { var dir = fs.readdirSync(self.tmpExtractPath); var copyFrom; - for(var x=0; x new Date()) { - ionic.jar = jar; - callback(jar); - return; - } - } - } - } - - this.run(ionic, argv, callback); }; LoginTask.IonicTask = IonicTask; diff --git a/lib/ionic/package.js b/lib/ionic/package.js index ea4e38b1dd..956b89eba0 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -1,22 +1,21 @@ -var fs = require('fs'), - path = require('path'), - _ = require('underscore'), - Q = require('q'), - moment = require('moment'), - expandTilde = require('expand-tilde'), - ProgressBar = require('progress'), - IonicAppLib = require('ionic-app-lib'), - Utils = IonicAppLib.utils, - Login = IonicAppLib.login, - log = IonicAppLib.logging.logger, - Package = IonicAppLib.package, - LoginTask = require('./login'), - Table = require('./table'), - Task = require('./task').Task; +var path = require('path'); +var _ = require('underscore'); +var Q = require('q'); +var moment = require('moment'); +var expandTilde = require('expand-tilde'); +var ProgressBar = require('progress'); +var IonicAppLib = require('ionic-app-lib'); +var Utils = IonicAppLib.utils; +var Login = IonicAppLib.login; +var log = IonicAppLib.logging.logger; +var Package = IonicAppLib.package; +var LoginTask = require('./login'); +var Table = require('./table'); +var Task = require('./task').Task; var Project = IonicAppLib.project; -var IonicTask = function() {}; +function IonicTask() {} IonicTask.prototype = new Task(); @@ -30,9 +29,9 @@ IonicTask.prototype.run = function(ionic, argv) { cmd = argv._[1]; } - var dir = null, - project = null, - appId = null; + var dir = null; + var project = null; + var appId = null; try { dir = process.cwd(); @@ -47,29 +46,28 @@ IonicTask.prototype.run = function(ionic, argv) { } switch (cmd) { - case 'build': - return packageBuild(ionic, argv, dir, project, appId); - case 'list': - return packageList(ionic, argv, dir, project, appId); - case 'info': - return packageInfo(ionic, argv, dir, project, appId); - case 'download': - return packageDownload(ionic, argv, dir, project, appId); + case 'build': + return packageBuild(ionic, argv, dir, project, appId); + case 'list': + return packageList(ionic, argv, dir, project, appId); + case 'info': + return packageInfo(ionic, argv, dir, project, appId); + case 'download': + return packageDownload(ionic, argv, dir, project, appId); } - return Utils.fail("Unknown subcommand '" + cmd + "'.", 'package') - + return Utils.fail("Unknown subcommand '" + cmd + "'.", 'package'); }; function packageBuild(ionic, argv, dir, project, appId) { if (argv._.length < 3) { - return Utils.fail("Specify a valid platform (android or ios).", 'package'); + return Utils.fail('Specify a valid platform (android or ios).', 'package'); } - var jar, - platform = argv._[2], - buildMode = argv.release ? 'release' : 'debug'; + var jar; + var platform = argv._[2]; + var buildMode = argv.release ? 'release' : 'debug'; if (!_.contains(['android', 'ios'], platform)) { return Utils.fail("Invalid platform '" + platform + "', please choose either 'android' or 'ios'.", 'package'); @@ -100,14 +98,14 @@ function packageBuild(ionic, argv, dir, project, appId) { return Package.buildAndroidDebug(dir, jar, appId, options); } else if (buildMode === 'release') { if (typeof argv.profile === 'undefined') { - return Utils.fail("Must specify security profile for android release builds (--profile ).", 'package'); + return Utils.fail('Must specify security profile for android release builds (--profile ).', 'package'); } return Package.buildAndroidRelease(dir, jar, appId, argv.profile, options); } } else if (platform === 'ios') { if (typeof argv.profile === 'undefined') { - return Utils.fail("Must specify security profile for ios builds (--profile ).", 'package'); + return Utils.fail('Must specify security profile for ios builds (--profile ).', 'package'); } return Package.buildIOS(dir, jar, appId, argv.profile, buildMode, options); @@ -118,15 +116,14 @@ function packageBuild(ionic, argv, dir, project, appId) { .catch(function(ex) { Utils.fail(ex, 'package'); }); - -}; +} function formatStatus(status) { switch (status) { - case 'SUCCESS': - return status.green; - case 'FAILED': - return status.red; + case 'SUCCESS': + return status.green; + case 'FAILED': + return status.red; } return status; @@ -158,10 +155,10 @@ function packageList(ionic, argv, dir, project, appId) { log.info('You don\'t have any builds yet!'); log.info('Type ' + 'ionic help package'.yellow + ' to learn how to use Ionic Package.'); } else { - var count = 0, - headers = ['id', 'status', 'platform', 'mode'], - table, - screenWidth = process.stdout.getWindowSize()[0]; + var count = 0; + var headers = ['id', 'status', 'platform', 'mode']; + var table; + var screenWidth = process.stdout.getWindowSize()[0]; if (screenWidth > 100) { headers.push('started'); @@ -174,7 +171,7 @@ function packageList(ionic, argv, dir, project, appId) { table = new Table({ head: headers }); _.each(body.data.slice(0, limit), function(build) { - count++; + count += 1; var row = [ build.id, @@ -225,14 +222,14 @@ function packageInfo(ionic, argv, dir, project, appId) { return Package.listBuilds(appId, jar); } - return { data: [ { id: argv._[2] } ] }; + return { data: [{ id: argv._[2] }] }; }) .then(function(body) { return Package.getBuild(appId, jar, body.data[0].id, { fields: ['output'] }); }) .then(function(body) { - var table = new Table(), - build = body.data; + var table = new Table(); + var build = body.data; table.push( ['id'.yellow, build.id], @@ -261,14 +258,13 @@ function packageInfo(ionic, argv, dir, project, appId) { .catch(function(ex) { Utils.fail(ex, 'package'); }); - } function packageDownload(ionic, argv, dir, project, appId) { - var jar, - bar, - downloadDirectory; + var jar; + var bar; + var downloadDirectory; if (typeof argv.d !== 'undefined') { argv['destination'] = argv.d; @@ -295,7 +291,7 @@ function packageDownload(ionic, argv, dir, project, appId) { return Package.listBuilds(appId, jar); } - return { data: [ { id: argv._[2] } ] }; + return { data: [{ id: argv._[2] }] }; }) .then(function(body) { return Package.downloadBuild(appId, jar, body.data[0].id, downloadDirectory); diff --git a/lib/ionic/prompt.js b/lib/ionic/prompt.js index 101e140935..a97add59f8 100644 --- a/lib/ionic/prompt.js +++ b/lib/ionic/prompt.js @@ -1,7 +1,5 @@ -var prompt = require('prompt'), - IonicAppLib = require('ionic-app-lib'), - Utils = IonicAppLib.utils, - Q = require('q'); +var prompt = require('prompt'); +var Q = require('q'); var Prompt = module.exports; @@ -11,7 +9,7 @@ Prompt.prompt = function(schema, argv) { prompt.message = ''; prompt.delimiter = ''; prompt.start(); - prompt.get(schema, function (err, result) { + prompt.get(schema, function(err, result) { if (err) { if (err.message === 'canceled') { return q.reject(false); diff --git a/lib/ionic/push.js b/lib/ionic/push.js index d38e0b87e0..04073dba41 100644 --- a/lib/ionic/push.js +++ b/lib/ionic/push.js @@ -1,20 +1,18 @@ -var fs = require('fs'), - path = require('path'), - parseUrl = require('url').parse, - request = require('request'), - argv = require('optimist').argv, - prompt = require('prompt'), - FormData = require('form-data'), - IonicTask = require('./task').IonicTask, - Task = require('./task').Task, - IonicAppLib = require('ionic-app-lib'), - IonicProject = IonicAppLib.project, - Login = IonicAppLib.login, - log = IonicAppLib.logging.logger, - LoginTask = require('./login'); - // IonicLoginTask = require('./login').IonicTask; - -var IonicTask = function() {}; +var fs = require('fs'); +var path = require('path'); +var parseUrl = require('url').parse; +var request = require('request'); +var argv = require('optimist').argv; +var prompt = require('prompt'); +var FormData = require('form-data'); +var Task = require('./task').Task; +var IonicAppLib = require('ionic-app-lib'); +var IonicProject = IonicAppLib.project; +var Login = IonicAppLib.login; +var log = IonicAppLib.logging.logger; +var LoginTask = require('./login'); + +function IonicTask() {} IonicTask.prototype = new Task(); @@ -28,7 +26,7 @@ IonicTask.prototype.run = function(ionic, argv) { this.getCmdLineOptions(); Login.retrieveLogin() - .then(function(jar){ + .then(function(jar) { if (!jar) { log.info('No previous login existed. Attempting to log in now.'); return LoginTask.login(argv); @@ -38,7 +36,7 @@ IonicTask.prototype.run = function(ionic, argv) { .then(function(jar) { self.jar = jar; - if (argv._[1] && argv._[1].trim() == 'webhook_url') { + if (argv._[1] && argv._[1].trim() === 'webhook_url') { if (argv._[2]) { self.set_webhook_url(self, argv._[2].trim()); } else { @@ -47,12 +45,15 @@ IonicTask.prototype.run = function(ionic, argv) { } else if (argv['google-api-key']) { self.set_google_api_key(self, argv['google-api-key'].trim()); } else if (argv['send'] || argv.s) { + // Dev wants to send a push notification self.send_push(self); } else if (argv['ios-dev-cert'] || argv['ios-prod-cert'] || argv.d || argv.p) { + // Dev wants to upload an ios push cert self.upload_cert(self); } else if (argv['production-mode'] || argv.l) { + // Dev wants to switch between test/live mode // this will look like --live-mode=[y/n] // where y will put into live and n will put into dev @@ -61,105 +62,102 @@ IonicTask.prototype.run = function(ionic, argv) { }) .catch(function(ex) { log.error('An error occurred', ex); - }) - + }); }; -IonicTask.prototype.set_webhook_url = function(self, webhook_url) { +IonicTask.prototype.set_webhook_url = function(task, webhook_url) { // eslint-disable-line camelcase var project = IonicProject.load(); if (!project.get('app_id')) { - log.error("You need to upload your app first!".bold.red); + log.error('You need to upload your app first!'.bold.red); return false; } - var url = self.ionic.IONIC_DASH + self.ionic.IONIC_API + 'app/' + project.get('app_id') + '/push/webhook-url'; + var url = task.ionic.IONIC_DASH + task.ionic.IONIC_API + 'app/' + project.get('app_id') + '/push/webhook-url'; var params = parseUrl(url); var form = new FormData(); - form.append('csrfmiddlewaretoken', self.getCsrfToken(self.jar)); + form.append('csrfmiddlewaretoken', task.getCsrfToken(task.jar)); form.append('webhook_url', webhook_url); form.submit({ - protocol: params.protocol, - hostname: params.hostname, - port: params.port, - path: params.path, - headers: form.getHeaders({ - cookie: self.jar.map(function(c) { - return c.key + "=" + encodeURIComponent(c.value); - }).join("; ") - }) - }, - function (err, response) { - response.on("data", function(data) { - if (err) { - return self.ionic.fail('Error setting Webhook URL: ' + err); - } + protocol: params.protocol, + hostname: params.hostname, + port: params.port, + path: params.path, + headers: form.getHeaders({ + cookie: task.jar.map(function(c) { + return c.key + '=' + encodeURIComponent(c.value); + }).join('; ') + }) + }, + function(err, response) { + response.on('data', function() { + if (err) { + return task.ionic.fail('Error setting Webhook URL: ' + err); + } - if (response.statusCode == '200') { - log.info("Webhook URL set to", webhook_url); - } else { - return self.ionic.fail('App not found'); - } - }) - } - ); + if (response.statusCode === '200') { + log.info('Webhook URL set to', webhook_url); + } else { + return task.ionic.fail('App not found'); + } + }); + }); return true; }; -IonicTask.prototype.set_google_api_key = function(self, google_api_key) { +IonicTask.prototype.set_google_api_key = function(task, google_api_key) { // eslint-disable-line camelcase var project = IonicProject.load(); if (!project.get('app_id')) { - log.error("You need to upload your app first!".bold.red); + log.error('You need to upload your app first!'.bold.red); return false; } - var url = self.ionic.IONIC_DASH + self.ionic.IONIC_API + 'app/' + project.get('app_id') + '/push/google-api-key'; + var url = task.ionic.IONIC_DASH + task.ionic.IONIC_API + 'app/' + project.get('app_id') + '/push/google-api-key'; var params = parseUrl(url); var form = new FormData(); - form.append('csrfmiddlewaretoken', self.getCsrfToken(self.jar)); + form.append('csrfmiddlewaretoken', task.getCsrfToken(task.jar)); form.append('android_api_key', google_api_key); form.submit({ - protocol: params.protocol, - hostname: params.hostname, - port: params.port, - path: params.path, - headers: form.getHeaders({ - cookie: self.jar.map(function(c) { - return c.key + "=" + encodeURIComponent(c.value); - }).join("; ") - }) - }, - function (err, response) { - response.on("data", function(data) { - if (err) { - return self.ionic.fail('Error setting Google API Key: ' + err); - } + protocol: params.protocol, + hostname: params.hostname, + port: params.port, + path: params.path, + headers: form.getHeaders({ + cookie: task.jar.map(function(c) { + return c.key + '=' + encodeURIComponent(c.value); + }).join('; ') + }) + }, + function(err, response) { + response.on('data', function() { + if (err) { + return task.ionic.fail('Error setting Google API Key: ' + err); + } - if (response.statusCode == '200') { - log.info("Google API Key Saved".green); - } else { - return self.ionic.fail('App not found'); - } - }) - } - ); + if (response.statusCode === '200') { + log.info('Google API Key Saved'.green); + } else { + return task.ionic.fail('App not found'); + } + }); + }); return true; }; -IonicTask.prototype.send_push = function(self) { +IonicTask.prototype.send_push = function(task) { // eslint-disable-line camelcase var project = IonicProject.load(); if (!project.get('app_id')) { - log.error("You need to upload your app first!".bold.red); + log.error('You need to upload your app first!'.bold.red); return false; } @@ -178,7 +176,7 @@ IonicTask.prototype.send_push = function(self) { description: 'Device token'.yellow.bold, required: true, isFile: false - } + }; promptProperties['alert'] = { name: 'alert', @@ -192,23 +190,23 @@ IonicTask.prototype.send_push = function(self) { description: 'Badge count'.yellow.bold, required: false, isFile: false - } + }; promptProperties['sound'] = { name: 'sound', description: 'Sound file name'.yellow.bold, required: false, isFile: false - } + }; prompt.start(); - prompt.get({properties: promptProperties}, function(err, promptResult) { + prompt.get({ properties: promptProperties }, function(err, promptResult) { if (err) { - return self.ionic.fail('Error: ' + err); + return task.ionic.fail('Error: ' + err); } - var api_key = promptResult['push-api-key']; + var apiKey = promptResult['push-api-key']; var notification = { platform: 'ios', @@ -220,7 +218,7 @@ IonicTask.prototype.send_push = function(self) { sound: promptResult['sound'] } } - } + }; var options = { url: 'https://push.ionic.io/api/v1/push', @@ -228,99 +226,97 @@ IonicTask.prototype.send_push = function(self) { json: true, headers: { 'X-Ionic-Application-Id': project.get('app_id'), - 'authorization': 'Basic ' + new Buffer(api_key + ':').toString('base64') + authorization: 'Basic ' + new Buffer(apiKey + ':').toString('base64') }, body: notification }; - request(options, function(err, response, body) { - if (!err && response.statusCode == 202) { - log.info("Successfully queued push notification"); + request(options, function(err, response) { + if (!err && response.statusCode === 202) { + log.info('Successfully queued push notification'); } else { - log.error("Error queueing push notification", err); + log.error('Error queueing push notification', err); } }); }); - }; -IonicTask.prototype.set_production_mode = function(self) { +IonicTask.prototype.set_production_mode = function(task) { // eslint-disable-line camelcase var project = IonicProject.load(); if (!project.get('app_id')) { - log.error("You need to upload your app first!".bold.red); + log.error('You need to upload your app first!'.bold.red); return false; } - if (self.inputValues['production-mode'] === true) { - log.error("You need to specify a value [y/n] to set the production mode!".bold.red); + if (task.inputValues['production-mode'] === true) { + log.error('You need to specify a value [y/n] to set the production mode!'.bold.red); return false; } - self.inputValues['production-mode'] = self.inputValues['production-mode'].toLowerCase(); + task.inputValues['production-mode'] = task.inputValues['production-mode'].toLowerCase(); - if (self.inputValues['production-mode'] != 'y' && self.inputValues['production-mode'] != 'n') { - log.error("You need to specify a value [y/n] to set the production mode!".bold.red); + if (task.inputValues['production-mode'] !== 'y' && task.inputValues['production-mode'] !== 'n') { + log.error('You need to specify a value [y/n] to set the production mode!'.bold.red); return false; } - var url = self.ionic.IONIC_DASH + self.ionic.IONIC_API + 'app/' + project.get('app_id') + '/push/set-production-mode'; + var url = task.ionic.IONIC_DASH + task.ionic.IONIC_API + 'app/' + project.get('app_id') + '/push/set-production-mode'; var params = parseUrl(url); var form = new FormData(); - form.append('csrfmiddlewaretoken', self.getCsrfToken(self.jar)); - form.append('production_mode', self.inputValues['production-mode']); + form.append('csrfmiddlewaretoken', task.getCsrfToken(task.jar)); + form.append('production_mode', task.inputValues['production-mode']); form.submit({ - protocol: params.protocol, - hostname: params.hostname, - port: params.port, - path: params.path, - headers: form.getHeaders({ - cookie: self.jar.map(function(c) { - return c.key + "=" + encodeURIComponent(c.value); - }).join("; ") - }) - }, - function (err, response) { + protocol: params.protocol, + hostname: params.hostname, + port: params.port, + path: params.path, + headers: form.getHeaders({ + cookie: task.jar.map(function(c) { + return c.key + '=' + encodeURIComponent(c.value); + }).join('; ') + }) + }, + function(err, response) { + if (err) { + return task.ionic.fail('Error uploading certificate: ' + err); + } + + response.setEncoding('utf8'); + response.on('data', function(data) { if (err) { - return self.ionic.fail('Error uploading certificate: ' + err); + return task.ionic.fail('Error setting mode: ' + err); } - response.setEncoding('utf8'); - response.on("data", function(data) { - if (err) { - return self.ionic.fail('Error setting mode: ' + err); - } - - try { - var d = JSON.parse(data); - if (d.errors && d.errors.length) { - for (var j = 0; j < d.errors.length; j++) { - log.error((d.errors[j]).bold.red); - } - return self.ionic.fail('Unable to set mode'); - } - - if (self.inputValues['production-mode'] == 'y') { - log.info("Successfully set production mode"); - } else { - log.info("Successfully set development mode"); + try { + var d = JSON.parse(data); + if (d.errors && d.errors.length) { + for (var j = 0; j < d.errors.length; j += 1) { + log.error((d.errors[j]).bold.red); } + return task.ionic.fail('Unable to set mode'); + } - } catch (parseEx) { - return self.ionic.fail('Error response: ' + parseEx); + if (task.inputValues['production-mode'] === 'y') { + log.info('Successfully set production mode'); + } else { + log.info('Successfully set development mode'); } - }); - } - ); + + } catch (parseEx) { + return task.ionic.fail('Error response: ' + parseEx); + } + }); + }); }; -IonicTask.prototype.upload_cert = function(self) { +IonicTask.prototype.upload_cert = function(task) { // eslint-disable-line camelcase var project = IonicProject.load(); if (!project.get('app_id')) { - log.error("You need to upload your app first!".bold.red); + log.error('You need to upload your app first!'.bold.red); return false; } @@ -350,77 +346,76 @@ IonicTask.prototype.upload_cert = function(self) { prompt.start(); - prompt.get({properties: promptProperties}, function(err, promptResult) { + prompt.get({ properties: promptProperties }, function(err, promptResult) { if (err) { - return self.ionic.fail('Error: ' + err); + return task.ionic.fail('Error: ' + err); } - var url = self.ionic.IONIC_DASH + self.ionic.IONIC_API + 'app/' + project.get('app_id') + '/push/upload-push-cert'; + var url = task.ionic.IONIC_DASH + task.ionic.IONIC_API + 'app/' + project.get('app_id') + '/push/upload-push-cert'; var params = parseUrl(url); var form = new FormData(); - form.append('csrfmiddlewaretoken', self.getCsrfToken(self.jar)); + form.append('csrfmiddlewaretoken', task.getCsrfToken(task.jar)); var inputFile; try { inputFile = promptResult['ios-push-cert'].replace(/\\ /g, ' ').trim(); - form.append('ios_push_cert', fs.createReadStream( resolvePath(inputFile) ) ); - } catch(e) { - return self.ionic.fail("Error loading " + resolvePath(inputFile)); + form.append('ios_push_cert', fs.createReadStream(resolvePath(inputFile))); + } catch (e) { + return task.ionic.fail('Error loading ' + resolvePath(inputFile)); } // Set the flag for if this is a production or dev cert form.append('prod_cert', prodCert); form.submit({ - protocol: params.protocol, - hostname: params.hostname, - port: params.port, - path: params.path, - headers: form.getHeaders({ - cookie: self.jar.map(function(c) { - return c.key + "=" + encodeURIComponent(c.value); - }).join("; ") - }) - }, - function (err, response) { + protocol: params.protocol, + hostname: params.hostname, + port: params.port, + path: params.path, + headers: form.getHeaders({ + cookie: task.jar.map(function(c) { + return c.key + '=' + encodeURIComponent(c.value); + }).join('; ') + }) + }, + function(err, response) { + if (err) { + return task.ionic.fail('Error uploading certificate: ' + err); + } + + response.setEncoding('utf8'); + response.on('data', function(data) { if (err) { - return self.ionic.fail('Error uploading certificate: ' + err); + return task.ionic.fail('Error uploading certificate: ' + err); } - response.setEncoding('utf8'); - response.on("data", function(data) { - if (err) { - return self.ionic.fail('Error uploading certificate: ' + err); - } - - try { - var d = JSON.parse(data); - if (d.errors && d.errors.length) { - for (var j = 0; j < d.errors.length; j++) { - log.error((d.errors[j]).bold.red); - } - return self.ionic.fail('Unable to upload certificate'); + try { + var d = JSON.parse(data); + if (d.errors && d.errors.length) { + for (var j = 0; j < d.errors.length; j += 1) { + log.error((d.errors[j]).bold.red); } + return task.ionic.fail('Unable to upload certificate'); + } - log.info("Successfully uploaded certificate"); + log.info('Successfully uploaded certificate'); - } catch (parseEx) { - return self.ionic.fail('Error response: ' + parseEx); - } - }); - } - ); + } catch (parseEx) { + return task.ionic.fail('Error response: ' + parseEx); + } + }); + }); }); -} +}; IonicTask.prototype.getCmdLineOptions = function() { var self = this; function getCmdArgValue(propertyName, shortName) { var value = argv[propertyName] || argv[shortName]; - if(value) { + if (value) { self.inputValues[propertyName] = value; self.useCmdArgs = true; } @@ -428,9 +423,9 @@ IonicTask.prototype.getCmdLineOptions = function() { function getCmdArgFile(propertyName, shortName) { var value = argv[propertyName] || argv[shortName]; - if(value) { - if(!fileExists(value)) { - return self.ionic.fail("Unable to find file: " + argv[propertyName]); + if (value) { + if (!fileExists(value)) { + return self.ionic.fail('Unable to find file: ' + argv[propertyName]); } self.inputFiles[propertyName] = value; self.useCmdArgs = true; @@ -444,21 +439,22 @@ IonicTask.prototype.getCmdLineOptions = function() { }; IonicTask.prototype.getCsrfToken = function(jar) { - for (var i = 0; i < jar.length; i++) { - if (jar[i].key == 'csrftoken') { + for (var i = 0; i < jar.length; i += 1) { + if (jar[i].key === 'csrftoken') { return jar[i].value; } } return ''; -} +}; function fileExists(filePath) { + // check if a file exists with a relative path or absolute path filePath = filePath.replace(/\\ /g, ' ').trim(); return fs.existsSync(resolvePath(filePath)); } -function resolvePath (p) { +function resolvePath(p) { if (p.substr(0, 1) === '~') { p = process.env.HOME + p.substr(1); } diff --git a/lib/ionic/resources.js b/lib/ionic/resources.js index 7d80d8a254..9d81bd4c69 100644 --- a/lib/ionic/resources.js +++ b/lib/ionic/resources.js @@ -1,22 +1,21 @@ -var argv = require('optimist').argv, - Task = require('./task').Task, - IonicAppLib = require('ionic-app-lib'), - IonicResources = IonicAppLib.resources, - IonicStats = require('./stats').IonicStats, - Utils = IonicAppLib.utils; - +var argv = require('optimist').argv; +var Task = require('./task').Task; +var IonicAppLib = require('ionic-app-lib'); +var IonicResources = IonicAppLib.resources; +var IonicStats = require('./stats').IonicStats; +var Utils = IonicAppLib.utils; var Project = IonicAppLib.project; -var IonicTask = function() {}; + +function IonicTask() {} IonicTask.prototype = new Task(); IonicTask.prototype.run = function() { - var dir = null, - project = null; + var dir = null; try { dir = process.cwd(); - project = Project.load(dir); + Project.load(dir); } catch (ex) { return Utils.fail(ex, 'resources'); } @@ -36,4 +35,4 @@ IonicTask.prototype.run = function() { module.exports = { IonicTask: IonicTask -} +}; diff --git a/lib/ionic/security.js b/lib/ionic/security.js index 3aca3e8a2a..4b5945c5a0 100644 --- a/lib/ionic/security.js +++ b/lib/ionic/security.js @@ -1,27 +1,28 @@ -var fs = require('fs'), - path = require('path'), - Q = require('q'), - expandTilde = require('expand-tilde'), - _ = require('underscore'), - IonicAppLib = require('ionic-app-lib'), - Utils = IonicAppLib.utils, - Login = IonicAppLib.login, - Security = IonicAppLib.security, - log = IonicAppLib.logging.logger, - LoginTask = require('./login'), - Prompt = require('./prompt'), - Table = require('./table'), - Task = require('./task').Task; +var fs = require('fs'); +var path = require('path'); +var Q = require('q'); +var expandTilde = require('expand-tilde'); +var _ = require('underscore'); +var IonicAppLib = require('ionic-app-lib'); +var Utils = IonicAppLib.utils; +var Login = IonicAppLib.login; +var Security = IonicAppLib.security; +var log = IonicAppLib.logging.logger; +var LoginTask = require('./login'); +var Prompt = require('./prompt'); +var Table = require('./table'); +var Task = require('./task').Task; var Project = IonicAppLib.project; -var IonicTask = function() {}; + +function IonicTask() {} IonicTask.prototype = new Task(); IonicTask.prototype.run = function(ionic, argv) { - var cmd, - subcmd; + var cmd; + var subcmd; if (argv._.length < 2) { cmd = 'profiles'; @@ -29,9 +30,9 @@ IonicTask.prototype.run = function(ionic, argv) { cmd = argv._[1]; } - var dir = null, - project = null, - appId = null; + var dir = null; + var project = null; + var appId = null; try { dir = process.cwd(); @@ -46,37 +47,37 @@ IonicTask.prototype.run = function(ionic, argv) { } switch (cmd) { - case 'profiles': - if (argv._.length < 3) { - subcmd = 'list'; - } else { - subcmd = argv._[2]; - } + case 'profiles': + if (argv._.length < 3) { + subcmd = 'list'; + } else { + subcmd = argv._[2]; + } - switch (subcmd) { - case 'add': - return addSecurityProfile(ionic, argv, dir, appId); - case 'list': - return listSecurityProfiles(ionic, argv, dir, appId); - default: - return Utils.fail("Unknown subcommand 'profiles " + subcmd + "'.", 'security') - } - case 'credentials': - return addSecurityCredentials(ionic, argv, dir, appId); + switch (subcmd) { + case 'add': + return addSecurityProfile(ionic, argv, dir, appId); + case 'list': + return listSecurityProfiles(ionic, argv, dir, appId); + default: + return Utils.fail("Unknown subcommand 'profiles " + subcmd + "'.", 'security'); + } + case 'credentials': + return addSecurityCredentials(ionic, argv, dir, appId); } - return Utils.fail("Unknown subcommand '" + cmd + "'.", 'security') - + return Utils.fail("Unknown subcommand '" + cmd + "'.", 'security'); }; function addSecurityProfile(ionic, argv, dir, appId) { if (argv._.length < 4) { - return Utils.fail("Specify a name for this Security Profile. Example: 'ionic security profile add \"My New Profile\"'.", 'security'); + return Utils.fail('Specify a name for this Security Profile. Example: ' + + "'ionic security profile add \"My New Profile\"'.", 'security'); } - var jar, - name = argv._[3]; + var jar; + var name = argv._[3]; return Login.retrieveLogin() .then(function(jar) { @@ -91,11 +92,11 @@ function addSecurityProfile(ionic, argv, dir, appId) { return Security.addProfile(appId, jar, name); }) - .then(function(body) { + .then(function() { console.success('Added "' + name + '".'); }, function(err) { if (typeof err === 'object') { - if (err.type == 'ProfileExists') { + if (err.type === 'ProfileExists') { console.error(err.message); } else { return Q.reject(new Error(err.message)); @@ -154,16 +155,17 @@ function listSecurityProfiles(ionic, argv, dir, appId) { function addSecurityCredentials(ionic, argv, dir, appId) { if (argv._.length < 3) { - return Utils.fail("Specify a valid platform (android or ios).", 'security'); + return Utils.fail('Specify a valid platform (android or ios).', 'security'); } if (typeof argv.profile === 'undefined') { - return Utils.fail("Specify the Security Profile on which these credentials are saved (--profile ).", 'security'); + return Utils.fail('Specify the Security Profile on which these credentials are saved ' + + '(--profile ).', 'security'); } - var jar, - platform = argv._[2], - profile = argv.profile; + var jar; + var platform = argv._[2]; + var profile = argv.profile; if (!_.contains(['android', 'ios'], platform)) { return Utils.fail("Invalid platform '" + platform + "', please choose either 'android' or 'ios'.", 'security'); @@ -243,30 +245,32 @@ function addSecurityCredentials(ionic, argv, dir, appId) { }) .then(function(result) { if (platform === 'android') { - var keystoreFilePath = path.resolve(expandTilde(result['keystore'])), - keystorePassword = result['keystore-password'], - keyAlias = result['key-alias'], - keyPassword = result['key-password'], - keystoreFileStream = fs.createReadStream(keystoreFilePath); - - return Security.addAndroidCredentials(appId, jar, profile, keystoreFileStream, keystorePassword, keyAlias, keyPassword) + var keystoreFilePath = path.resolve(expandTilde(result['keystore'])); + var keystorePassword = result['keystore-password']; + var keyAlias = result['key-alias']; + var keyPassword = result['key-password']; + var keystoreFileStream = fs.createReadStream(keystoreFilePath); + + return Security.addAndroidCredentials(appId, jar, profile, keystoreFileStream, + keystorePassword, keyAlias, keyPassword); } else if (platform === 'ios') { - var certificateFilePath = path.resolve(expandTilde(result['cert'])), - certificatePassword = result['cert-password'], - provisioningProfileFilePath = path.resolve(expandTilde(result['provisioning-profile'])), - certificateFileStream = fs.createReadStream(certificateFilePath), - provisioningProfileFileStream = fs.createReadStream(provisioningProfileFilePath); - - return Security.addIOSCredentials(appId, jar, profile, certificateFileStream, certificatePassword, provisioningProfileFileStream) + var certificateFilePath = path.resolve(expandTilde(result['cert'])); + var certificatePassword = result['cert-password']; + var provisioningProfileFilePath = path.resolve(expandTilde(result['provisioning-profile'])); + var certificateFileStream = fs.createReadStream(certificateFilePath); + var provisioningProfileFileStream = fs.createReadStream(provisioningProfileFilePath); + + return Security.addIOSCredentials(appId, jar, profile, certificateFileStream, + certificatePassword, provisioningProfileFileStream); } return Q.reject('Unrecognized platform.'); }) - .then(function(body) { + .then(function() { console.success('Added ' + platform + ' credentials to your Security Profile.'); }, function(err) { if (typeof err === 'object') { - if (err.type == 'CredentialsExist') { + if (err.type === 'CredentialsExist') { console.error(err.message); } else { return Q.reject(new Error(err.message)); @@ -276,7 +280,6 @@ function addSecurityCredentials(ionic, argv, dir, appId) { .catch(function(ex) { Utils.fail(ex, 'package'); }); - -}; +} exports.IonicTask = IonicTask; diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index c9ce1a1129..c66d67f78a 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -1,32 +1,28 @@ -var IonicAppLib = require('ionic-app-lib'), - Utils = IonicAppLib.utils, - events = IonicAppLib.events, - Q = require('q'), - Task = require('./task').Task, - Serve = IonicAppLib.serve, - log = IonicAppLib.logging.logger, - IonicProject = IonicAppLib.project; +var IonicAppLib = require('ionic-app-lib'); +var Utils = IonicAppLib.utils; +var events = IonicAppLib.events; +var Q = require('q'); +var Task = require('./task').Task; +var Serve = IonicAppLib.serve; +var log = IonicAppLib.logging.logger; +var IonicProject = IonicAppLib.project; -var DEFAULT_HTTP_PORT = 8100; -var DEFAULT_LIVE_RELOAD_PORT = 35729; -var IONIC_LAB_URL = '/ionic-lab'; - -var IonicTask = function() {}; +function IonicTask() {} IonicTask.prototype = new Task(); IonicTask.prototype.run = function(ionic, argv) { - var self = this; this.ionic = ionic; this.port = argv._[1]; this.liveReloadPort = argv._[2]; - if (argv._[0] == 'address') { + if (argv._[0] === 'address') { + // reset any address configs var ionicConfig = IonicAppLib.config.load(); ionicConfig.set('ionicServeAddress', null); ionicConfig.set('platformServeAddress', null); - return Serve.getAddress({isAddressCmd: true}); + return Serve.getAddress({ isAddressCmd: true }); } var project = null; @@ -41,11 +37,11 @@ IonicTask.prototype.run = function(ionic, argv) { var options = Serve.loadSettings(argv, project); - options.appDirectory = cwd;//called from cli - ionic serve - cwd is this + options.appDirectory = cwd; // called from cli - ionic serve - cwd is this options.nogulp = argv.nogulp; - //check if CONNECT_LIVE_RELOAD_PORT is defined and use that instead + // check if CONNECT_LIVE_RELOAD_PORT is defined and use that instead if (process.env.CONNECT_LIVE_RELOAD_PORT) { options.liveReloadPort = process.env.CONNECT_LIVE_RELOAD_PORT; } @@ -65,11 +61,10 @@ IonicTask.prototype.run = function(ionic, argv) { return promise .then(function() { - // log.info('options.address', options.address); return Serve.checkPorts(true, options.port, options.address, options); }) .then(function() { - if(options.runLivereload) { + if (options.runLivereload) { return Serve.checkPorts(false, options.liveReloadPort, options.address, options); } }) @@ -80,7 +75,6 @@ IonicTask.prototype.run = function(ionic, argv) { return Serve.showFinishedServeMessage(options); }) .then(function() { - // return Serve.listenToServeLogs(options); events.on('serverlog', log.info); events.on('consolelog', log.info); }) diff --git a/lib/ionic/service.js b/lib/ionic/service.js index 0f8c32a3e2..2d5ffe2dbc 100644 --- a/lib/ionic/service.js +++ b/lib/ionic/service.js @@ -1,15 +1,14 @@ -require('shelljs/global') +require('shelljs/global'); -var Task = require('./task').Task, - IonicCordova = require('./cordova').IonicTask, - argv = require('optimist').argv, - fs = require('fs'), - path = require('path'), - _ = require('underscore'), - bower = require('./bower'), - log = require('ionic-app-lib').logging.logger; +var Task = require('./task').Task; +var fs = require('fs'); +var path = require('path'); +var exec = require('child_process').exec; +var _ = require('underscore'); +var bower = require('./bower'); +var log = require('ionic-app-lib').logging.logger; -var IonicTask = function() {}; +function IonicTask() {} IonicTask.prototype = new Task(); @@ -19,10 +18,12 @@ IonicTask.prototype.readIonicProjectJson = function readIonicProjectJson() { try { var content = fs.readFileSync(ionicProjectFile, 'utf8'); ionicProjectJson = JSON.parse(content); - } catch (ex) { } + } catch (ex) { + throw ex; + } return ionicProjectJson; -} +}; IonicTask.prototype.addServiceToIonicJson = function addServiceToIonicJson(serviceName) { var ionicProjectFile = path.join(process.cwd(), 'ionic.project'); @@ -34,122 +35,112 @@ IonicTask.prototype.addServiceToIonicJson = function addServiceToIonicJson(servi ionicProjectJson.services = []; } - var existingProject = _.findWhere(ionicProjectJson.services, {name: serviceName}); + var existingProject = _.findWhere(ionicProjectJson.services, { name: serviceName }); if (typeof existingProject != 'undefined') { return; } - ionicProjectJson.services.push({name: serviceName}); + ionicProjectJson.services.push({ name: serviceName }); fs.writeFileSync(ionicProjectFile, JSON.stringify(ionicProjectJson, null, 2), 'utf8'); } catch (ex) { this.ionic.fail('Failed to update the ionic.project settings for the service', 'service'); } -} +}; IonicTask.prototype.installBowerComponent = function installBowerComponent(serviceName) { - // log.info('We are here now.'); var bowerInstallCommand = 'bower link ionic-service-' + serviceName; - // var bowerInstallCommand = 'bower install ionic-service-' + serviceName; - var result = exec(bowerInstallCommand); - if (result.code != 0) { - //Error happened, report it. - var errorMessage = 'Failed to find the service "'.bold + serviceName.verbose + '"'.bold + '.\nAre you sure it exists?'.bold; + if (result.code !== 0) { + + // Error happened, report it. + var errorMessage = 'Failed to find the service "'.bold + serviceName.verbose + + '"'.bold + '.\nAre you sure it exists?'.bold; this.ionic.fail(errorMessage, 'service'); } else { this.addServiceToIonicJson(serviceName); } -} +}; IonicTask.prototype.uninstallBowerComponent = function uninstallBowerComponent(serviceName) { var bowerUninstallCommand = 'bower unlink ionic-service-' + serviceName; var result = exec(bowerUninstallCommand); - if (result.code != 0) { - var errorMessage = 'Failed to find the service "'.bold + serviceName.verbose + '"'.bold + '.\nAre you sure it exists?'.bold; - this.ionic.fail(errorMessage, 'service') - } else { - // log.info('Uninstalled Ionic service', serviceName); + if (result.code !== 0) { + var errorMessage = 'Failed to find the service "'.bold + serviceName.verbose + + '"'.bold + '.\nAre you sure it exists?'.bold; + this.ionic.fail(errorMessage, 'service'); } -} +}; IonicTask.prototype.getBowerComponentsLocation = function getBowerComponentsLocation() { var bowerRcFileLocation = path.join(process.cwd(), '.bowerrc'); - // log.info('location bowerrc: ' , bowerRcFileLocation) var bowerRc = null; try { var content = fs.readFileSync(bowerRcFileLocation, 'utf8'); bowerRc = JSON.parse(content); - // log.info('bowerRc contents: ', bowerRc) - } catch (ex) { } + } catch (ex) { + throw ex; + } - var directory = 'www/lib'; //Default directory + var directory = 'www/lib'; // Default directory if (bowerRc && bowerRc.directory) { directory = bowerRc.directory; } return directory; -} +}; IonicTask.prototype.getBowerJson = function getBowerJson(directory, serviceName) { var bowerJsonLocation = path.join(process.cwd(), directory, 'ionic-service-' + serviceName, 'ionic-plugins.json'); var packageBowerJson = require(bowerJsonLocation); return packageBowerJson; -} +}; IonicTask.prototype.installBowerPlugins = function installBowerPlugins(directory, serviceName) { - // var bowerJsonLocation = process.cwd() + '/' + directory + '/ionic-service-' + serviceName + '/bower.json'; - // var packageBowerJson = require(bowerJsonLocation); var packageBowerJson = this.getBowerJson(directory, serviceName); - // log.info('bowerjson = ', packageBowerJson.plugins); - // log.info('ionic - ', IonicCordova); _.each(packageBowerJson.plugins, function(plugin) { - // var ic = new IonicCordova(); - // ic.run('ionic plugin add ' + plugin); log.info('Installing cordova plugin - ' + plugin.name + ' (' + plugin.id + ')'); var installPluginCmd = 'ionic plugin add ' + plugin.uri; log.info(installPluginCmd); var pluginInstallResult = exec(installPluginCmd); - if (pluginInstallResult.code != 0) { + if (pluginInstallResult.code !== 0) { var errorMessage = 'Failed to find the plugin "'.bold + plugin.name.verbose + '"'.bold + '.'.bold; this.ionic.fail(errorMessage, 'service'); } - // log.info(pluginInstallResult); - }) -} + }); +}; IonicTask.prototype.uninstallBowerPlugins = function uninstallBowerPlugins(bowerJson) { _.each(bowerJson.plugins, function(plugin) { log.info('Uninstalling cordova plugin - ' + plugin.name); var uninstallPluginCmd = 'ionic plugin rm ' + plugin.id; log.info(uninstallPluginCmd); - var pluginRemoveResult = exec(uninstallPluginCmd) + var pluginRemoveResult = exec(uninstallPluginCmd); - if (pluginRemoveResult.code != 0) { + if (pluginRemoveResult.code !== 0) { var errorMessage = 'Failed to find the plugin to remove "'.bold + plugin.name.verbose + '"'.bold + '.'.bold; this.ionic.fail(errorMessage, 'service'); } - }) -} + }); +}; IonicTask.prototype.addService = function addService(serviceName) { this.installBowerComponent(serviceName); var directory = this.getBowerComponentsLocation(); - // log.info('Directory we are searching for bower.json - ', directory); log.info('Checking for any plugins required by service package'); this.installBowerPlugins(directory, serviceName); log.info('ionic service add completed'); -} +}; IonicTask.prototype.removeService = function removeService(serviceName) { var directory = this.getBowerComponentsLocation(); @@ -158,21 +149,16 @@ IonicTask.prototype.removeService = function removeService(serviceName) { this.uninstallBowerComponent(serviceName); this.uninstallBowerPlugins(packageBowerJson); - -} +}; IonicTask.prototype.listServices = function listServices() { - // var directory = this.getBowerComponentsLocation(); - var bowerJson = require(process.cwd() + '/bower.json'); - // log.info(bowerJson); - -} +}; // Need to look at bower.json of package just installed and look for any cordova plugins required // Check the directory in the projects `.bowerrc` file // Then go to /path/to/bower/components//ionic-plugins.json - 'plugins' // For each plugins - call 'ionic add plugin ' -IonicTask.prototype.run = function run(ionic, argv, callback) { +IonicTask.prototype.run = function run(ionic, argv) { this.ionic = ionic; if (!bower.IonicBower.checkForBower()) { @@ -180,25 +166,25 @@ IonicTask.prototype.run = function run(ionic, argv, callback) { return; } - var action = argv._[1] + var action = argv._[1]; var serviceName = argv._[2]; try { switch (action) { - case 'add': - this.addService(serviceName); - break; - case 'remove': - this.removeService(serviceName); - break; - case 'list': - this.listServices(); - break; + case 'add': + this.addService(serviceName); + break; + case 'remove': + this.removeService(serviceName); + break; + case 'list': + this.listServices(); + break; } } catch (error) { var errorMessage = error.message ? error.message : error; - this.ionic.fail(errorMessage, 'service') + this.ionic.fail(errorMessage, 'service'); } -} +}; exports.IonicTask = IonicTask; diff --git a/lib/ionic/setup.js b/lib/ionic/setup.js index c246cdf889..5bba986374 100644 --- a/lib/ionic/setup.js +++ b/lib/ionic/setup.js @@ -1,11 +1,12 @@ var Task = require('./task').Task; -var IonicTask = function() {}; +function IonicTask() {} IonicTask.prototype = new Task(); IonicTask.prototype.run = function() { - //TODO link to gulp hook docs + + // TODO link to gulp hook docs console.log('The setup task has been deprecated.\n'); }; diff --git a/lib/ionic/share.js b/lib/ionic/share.js index b768ecbffd..699caccae3 100644 --- a/lib/ionic/share.js +++ b/lib/ionic/share.js @@ -1,27 +1,22 @@ -var path = require('path'), - parseUrl = require('url').parse, - shelljs = require('shelljs/global'), - argv = require('optimist').boolean(['no-cordova', 'sass', 'list']).argv, - Q = require('q'), - FormData = require('form-data'), - IonicProject = require('./project'), - IonicStore = require('./store').IonicStore, - Task = require('./task').Task, - LoginTask = require('./login'), - IonicAppLib = require('ionic-app-lib'), - Share = IonicAppLib.share, - log = IonicAppLib.logging.logger, - Login = IonicAppLib.login, - Utils = IonicAppLib.utils; +require('shelljs/global'); -var IonicTask = function() {}; +var IonicProject = require('./project'); +var Task = require('./task').Task; +var LoginTask = require('./login'); +var IonicAppLib = require('ionic-app-lib'); +var Share = IonicAppLib.share; +var log = IonicAppLib.logging.logger; +var Login = IonicAppLib.login; +var Utils = IonicAppLib.utils; + +function IonicTask() {} IonicTask.prototype = new Task(); IonicTask.prototype.run = function(ionic, argv) { var project; - if(argv._.length < 2) { + if (argv._.length < 2) { return ionic.fail('Invalid command', 'share'); } @@ -29,23 +24,23 @@ IonicTask.prototype.run = function(ionic, argv) { project = IonicProject.load(); } catch (ex) { ionic.fail(ex.message); - return + return; } - if (project.get('app_id') == '') { + if (project.get('app_id') === '') { return ionic.fail('You must first upload the app to share it'); } var email = argv._[1]; - if(email.indexOf('@') < 0) { + if (email.indexOf('@') < 0) { return ionic.fail('Invalid email address', 'share'); } log.info(['Sharing app ', project.get('name'), ' (', project.get('app_id'), ') with ', email, '.'].join('').green); return Login.retrieveLogin() - .then(function(jar){ + .then(function(jar) { if (!jar) { log.info('No previous login existed. Attempting to log in now.'); return LoginTask.login(argv); @@ -56,7 +51,6 @@ IonicTask.prototype.run = function(ionic, argv) { return Share.shareApp(process.cwd(), jar, email); }) .catch(function(ex) { - // log.info('Error', ex, ex.stack); return Utils.fail(ex); }); }; diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 77445d1ee7..07debdbb54 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -1,20 +1,18 @@ -var fs = require('fs'), - shelljs = require('shelljs/global'), - // argv = require('optimist').boolean(['no-cordova', 'sass', 'list', 'w']).argv, - prompt = require('prompt'), - colors = require('colors'), - Q = require('q'), - IonicTemplates = require('./templates').IonicTask, - IonicStore = require('./store').IonicStore, - Task = require('./task').Task, - IonicAppLib = require('ionic-app-lib'), - Start = IonicAppLib.start, - log = IonicAppLib.logging.logger, - Utils = IonicAppLib.utils; +require('shelljs/global'); +require('colors'); +var fs = require('fs'); +var Q = require('q'); +var IonicTemplates = require('./templates').IonicTask; +var Task = require('./task').Task; +var IonicAppLib = require('ionic-app-lib'); +var Start = IonicAppLib.start; +var log = IonicAppLib.logging.logger; +var Utils = IonicAppLib.utils; + + +function IonicTask() {} -var Start; -var IonicTask = function() {}; IonicTask.prototype = new Task(); IonicTask.prototype.run = function run(ionic, argv) { @@ -28,16 +26,16 @@ IonicTask.prototype.run = function run(ionic, argv) { return ionic.fail('Invalid command', 'start'); } - if (argv._[1] == '.') { + if (argv._[1] === '.') { log.error('Please name your Ionic project something meaningful other than \'.\''.red); - return + return; } - var promptPromise, - options = Utils.preprocessCliOptions(argv), - startingApp = true; - // Grab the app's relative directory name + var promptPromise; + var options = Utils.preprocessCliOptions(argv); + var startingApp = true; + // Grab the app's relative directory name if (fs.existsSync(options.targetPath)) { promptPromise = Start.promptForOverwrite(options.targetPath); } else { @@ -58,13 +56,6 @@ IonicTask.prototype.run = function run(ionic, argv) { return Start.printQuickHelp(options); } }) - /* - .then(function() { - if (startingApp) { - return ionic.printNewsUpdates(true); - } - }) - */ .then(function() { if (startingApp) { return Start.promptLogin(options); @@ -76,7 +67,6 @@ IonicTask.prototype.run = function run(ionic, argv) { } }) .catch(function(error) { - // return Utils.fail(error); log.error(error); throw error; }); diff --git a/lib/ionic/state.js b/lib/ionic/state.js index 133cd53a41..809bb467ba 100644 --- a/lib/ionic/state.js +++ b/lib/ionic/state.js @@ -1,99 +1,51 @@ -// "cordovaPlatforms": [ -// "ios", -// { -// "android": { -// "id": "android", -// "locator": "https://github.com/apache/cordova-android.git" -// } -// } -// ], -// "cordovaPlugins": [ -// "org.apache.cordova.device", -// "org.apache.cordova.console", -// "com.ionic.keyboard", -// "org.apache.cordova.splashscreen", -// { -// "locator": "https://github.com/MobileChromeApps/cordova-crosswalk-engine.git", -// "id": "org.cordova.croswalk" -// }, -// { -// "locator": "/path/to/cloned/phonegap-facebook-plugin", -// "id": "", -// "variables": { -// "APP_ID": "some_id", -// "APP_NAME": "some_name" -// } -// } -// ] - -var fs = require('fs'), - path = require('path'), - // argv = require('optimist').boolean(['plugins', 'platforms']).argv, - Q = require('q'), - shelljs = require('shelljs'), - Task = require('./task').Task, - _ = require('underscore'), - IonicAppLib = require('ionic-app-lib'), - IonicProject = IonicAppLib.project, - State = IonicAppLib.state, - Utils = IonicAppLib.utils, - log = IonicAppLib.logging.logger, - IonicInfo = IonicAppLib.info; +var shelljs = require('shelljs'); +var Task = require('./task').Task; +var IonicAppLib = require('ionic-app-lib'); +var IonicProject = IonicAppLib.project; +var State = IonicAppLib.state; +var log = IonicAppLib.logging.logger; // var State = module.exports; shelljs.config.silent = true; -var IonicTask = function() {}; +function IonicTask() {} IonicTask.prototype = new Task(); IonicTask.prototype.run = function run(ionic, argv) { - var self = this, - project, - stats, - projectPath, - options = { platforms: true, plugins: true }; + var options = { platforms: true, plugins: true }; this.ionic = ionic; - // try { - // projectPath = path.resolve('ionic.project'); - // stats = fs.statSync(projectPath); - // } catch (ex) { - // this.ionic.fail('You cannot run any state commands on a project that is not an Ionic project.\nTry adding an ionic.project file or running ionic start to get an application to save or restore'); - // return; - // } - try { - project = IonicProject.load(); + IonicProject.load(); } catch (ex) { this.ionic.fail(ex.message); return; } - //If either plugin or platforms is specified, set it to that value. + // If either plugin or platforms is specified, set it to that value. if (argv.platforms || argv.plugins) { options = { platforms: argv.platforms, plugins: argv.plugins }; } switch (argv._[1]) { - case 'save': - State.saveState(process.cwd(), options); - break; - case 'restore': - State.restoreState(process.cwd(), options); - break; - case 'reset': - State.resetState(process.cwd(), options); - break; - case 'clear': - State.clearState(process.cwd(), options); - break; - default: - log.error('Please specify a command [ save | restore | reset | clear ] for the state command.'); + case 'save': + State.saveState(process.cwd(), options); + break; + case 'restore': + State.restoreState(process.cwd(), options); + break; + case 'reset': + State.resetState(process.cwd(), options); + break; + case 'clear': + State.clearState(process.cwd(), options); + break; + default: + log.error('Please specify a command [ save | restore | reset | clear ] for the state command.'); } - }; exports.IonicTask = IonicTask; diff --git a/lib/ionic/stats.js b/lib/ionic/stats.js index a276fe5daf..f5cf1b2884 100644 --- a/lib/ionic/stats.js +++ b/lib/ionic/stats.js @@ -1,511 +1,510 @@ -var MixpanelAPI, crypto, Buffer, http, querystring, util, path, fs, os, IonicConfig; -var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, - __slice = Array.prototype.slice; - -var path = require('path'), - http = require('http'), - _ = require('underscore'); - querystring = require('querystring'), - crypto = require('crypto'), - Buffer = require('buffer').Buffer, - util = require('util'), - fs = require('fs'), - os = require('os'), - IonicConfig = require('ionic-app-lib').config, - IonicInfo = require('ionic-app-lib').info; +/* eslint-disable camelcase, no-underscore-dangle */ + +var path = require('path'); +var http = require('http'); +var _ = require('underscore'); +var querystring = require('querystring'); +var Buffer = require('buffer').Buffer; +var util = require('util'); +var IonicConfig = require('ionic-app-lib').config; +var IonicInfo = require('ionic-app-lib').info; exports.getVersion = function getVersion() { var packageJson = require('../../package.json'); return packageJson; }; -/* - Heavily inspired by the original js library copyright Mixpanel, Inc. - (http://mixpanel.com/) +/** + * Heavily inspired by the original js library copyright Mixpanel, Inc. + * (http://mixpanel.com/) + * + * Copyright (c) 2012 Carl Sverre + * + * Released under the MIT license. + */ - Copyright (c) 2012 Carl Sverre +function create_client(token, config) { + var metrics = {}; - Released under the MIT license. -*/ + if (!token) { + throw new Error('The Mixpanel Client needs a Mixpanel token: `init(token)`'); + } -var create_client = function(token, config) { - var metrics = {}; + metrics.config = { + test: false, + debug: false, + verbose: false + }; + + metrics.token = token; + + /** + * send_request(data) + * --- + * this function sends an async GET request to mixpanel + * + * data:object the data to send in the request + * callback:function(err:Error) callback is called when the request is + * finished or an error occurs + */ + metrics.send_request = function(endpoint, data, callback) { + callback = callback || function() {}; + var event_data = new Buffer(JSON.stringify(data)); + var request_data = { + data: event_data.toString('base64'), + ip: 0, + verbose: metrics.config.verbose ? 1 : 0 + }; - if(!token) { - throw new Error("The Mixpanel Client needs a Mixpanel token: `init(token)`"); + if (endpoint === '/import') { + var key = metrics.config.key; + if (!key) { + throw new Error('The Mixpanel Client needs a Mixpanel api key when importing old events: ' + + ' `init(token, { key: ... })`'); + } + request_data.api_key = key; } - metrics.config = { - test: false, - debug: false, - verbose: false + var request_options = { + host: 'api.mixpanel.com', + port: 80, + headers: {} }; - metrics.token = token; + if (metrics.config.test) { request_data.test = 1; } - /** - send_request(data) - --- - this function sends an async GET request to mixpanel - - data:object the data to send in the request - callback:function(err:Error) callback is called when the request is - finished or an error occurs - */ - metrics.send_request = function(endpoint, data, callback) { - callback = callback || function() {}; - var event_data = new Buffer(JSON.stringify(data)); - var request_data = { - 'data': event_data.toString('base64'), - 'ip': 0, - 'verbose': metrics.config.verbose ? 1 : 0 - }; - - if (endpoint === '/import') { - var key = metrics.config.key; - if (!key) { - throw new Error("The Mixpanel Client needs a Mixpanel api key when importing old events: `init(token, { key: ... })`"); - } - request_data.api_key = key; - } + var query = querystring.stringify(request_data); - var request_options = { - host: 'api.mixpanel.com', - port: 80, - headers: {} - }; - - if (metrics.config.test) { request_data.test = 1; } - - var query = querystring.stringify(request_data); - - request_options.path = [endpoint,"?",query].join(""); - - http.get(request_options, function(res) { - var data = ""; - res.on('data', function(chunk) { - data += chunk; - }); - - res.on('end', function() { - var e; - if(metrics.config.verbose) { - try { - var result = JSON.parse(data); - if(result.status != 1) { - e = new Error("Mixpanel Server Error: " + result.error); - } - } - catch(ex) { - e = new Error("Could not parse response from Mixpanel"); - } - } - else { - e = (data !== '1') ? new Error("Mixpanel Server Error: " + data) : undefined; - } - - callback(e); - }); - }).on('error', function(e) { - if(metrics.config.debug) { - console.log("Got Error: " + e.message); - } - callback(e); - }); - }; - - /** - track(event, properties, callback) - --- - this function sends an event to mixpanel. - - event:string the event name - properties:object additional event properties to send - callback:function(err:Error) callback is called when the request is - finished or an error occurs - */ - metrics.track = function(event, properties, callback) { - if (typeof(properties) === 'function' || !properties) { - callback = properties; - properties = {}; - } + request_options.path = [endpoint, '?', query].join(''); - // if properties.time exists, use import endpoint - var endpoint = (typeof(properties.time) === 'number') ? '/import' : '/track'; + http.get(request_options, function(res) { + var data = ''; + res.on('data', function(chunk) { + data += chunk; + }); - properties.token = metrics.token; - properties.mp_lib = "node"; + res.on('end', function() { + var e; - var data = { - 'event' : event, - 'properties' : properties - }; - - if (metrics.config.debug) { - console.log("Sending the following event to Mixpanel:"); - console.log(data); - } - - metrics.send_request(endpoint, data, callback); - }; - - /** - import(event, properties, callback) - --- - This function sends an event to mixpanel using the import - endpoint. The time argument should be either a Date or Number, - and should signify the time the event occurred. - - It is highly recommended that you specify the distinct_id - property for each event you import, otherwise the events will be - tied to the IP address of the sending machine. - - For more information look at: - https://mixpanel.com/docs/api-documentation/importing-events-older-than-31-days - - event:string the event name - time:date|number the time of the event - properties:object additional event properties to send - callback:function(err:Error) callback is called when the request is - finished or an error occurs - */ - metrics.import = function(event, time, properties, callback) { - if (typeof(properties) === 'function' || !properties) { - callback = properties; - properties = {}; + if (metrics.config.verbose) { + try { + var result = JSON.parse(data); + if (result.status !== 1) { + e = new Error('Mixpanel Server Error: ' + result.error); + } + } catch (ex) { + e = new Error('Could not parse response from Mixpanel'); + } + } else { + e = (data !== '1') ? new Error('Mixpanel Server Error: ' + data) : undefined; } - if (time === void 0) { - throw new Error("The import method requires you to specify the time of the event"); - } else if (Object.prototype.toString.call(time) === '[object Date]') { - time = Math.floor(time.getTime() / 1000); - } + callback(e); + }); + }).on('error', function(e) { + if (metrics.config.debug) { + console.log('Got Error: ' + e.message); + } + callback(e); + }); + }; + + /** + * track(event, properties, callback) + * --- + * this function sends an event to mixpanel. + * + * event:string the event name + * properties:object additional event properties to send + * callback:function(err:Error) callback is called when the request is + * finished or an error occurs + */ + metrics.track = function(event, properties, callback) { + if (typeof(properties) === 'function' || !properties) { + callback = properties; + properties = {}; + } - properties.time = time; + // if properties.time exists, use import endpoint + var endpoint = (typeof(properties.time) === 'number') ? '/import' : '/track'; - metrics.track(event, properties, callback); - }; + properties.token = metrics.token; + properties.mp_lib = 'node'; - /** - alias(distinct_id, alias) - --- - This function creates an alias for distinct_id - - For more information look at: - https://mixpanel.com/docs/integration-libraries/using-mixpanel-alias - - distinct_id:string the current identifier - alias:string the future alias - */ - metrics.alias = function(distinct_id, alias, callback) { - var properties = { - distinct_id: distinct_id, - alias: alias - }; - - metrics.track('$create_alias', properties, callback); + var data = { + event: event, + properties: properties }; - metrics.people = { - /** people.set_once(distinct_id, prop, to, callback) - --- - The same as people.set but in the words of mixpanel: - mixpanel.people.set_once - - " This method allows you to set a user attribute, only if - it is not currently set. It can be called multiple times - safely, so is perfect for storing things like the first date - you saw a user, or the referrer that brought them to your - website for the first time. " - - */ - set_once: function(distinct_id, prop, to, callback) { - var $set = {}, data = {}; - - if (typeof(prop) === 'object') { - callback = to; - $set = prop; - } else { - $set[prop] = to; - } - - this._set(distinct_id, $set, callback, { set_once: true }); - }, - - /** - people.set(distinct_id, prop, to, callback) - --- - set properties on an user record in engage + if (metrics.config.debug) { + console.log('Sending the following event to Mixpanel:'); + console.log(data); + } - usage: + metrics.send_request(endpoint, data, callback); + }; + + /** + * import(event, properties, callback) + * --- + * This function sends an event to mixpanel using the import + * endpoint. The time argument should be either a Date or Number, + * and should signify the time the event occurred. + * + * It is highly recommended that you specify the distinct_id + * property for each event you import, otherwise the events will be + * tied to the IP address of the sending machine. + * + * For more information look at: + * https://mixpanel.com/docs/api-documentation/importing-events-older-than-31-days + * + * event:string the event name + * time:date|number the time of the event + * properties:object additional event properties to send + * callback:function(err:Error) callback is called when the request is + * finished or an error occurs + */ + metrics.import = function(event, time, properties, callback) { + if (typeof(properties) === 'function' || !properties) { + callback = properties; + properties = {}; + } - mixpanel.people.set('bob', 'gender', 'm'); + if (time === void 0) { + throw new Error('The import method requires you to specify the time of the event'); + } else if (Object.prototype.toString.call(time) === '[object Date]') { + time = Math.floor(time.getTime() / 1000); + } - mixpanel.people.set('joe', { - 'company': 'acme', - 'plan': 'premium' - }); - */ - set: function(distinct_id, prop, to, callback) { - var $set = {}, data = {}; + properties.time = time; + + metrics.track(event, properties, callback); + }; + + /** + * alias(distinct_id, alias) + * --- + * This function creates an alias for distinct_id + * + * For more information look at: + * https://mixpanel.com/docs/integration-libraries/using-mixpanel-alias + * + * distinct_id:string the current identifier + * alias:string the future alias + */ + metrics.alias = function(distinct_id, alias, callback) { + var properties = { + distinct_id: distinct_id, + alias: alias + }; - if (typeof(prop) === 'object') { - callback = to; - $set = prop; - } else { - $set[prop] = to; - } + metrics.track('$create_alias', properties, callback); + }; + + metrics.people = { + + /** people.set_once(distinct_id, prop, to, callback) + * --- + * The same as people.set but in the words of mixpanel: + * mixpanel.people.set_once + * + * " This method allows you to set a user attribute, only if + * it is not currently set. It can be called multiple times + * safely, so is perfect for storing things like the first date + * you saw a user, or the referrer that brought them to your + * website for the first time. " + * + */ + set_once: function(distinct_id, prop, to, callback) { + var $set = {}; + + if (typeof(prop) === 'object') { + callback = to; + $set = prop; + } else { + $set[prop] = to; + } - this._set(distinct_id, $set, callback); - }, + this._set(distinct_id, $set, callback, { set_once: true }); + }, - // used internally by set and set_once - _set: function(distinct_id, $set, callback, options) { - var set_key = (options && options.set_once) ? "$set_once" : "$set"; + /** + * people.set(distinct_id, prop, to, callback) + * --- + * set properties on an user record in engage + * + * usage: + * + * mixpanel.people.set('bob', 'gender', 'm'); + * + * mixpanel.people.set('joe', { + * 'company': 'acme', + * 'plan': 'premium' + * }); + */ + set: function(distinct_id, prop, to, callback) { + var $set = {}; + + if (typeof(prop) === 'object') { + callback = to; + $set = prop; + } else { + $set[prop] = to; + } - var data = { - '$token': metrics.token, - '$distinct_id': distinct_id - }; - data[set_key] = $set; + this._set(distinct_id, $set, callback); + }, - if ('ip' in $set) { - data.$ip = $set.ip; - delete $set.ip; - } + // used internally by set and set_once + _set: function(distinct_id, $set, callback, options) { + var set_key = (options && options.set_once) ? '$set_once' : '$set'; - if ($set.$ignore_time) { - data.$ignore_time = $set.$ignore_time; - delete $set.$ignore_time; - } + var data = { + $token: metrics.token, + $distinct_id: distinct_id + }; + data[set_key] = $set; - if(metrics.config.debug) { - console.log("Sending the following data to Mixpanel (Engage):"); - console.log(data); - } + if ('ip' in $set) { + data.$ip = $set.ip; + delete $set.ip; + } - metrics.send_request('/engage', data, callback); - }, + if ($set.$ignore_time) { + data.$ignore_time = $set.$ignore_time; + delete $set.$ignore_time; + } - /** - people.increment(distinct_id, prop, to, callback) - --- - increment/decrement properties on an user record in engage - - usage: - - mixpanel.people.increment('bob', 'page_views', 1); - - // or, for convenience, if you're just incrementing a counter by 1, you can - // simply do - mixpanel.people.increment('bob', 'page_views'); - - // to decrement a counter, pass a negative number - mixpanel.people.increment('bob', 'credits_left', -1); - - // like mixpanel.people.set(), you can increment multiple properties at once: - mixpanel.people.increment('bob', { - counter1: 1, - counter2: 3, - counter3: -2 - }); - */ - increment: function(distinct_id, prop, by, callback) { - var $add = {}; - - if (typeof(prop) === 'object') { - callback = by; - Object.keys(prop).forEach(function(key) { - var val = prop[key]; - - if (isNaN(parseFloat(val))) { - if (metrics.config.debug) { - console.error("Invalid increment value passed to mixpanel.people.increment - must be a number"); - console.error("Passed " + key + ":" + val); - } - return; - } else { - $add[key] = val; - } - }); - } else { - if (!by) { by = 1; } - $add[prop] = by; - } + if (metrics.config.debug) { + console.log('Sending the following data to Mixpanel (Engage):'); + console.log(data); + } - var data = { - '$add': $add, - '$token': metrics.token, - '$distinct_id': distinct_id - }; + metrics.send_request('/engage', data, callback); + }, - if(metrics.config.debug) { - console.log("Sending the following data to Mixpanel (Engage):"); - console.log(data); + /** + * people.increment(distinct_id, prop, to, callback) + * --- + * increment/decrement properties on an user record in engage + * + * usage: + * + * mixpanel.people.increment('bob', 'page_views', 1); + * + * // or, for convenience, if you're just incrementing a counter by 1, you can + * // simply do + * mixpanel.people.increment('bob', 'page_views'); + * + * // to decrement a counter, pass a negative number + * mixpanel.people.increment('bob', 'credits_left', -1); + * + * // like mixpanel.people.set(), you can increment multiple properties at once: + * mixpanel.people.increment('bob', { + * counter1: 1, + * counter2: 3, + * counter3: -2 + * }); + */ + increment: function(distinct_id, prop, by, callback) { + var $add = {}; + + if (typeof(prop) === 'object') { + callback = by; + Object.keys(prop).forEach(function(key) { + var val = prop[key]; + + if (isNaN(parseFloat(val))) { + if (metrics.config.debug) { + console.error('Invalid increment value passed to mixpanel.people.increment - must be a number'); + console.error('Passed ' + key + ':' + val); } + return; + } else { + $add[key] = val; + } + }); + } else { + if (!by) { by = 1; } + $add[prop] = by; + } - metrics.send_request('/engage', data, callback); - }, - - /** - people.track_charge(distinct_id, amount, properties, callback) - --- - Record that you have charged the current user a certain - amount of money. - - usage: - - // charge a user $29.99 - mixpanel.people.track_charge('bob', 29.99); - - // charge a user $19 on the 1st of february - mixpanel.people.track_charge('bob', 19, { '$time': new Date('feb 1 2012') }); - */ - track_charge: function(distinct_id, amount, properties, callback) { - var $append = {}; - - if (!properties) { properties = {}; } + var data = { + $add: $add, + $token: metrics.token, + $distinct_id: distinct_id + }; - if (typeof(amount) !== 'number') { - amount = parseFloat(amount); - if (isNaN(amount)) { - console.error("Invalid value passed to mixpanel.people.track_charge - must be a number"); - return; - } - } + if (metrics.config.debug) { + console.log('Sending the following data to Mixpanel (Engage):'); + console.log(data); + } - properties.$amount = amount; + metrics.send_request('/engage', data, callback); + }, - if (properties.hasOwnProperty('$time')) { - var time = properties.$time; - if (Object.prototype.toString.call(time) === '[object Date]') { - properties.$time = time.toISOString(); - } - } + /** + * people.track_charge(distinct_id, amount, properties, callback) + * --- + * Record that you have charged the current user a certain + * amount of money. + * + * usage: + * + * // charge a user $29.99 + * mixpanel.people.track_charge('bob', 29.99); + * + * // charge a user $19 on the 1st of february + * mixpanel.people.track_charge('bob', 19, { '$time': new Date('feb 1 2012') }); + */ + track_charge: function(distinct_id, amount, properties, callback) { + + if (!properties) { properties = {}; } + + if (typeof(amount) !== 'number') { + amount = parseFloat(amount); + if (isNaN(amount)) { + console.error('Invalid value passed to mixpanel.people.track_charge - must be a number'); + return; + } + } - var data = { - '$append': { '$transactions': properties }, - '$token': metrics.token, - '$distinct_id': distinct_id - }; + properties.$amount = amount; - if(metrics.config.debug) { - console.log("Sending the following data to Mixpanel (Engage):"); - console.log(data); - } + if (properties.hasOwnProperty('$time')) { + var time = properties.$time; + if (Object.prototype.toString.call(time) === '[object Date]') { + properties.$time = time.toISOString(); + } + } - metrics.send_request('/engage', data, callback); + var data = { + $append: { + $transactions: properties }, + $token: metrics.token, + $distinct_id: distinct_id + }; - /** - people.clear_charges(distinct_id, callback) - --- - Clear all the current user's transactions. - - usage: - - mixpanel.people.clear_charges('bob'); - */ - clear_charges: function(distinct_id, callback) { - var data = { - '$set': { '$transactions': [] }, - '$token': metrics.token, - '$distinct_id': distinct_id - }; + if (metrics.config.debug) { + console.log('Sending the following data to Mixpanel (Engage):'); + console.log(data); + } - if(metrics.config.debug) { - console.log("Clearing this user's charges:", distinct_id); - } + metrics.send_request('/engage', data, callback); + }, - metrics.send_request('/engage', data, callback); + /** + * people.clear_charges(distinct_id, callback) + * --- + * Clear all the current user's transactions. + * + * usage: + * + * mixpanel.people.clear_charges('bob'); + */ + clear_charges: function(distinct_id, callback) { + var data = { + $set: { + $transactions: [] }, + $token: metrics.token, + $distinct_id: distinct_id + }; - /** - people.delete_user(distinct_id, callback) - --- - delete an user record in engage - - usage: - - mixpanel.people.delete_user('bob'); - */ - delete_user: function(distinct_id, callback) { - var data = { - '$delete': distinct_id, - '$token': metrics.token, - '$distinct_id': distinct_id - }; - - if(metrics.config.debug) { - console.log("Deleting the user from engage:", distinct_id); - } - - metrics.send_request('/engage', data, callback); - }, + if (metrics.config.debug) { + console.log("Clearing this user's charges:", distinct_id); + } - /** - people.unset(distinct_id, prop, callback) - --- - delete a property on an user record in engage - - usage: - - mixpanel.people.unset('bob', 'page_views'); - - mixpanel.people.unset('bob', ['page_views', 'last_login']); - */ - unset: function(distinct_id, prop, callback) { - var $unset = []; - - if (util.isArray(prop)) { - $unset = prop; - } else if (typeof(prop) === 'string') { - $unset = [prop]; - } else { - if (metrics.config.debug) { - console.error("Invalid argument passed to mixpanel.people.unset - must be a string or array"); - console.error("Passed: " + prop); - } - return; - } + metrics.send_request('/engage', data, callback); + }, - var data = { - '$unset': $unset, - '$token': metrics.token, - '$distinct_id': distinct_id - }; + /** + * people.delete_user(distinct_id, callback) + * --- + * delete an user record in engage + * + * usage: + * + * mixpanel.people.delete_user('bob'); + */ + delete_user: function(distinct_id, callback) { + var data = { + $delete: distinct_id, + $token: metrics.token, + $distinct_id: distinct_id + }; - if(metrics.config.debug) { - console.log("Sending the following data to Mixpanel (Engage):"); - console.log(data); - } + if (metrics.config.debug) { + console.log('Deleting the user from engage:', distinct_id); + } - metrics.send_request('/engage', data, callback); - } - }; + metrics.send_request('/engage', data, callback); + }, /** - set_config(config) - --- - Modifies the mixpanel config - - config:object an object with properties to override in the - mixpanel client config - */ - metrics.set_config = function(config) { - for (var c in config) { - if (config.hasOwnProperty(c)) { - metrics.config[c] = config[c]; - } + * people.unset(distinct_id, prop, callback) + * --- + * delete a property on an user record in engage + * + * usage: + * + * mixpanel.people.unset('bob', 'page_views'); + * + * mixpanel.people.unset('bob', ['page_views', 'last_login']); + */ + unset: function(distinct_id, prop, callback) { + var $unset = []; + + if (util.isArray(prop)) { + $unset = prop; + } else if (typeof(prop) === 'string') { + $unset = [prop]; + } else { + if (metrics.config.debug) { + console.error('Invalid argument passed to mixpanel.people.unset - must be a string or array'); + console.error('Passed: ' + prop); } - }; + return; + } + + var data = { + $unset: $unset, + $token: metrics.token, + $distinct_id: distinct_id + }; + + if (metrics.config.debug) { + console.log('Sending the following data to Mixpanel (Engage):'); + console.log(data); + } - if (config) { - metrics.set_config(config); + metrics.send_request('/engage', data, callback); } + }; + + /** + * set_config(config) + * --- + * Modifies the mixpanel config + * + * config:object an object with properties to override in the + * mixpanel client config + */ + metrics.set_config = function(config) { + for (var c in config) { + if (config.hasOwnProperty(c)) { + metrics.config[c] = config[c]; + } + } + }; - return metrics; -}; + if (config) { + metrics.set_config(config); + } + + return metrics; +} // module exporting /* @@ -529,9 +528,9 @@ exports.IonicStats = { var packageJson = exports.getVersion(); - if(process.argv.length < 3) return; + if (process.argv.length < 3) return; - if( ionicConfig.get('statsOptOut') === true ) { + if (ionicConfig.get('statsOptOut') === true) { return; } @@ -540,13 +539,16 @@ exports.IonicStats = { var statsData = additionalData || {}; var platforms = []; - var x, y, cmd; + var x; + var y; + var cmd; // update any aliases with the full cmd so there's a common property + // TODO: This probably does not work correctly var aliasMap = { - 'rm': 'remove', - 'ls': 'list', - 'up': 'update', + rm: 'remove', + ls: 'list', + up: 'update', '-w': '--no-cordova', '-b': '--nobrowser', '-r': '--nolivereload', @@ -554,35 +556,37 @@ exports.IonicStats = { '-l': '--livereload', '-c': '--consolelogs', '-s': '--serverlogs', - '-n': '--no-email', - '-s': '--sass' + '-n': '--no-email' }; - for(x=0; x': 'The email to share the app with', + '': 'The email to share the app with' }, module: './ionic/share' }, @@ -297,18 +296,27 @@ var TASKS = [ name: 'security', summary: 'Store your app\'s credentials for the Ionic Platform ' + '(alpha)'.red, args: { - '': 'profiles list'.yellow + ', ' + 'profiles add ""'.yellow + ', ' + 'credentials android'.yellow + ', or ' + 'credentials ios'.yellow, + '': 'profiles list'.yellow + ', ' + 'profiles add ""'.yellow + ', ' + + 'credentials android'.yellow + ', or ' + 'credentials ios'.yellow, '[options]': '' }, options: { - '--profile ': '(' + 'credentials '.yellow + ') Specify the profile on which these credentials are saved', - '--keystore|-s ': '(' + 'credentials android'.yellow + ') Specify the location of your keystore file', - '--keystore-password|-p ': '(' + 'credentials android'.yellow + ') Specify your keystore password (exclude for prompt)', - '--key-alias|-k ': '(' + 'credentials android'.yellow + ') Specify your key alias for this app', - '--key-password|-w ': '(' + 'credentials android'.yellow + ') Specify your key password for this app (exclude for prompt)', - '--cert|-c ': '(' + 'credentials ios'.yellow + ') Specify the location of your .p12 file', - '--cert-password|-p ': '(' + 'credentials ios'.yellow + ') Specify your certificate password (exclude for prompt)', - '--provisioning-profile|-r ': '(' + 'credentials ios'.yellow + ') Specify the location of your .mobileprovision file', + '--profile ': '(' + 'credentials '.yellow + + ') Specify the profile on which these credentials are saved', + '--keystore|-s ': '(' + 'credentials android'.yellow + + ') Specify the location of your keystore file', + '--keystore-password|-p ': '(' + 'credentials android'.yellow + + ') Specify your keystore password (exclude for prompt)', + '--key-alias|-k ': '(' + 'credentials android'.yellow + + ') Specify your key alias for this app', + '--key-password|-w ': '(' + 'credentials android'.yellow + + ') Specify your key password for this app (exclude for prompt)', + '--cert|-c ': '(' + 'credentials ios'.yellow + + ') Specify the location of your .p12 file', + '--cert-password|-p ': '(' + 'credentials ios'.yellow + + ') Specify your certificate password (exclude for prompt)', + '--provisioning-profile|-r ': '(' + 'credentials ios'.yellow + + ') Specify the location of your .mobileprovision file' }, module: './ionic/security' }, @@ -329,14 +337,19 @@ var TASKS = [ name: 'package', summary: 'Use Ionic Package to build your app ' + '(alpha)'.red, args: { - '': 'build android'.yellow + ', ' + 'build ios'.yellow + ', ' + 'list'.yellow + ', ' + 'info'.yellow + ', or ' + 'download'.yellow, + '': 'build android'.yellow + ', ' + 'build ios'.yellow + + ', ' + 'list'.yellow + ', ' + 'info'.yellow + ', or ' + 'download'.yellow, '[options]': '' }, options: { - '--release': '(' + 'build '.yellow + ') Mark this build as a release', - '--profile|-p ': '(' + 'build '.yellow + ') Specify the Security Profile to use with this build', - '--noresources': '(' + 'build '.yellow + ') Do not generate icon and splash screen resources during this build', - '--destination|-d ': '(' + 'download'.yellow + ') Specify the destination directory to download your packaged app.' + '--release': '(' + 'build '.yellow + + ') Mark this build as a release', + '--profile|-p ': '(' + 'build '.yellow + + ') Specify the Security Profile to use with this build', + '--noresources': '(' + 'build '.yellow + + ') Do not generate icon and splash screen resources during this build', + '--destination|-d ': '(' + 'download'.yellow + + ') Specify the destination directory to download your packaged app.' }, module: './ionic/package' }, @@ -376,8 +389,8 @@ var TASKS = [ '': 'Can be a service name or a git url' }, module: './ionic/service' - }, - { + }, + { title: 'add', name: 'add', summary: 'Add an Ion, bower component, or addon to the project', @@ -385,8 +398,8 @@ var TASKS = [ '[name]': 'The name of the ion, bower component, or addon you wish to install' }, module: './ionic/add' - }, - { + }, + { title: 'remove', name: 'remove', summary: 'Remove an Ion, bower component, or addon from the project', @@ -395,45 +408,46 @@ var TASKS = [ }, module: './ionic/add', alt: ['rm'] - }, - { + }, + { title: 'list', name: 'list', summary: 'List Ions, bower components, or addons in the project', module: './ionic/add', alt: ['ls'] - }, - /* - { + }, + + /* + { title: 'ions', name: 'ions', summary: 'List available ions to add to your project', module: './ionic/ions' - }, - { + }, + { title: 'templates', name: 'templates', summary: 'List available Ionic starter templates', module: './ionic/templates' - }, - */ - { - title: 'info', - name: 'info', - summary: 'List information about the users runtime environment', - module: './ionic/info' - }, - { - title: 'help', - name: 'help', - summary: 'Provides help for a certain command', - args: { - '[command]': 'The command you desire help with' - }, - module: './ionic/help', - disableChangePwd: true - }, - { + }, + */ + { + title: 'info', + name: 'info', + summary: 'List information about the users runtime environment', + module: './ionic/info' + }, + { + title: 'help', + name: 'help', + summary: 'Provides help for a certain command', + args: { + '[command]': 'The command you desire help with' + }, + module: './ionic/help', + disableChangePwd: true + }, + { title: 'link', name: 'link', summary: 'Sets your Ionic App ID for your project', @@ -447,9 +461,8 @@ var TASKS = [ } }, module: './ionic/link' - }, - { - + }, + { title: 'hooks', name: 'hooks', summary: 'Manage your Ionic Cordova hooks', @@ -466,10 +479,11 @@ var TASKS = [ '': '[ save | restore | clear | reset ]' }, options: { - 'save': 'Save the platforms and plugins into package.json', - 'restore': 'Restore the platforms and plugins from package.json', - 'clear': 'Clear the package.json of cordovaPlugins and cordovaPlatforms, as well as clear out the platforms and plugins folders', - 'reset': 'Clear out the platforms and plugins directories, and reinstall plugins and platforms', + save: 'Save the platforms and plugins into package.json', + restore: 'Restore the platforms and plugins from package.json', + clear: 'Clear the package.json of cordovaPlugins and cordovaPlatforms, ' + + 'as well as clear out the platforms and plugins folders', + reset: 'Clear out the platforms and plugins directories, and reinstall plugins and platforms', '--plugins': { title: 'Only do operations with plugins', boolean: true @@ -480,8 +494,8 @@ var TASKS = [ } }, module: './ionic/state' - }, - { + }, + { title: 'docs', name: 'docs', summary: 'Opens up the documentation for Ionic', @@ -490,8 +504,8 @@ var TASKS = [ }, module: './ionic/docs', disableChangePwd: true - }, - { + }, + { title: 'generate', alt: ['g'], name: 'generate', @@ -506,8 +520,8 @@ var TASKS = [ boolean: true, title: '(with --v2 only) Use TypeScript in generation' } - }, - } + } + } ]; diff --git a/lib/tasks/createDocsJson.js b/lib/tasks/createDocsJson.js index 44dc396227..3132024eff 100644 --- a/lib/tasks/createDocsJson.js +++ b/lib/tasks/createDocsJson.js @@ -1,15 +1,12 @@ -var fs = require('fs'), - path = require('path'), - cl = console.log; - - +var fs = require('fs'); +var path = require('path'); +var cl = console.log; function crawlDocsCreateJson() { - var docsJson = { versions: [], api: {} - } + }; var docsPath = path.resolve('docs'); var docsApiPath = path.join(docsPath, 'api'); @@ -17,31 +14,31 @@ function crawlDocsCreateJson() { var docsDirs = fs.readdirSync(docsPath); docsDirs.forEach(function(doc) { - var isDocVersion = !isNaN(doc[0]) - if(isDocVersion) { - docsJson.versions.push(doc) + var isDocVersion = !isNaN(doc[0]); + if (isDocVersion) { + docsJson.versions.push(doc); } - }) + }); var apiDocsDirs = fs.readdirSync(docsApiPath); apiDocsDirs.forEach(function(docs) { console.log('docs:', docs); - if(docs.indexOf('.md') != -1) { - return + if (docs.indexOf('.md') !== -1) { + return; } - docsJson.api[docs] = {id: docs, docs: []}; + docsJson.api[docs] = { id: docs, docs: [] }; - var apiDocPath = path.join(docsApiPath, docs) - console.log('apiDocPath:', apiDocPath) + var apiDocPath = path.join(docsApiPath, docs); + console.log('apiDocPath:', apiDocPath); - var apiSubDocs = fs.readdirSync(apiDocPath) + var apiSubDocs = fs.readdirSync(apiDocPath); apiSubDocs.forEach(function(subdoc) { - console.log('subdoc: ', subdoc) + console.log('subdoc: ', subdoc); docsJson.api[docs].docs.push(subdoc); - }) - }) + }); + }); // cl('json:') // cl(docsJson) @@ -51,8 +48,7 @@ function crawlDocsCreateJson() { // cl(e) // }) - cl(JSON.stringify(docsJson)) - + cl(JSON.stringify(docsJson)); } diff --git a/spec/.eslintrc.js b/spec/.eslintrc.js index d1f5e00643..3f1a19a2bb 100644 --- a/spec/.eslintrc.js +++ b/spec/.eslintrc.js @@ -1,5 +1,8 @@ module.exports = { env: { jasmine: true + }, + rules: { + 'no-underscore-dangle': 0 } }; diff --git a/spec/cli.spec.js b/spec/cli.spec.js index 22bbf6263d..8faeffe051 100644 --- a/spec/cli.spec.js +++ b/spec/cli.spec.js @@ -1,13 +1,13 @@ -var IonicAppLib = require('ionic-app-lib'), - Ionitron = require('../lib/ionic/ionitron'), - IonicCli = require('../lib/cli'), - Q = require('q'), - IonicStats = require('../lib/ionic/stats').IonicStats, - Task = require('../lib/ionic/task').Task, - Info = IonicAppLib.info, - Utils = IonicAppLib.utils, - Project = IonicAppLib.project, - rewire = require('rewire'); +var IonicAppLib = require('ionic-app-lib'); +var Ionitron = require('../lib/ionic/ionitron'); +var IonicCli = require('../lib/cli'); +var Q = require('q'); +var IonicStats = require('../lib/ionic/stats').IonicStats; +var Task = require('../lib/ionic/task').Task; +var Info = IonicAppLib.info; +var Utils = IonicAppLib.utils; +var Project = IonicAppLib.project; +var rewire = require('rewire'); describe('Cli', function() { @@ -21,7 +21,7 @@ describe('Cli', function() { spyOn(Utils, 'cdIonicRoot'); spyOn(Project, 'load'); - spyOn(Utils, 'fail').andCallFake(function(err){ + spyOn(Utils, 'fail').andCallFake(function(err) { console.log(err); console.log(err.stack); throw err; @@ -39,7 +39,7 @@ describe('Cli', function() { describe('#run', function() { beforeEach(function() { - var fakeTask = function() {}; + function fakeTask() {} fakeTask.prototype = new Task(); fakeTask.prototype.run = function() {}; @@ -112,10 +112,10 @@ describe('Cli', function() { }); it('should get boolean options from start task', function() { - var tasks = require('../lib/tasks/cliTasks'); var task = IonicCli.getTaskWithName('start'); var booleanOptions = IonicCli.getBooleanOptionsForTask(task); - //We expect 6 total = 3 options, each with short hand notation. + + // We expect 6 total = 3 options, each with short hand notation. expect(booleanOptions.length).toBe(11); }); @@ -151,14 +151,14 @@ describe('Cli', function() { }); it('should parse start options correctly', function(done) { - var processArgs = [ 'node', '/usr/local/bin/ionic', 'start', 's1', '-w', '--appname', 'asdf']; + var processArgs = ['node', '/usr/local/bin/ionic', 'start', 's1', '-w', '--appname', 'asdf']; var promise = IonicCli.run(processArgs); - var _fakeTask = fakeTask; + var fakeTaskRef = fakeTask; - promise.then(function(){ - expect(_fakeTask.prototype.run).toHaveBeenCalled(); - var taskArgs = _fakeTask.prototype.run.mostRecentCall.args; + promise.then(function() { + expect(fakeTaskRef.prototype.run).toHaveBeenCalled(); + var taskArgs = fakeTaskRef.prototype.run.mostRecentCall.args; var taskArgv = taskArgs[1]; expect(taskArgv._.length).toBe(2); @@ -178,18 +178,18 @@ describe('Cli', function() { }); it('should parse serve options correctly', function(done) { - var processArgs = [ 'node', '/usr/local/bin/ionic', 'serve', '--nogulp', '--all', '--browser', 'firefox']; + var processArgs = ['node', '/usr/local/bin/ionic', 'serve', '--nogulp', '--all', '--browser', 'firefox']; var promise = IonicCli.run(processArgs); - var _fakeTask = fakeTask; + var fakeTaskRef = fakeTask; - promise.then(function(){ - expect(_fakeTask.prototype.run).toHaveBeenCalled(); - var taskArgs = _fakeTask.prototype.run.mostRecentCall.args; + promise.then(function() { + expect(fakeTaskRef.prototype.run).toHaveBeenCalled(); + var taskArgs = fakeTaskRef.prototype.run.mostRecentCall.args; var taskArgv = taskArgs[1]; - // console.log('taskArgv', taskArgv); - //should only have serve in the command args + + // should only have serve in the command args expect(taskArgv._.length).toBe(1); expect(taskArgv.browser).toBe('firefox'); expect(taskArgv.nogulp).toBe(true); @@ -203,18 +203,19 @@ describe('Cli', function() { it('should parse upload options correctly', function(done) { var note = 'A note for notes'; - var processArgs = [ 'node', '/usr/local/bin/ionic', 'upload', '--email', 'user@ionic.io', '--password', 'pass', '--note', note]; + var processArgs = ['node', '/usr/local/bin/ionic', 'upload', '--email', + 'user@ionic.io', '--password', 'pass', '--note', note]; var promise = IonicCli.run(processArgs); - var _fakeTask = fakeTask; + var fakeTaskRef = fakeTask; - promise.then(function(){ - expect(_fakeTask.prototype.run).toHaveBeenCalled(); - var taskArgs = _fakeTask.prototype.run.mostRecentCall.args; + promise.then(function() { + expect(fakeTaskRef.prototype.run).toHaveBeenCalled(); + var taskArgs = fakeTaskRef.prototype.run.mostRecentCall.args; var taskArgv = taskArgs[1]; - //should only have serve in the command args + // should only have serve in the command args expect(taskArgv._.length).toBe(1); expect(taskArgv.note).toBe(note); expect(taskArgv.email).toBe('user@ionic.io'); @@ -224,18 +225,18 @@ describe('Cli', function() { }); it('should parse login options correctly', function(done) { - var processArgs = [ 'node', '/usr/local/bin/ionic', 'login', '--email', 'user@ionic.io', '--password', 'pass']; + var processArgs = ['node', '/usr/local/bin/ionic', 'login', '--email', 'user@ionic.io', '--password', 'pass']; var promise = IonicCli.run(processArgs); - var _fakeTask = fakeTask; + var fakeTaskRef = fakeTask; - promise.then(function(){ - expect(_fakeTask.prototype.run).toHaveBeenCalled(); - var taskArgs = _fakeTask.prototype.run.mostRecentCall.args; + promise.then(function() { + expect(fakeTaskRef.prototype.run).toHaveBeenCalled(); + var taskArgs = fakeTaskRef.prototype.run.mostRecentCall.args; var taskArgv = taskArgs[1]; - //should only have serve in the command args + // should only have serve in the command args expect(taskArgv._.length).toBe(1); expect(taskArgv.email).toBe('user@ionic.io'); expect(taskArgv.password).toBe('pass'); @@ -244,18 +245,19 @@ describe('Cli', function() { }); it('should parse run options correctly', function(done) { - var processArgs = [ 'node', '/usr/local/bin/ionic', 'run', 'ios', '--livereload', '--port', '5000', '-r', '35730', '--consolelogs', '--serverlogs', '--device']; + var processArgs = ['node', '/usr/local/bin/ionic', 'run', 'ios', '--livereload', + '--port', '5000', '-r', '35730', '--consolelogs', '--serverlogs', '--device']; var promise = IonicCli.run(processArgs); - var _fakeTask = fakeTask; + var fakeTaskRef = fakeTask; - promise.then(function(){ - expect(_fakeTask.prototype.run).toHaveBeenCalled(); - var taskArgs = _fakeTask.prototype.run.mostRecentCall.args; + promise.then(function() { + expect(fakeTaskRef.prototype.run).toHaveBeenCalled(); + var taskArgs = fakeTaskRef.prototype.run.mostRecentCall.args; var taskArgv = taskArgs[1]; - //should only have serve in the command args + // should only have serve in the command args expect(taskArgv._.length).toBe(2); expect(taskArgv.r).toBe(35730); expect(taskArgv.port).toBe(5000); @@ -267,18 +269,20 @@ describe('Cli', function() { }); }); - it('should parse emulate options correctly', function() { - var processArgs = [ 'node', '/usr/local/bin/ionic', 'emulate', 'android', '--livereload', '--address', 'localhost', '--port', '5000', '-r', '35730', '--consolelogs', '--serverlogs']; + it('should parse emulate options correctly', function(done) { + var processArgs = ['node', '/usr/local/bin/ionic', 'emulate', 'android', + '--livereload', '--address', 'localhost', '--port', '5000', '-r', '35730', '--consolelogs', '--serverlogs']; var promise = IonicCli.run(processArgs); - var _fakeTask = fakeTask; + var fakeTaskRef = fakeTask; - promise.then(function(){ - expect(_fakeTask.prototype.run).toHaveBeenCalled(); - var taskArgs = _fakeTask.prototype.run.mostRecentCall.args; + promise.then(function() { + expect(fakeTaskRef.prototype.run).toHaveBeenCalled(); + var taskArgs = fakeTaskRef.prototype.run.mostRecentCall.args; var taskArgv = taskArgs[1]; - //should only have serve in the command args + + // should only have serve in the command args expect(taskArgv._.length).toBe(2); expect(taskArgv._[1]).toBe('android'); expect(taskArgv.r).toBe(35730); @@ -292,18 +296,18 @@ describe('Cli', function() { }); it('should parse state options correctly', function(done) { - var processArgs = [ 'node', '/usr/local/bin/ionic', 'state', 'save', '--plugins']; + var processArgs = ['node', '/usr/local/bin/ionic', 'state', 'save', '--plugins']; var promise = IonicCli.run(processArgs); - var _fakeTask = fakeTask; + var fakeTaskRef = fakeTask; - promise.then(function(){ - expect(_fakeTask.prototype.run).toHaveBeenCalled(); - var taskArgs = _fakeTask.prototype.run.mostRecentCall.args; + promise.then(function() { + expect(fakeTaskRef.prototype.run).toHaveBeenCalled(); + var taskArgs = fakeTaskRef.prototype.run.mostRecentCall.args; var taskArgv = taskArgs[1]; - //should only have serve in the command args + // should only have serve in the command args expect(taskArgv._.length).toBe(2); expect(taskArgv._[1]).toBe('save'); expect(taskArgv.plugins).toBe(true); @@ -313,18 +317,19 @@ describe('Cli', function() { }); it('should parse plugin options correctly', function(done) { - var processArgs = [ 'node', '/usr/local/bin/ionic', 'plugin', 'add', 'org.apache.cordova.splashscreen', '--nosave', '--searchpath', '../']; + var processArgs = ['node', '/usr/local/bin/ionic', 'plugin', 'add', + 'org.apache.cordova.splashscreen', '--nosave', '--searchpath', '../']; var promise = IonicCli.run(processArgs); - var _fakeTask = fakeTask; + var fakeTaskRef = fakeTask; - promise.then(function(){ - expect(_fakeTask.prototype.run).toHaveBeenCalled(); - var taskArgs = _fakeTask.prototype.run.mostRecentCall.args; + promise.then(function() { + expect(fakeTaskRef.prototype.run).toHaveBeenCalled(); + var taskArgs = fakeTaskRef.prototype.run.mostRecentCall.args; var taskArgv = taskArgs[1]; - //should only have serve in the command args + // should only have serve in the command args expect(taskArgv._.length).toBe(3); expect(taskArgv._[0]).toBe('plugin'); expect(taskArgv._[1]).toBe('add'); @@ -336,18 +341,18 @@ describe('Cli', function() { }); it('should parse build options correctly', function(done) { - var processArgs = [ 'node', '/usr/local/bin/ionic', 'build', 'ios', '--nohooks']; + var processArgs = ['node', '/usr/local/bin/ionic', 'build', 'ios', '--nohooks']; var promise = IonicCli.run(processArgs); - var _fakeTask = fakeTask; + var fakeTaskRef = fakeTask; - promise.then(function(){ - expect(_fakeTask.prototype.run).toHaveBeenCalled(); - var taskArgs = _fakeTask.prototype.run.mostRecentCall.args; + promise.then(function() { + expect(fakeTaskRef.prototype.run).toHaveBeenCalled(); + var taskArgs = fakeTaskRef.prototype.run.mostRecentCall.args; var taskArgv = taskArgs[1]; - //should only have serve in the command args + // should only have serve in the command args expect(taskArgv._.length).toBe(2); expect(taskArgv._[0]).toBe('build'); expect(taskArgv._[1]).toBe('ios'); @@ -363,7 +368,7 @@ describe('Cli', function() { }); xit('should do runtime check when version is not checked', function() { - var IonicConfigSpy = createSpyObj('IonicConfig', ['get', 'set', 'save']); + var IonicConfigSpy = jasmine.createSpyObj('IonicConfig', ['get', 'set', 'save']); IonicConfigSpy.get.andReturn('1.6.4'); IonicCli.__set__('IonicConfig', IonicConfigSpy); IonicCli.doRuntimeCheck('1.6.4'); @@ -373,7 +378,7 @@ describe('Cli', function() { }); xit('should do runtime check when version is not checked', function() { - var IonicConfigSpy = createSpyObj('IonicConfig', ['get', 'set', 'save']); + var IonicConfigSpy = jasmine.createSpyObj('IonicConfig', ['get', 'set', 'save']); IonicConfigSpy.get.andReturn('1.6.4'); IonicCli.__set__('IonicConfig', IonicConfigSpy); IonicCli.doRuntimeCheck('1.6.5'); @@ -382,7 +387,6 @@ describe('Cli', function() { expect(IonicConfigSpy.set).toHaveBeenCalledWith('lastVersionChecked', '1.6.5'); expect(IonicConfigSpy.save).toHaveBeenCalled(); }); - }) - + }); }); }); diff --git a/spec/serve.spec.js b/spec/serve.spec.js index 0bef54cf0a..9f2c1bd7dc 100644 --- a/spec/serve.spec.js +++ b/spec/serve.spec.js @@ -1,14 +1,13 @@ -var IonicAppLib = require('ionic-app-lib'), - IonicProject = IonicAppLib.project, - Serve = IonicAppLib.serve, - Q = require('q'), - rewire = require('rewire'), - Utils = IonicAppLib.utils; +var IonicAppLib = require('ionic-app-lib'); +var IonicProject = IonicAppLib.project; +var Serve = IonicAppLib.serve; +var Q = require('q'); +var rewire = require('rewire'); var argv = { _: ['--livereload'], nogulp: false -} +}; describe('Serve', function() { var serveTask; @@ -41,13 +40,13 @@ describe('Serve', function() { }; Q() - .then(function(){ + .then(function() { return serve.run({}, argv); }) .then(function() { expect(Serve.start).toHaveBeenCalledWith(options); }) - .catch(function(ex){ + .catch(function(ex) { expect('this').toBe(ex.stack); }) .fin(done); @@ -70,14 +69,14 @@ describe('Serve', function() { var serve = new serveTask.IonicTask(); Q() - .then(function(){ + .then(function() { return serve.run({}, argv); }) .then(function() { var calls = Serve.start.calls[0].args[0]; expect(calls.liveReloadPort).toBe(35729); }) - .catch(function(ex){ + .catch(function(ex) { expect('this').toBe(ex.stack); }) .fin(done); diff --git a/spec/start.spec.js b/spec/start.spec.js index 2bb971ca8a..60f26969e0 100644 --- a/spec/start.spec.js +++ b/spec/start.spec.js @@ -1,13 +1,4 @@ -var IonicAppLib = require('ionic-app-lib'), - IonicProject = IonicAppLib.project, - Serve = IonicAppLib.serve, - Q = require('q'), - rewire = require('rewire'); - -var argv = { - _: ['--livereload'], - nogulp: false -} +var rewire = require('rewire'); describe('Start', function() { var startTask; diff --git a/spec/stats.spec.js b/spec/stats.spec.js index 38b0fcc532..cad77d7e97 100644 --- a/spec/stats.spec.js +++ b/spec/stats.spec.js @@ -1,13 +1,12 @@ -var Q = require('q'), - // IonicStats = require('../lib/ionic/stats').IonicStats, - IonicStatsModule, - IonicStats, - path = require('path'), - rewire = require('rewire'); +var IonicStatsModule; +var IonicInfoModule; +var IonicStats; +var rewire = require('rewire'); describe('Stats', function() { beforeEach(function() { IonicStatsModule = rewire('../lib/ionic/stats'); + IonicInfoModule = rewire('ionic-app-lib').info; IonicStats = IonicStatsModule.IonicStats; }); @@ -22,7 +21,7 @@ describe('Stats', function() { spyOn(IonicStats, 'mp'); - var configSpy = createSpyObj('ionicConfig', ['get']); + var configSpy = jasmine.createSpyObj('ionicConfig', ['get']); configSpy.get.andReturn(true); IonicStatsModule.__set__('ionicConfig', configSpy); @@ -35,9 +34,9 @@ describe('Stats', function() { }); it('should not track stats if opted out', function() { - var configSpy = createSpyObj('ionicConfig', ['get']); + var configSpy = jasmine.createSpyObj('ionicConfig', ['get']); configSpy.get.andReturn(true); - spyOn(IonicStatsModule, 'getVersion').andReturn({version: '1.6.4'}); + spyOn(IonicStatsModule, 'getVersion').andReturn({ version: '1.6.4' }); IonicStatsModule.__set__('ionicConfig', configSpy); @@ -53,15 +52,27 @@ describe('Stats', function() { spyOn(IonicStats, 'mp'); spyOn(IonicStatsModule, 'getVersion').andReturn(packageJson); + spyOn(IonicInfoModule, 'gatherGulpInfo').andCallFake(function(info) { + info.os = 'Mac OS X El Capitan'; + info.node = 'v5.10.1'; + info.gulp = 'v3.0.0'; + }); - var configSpy = createSpyObj('ionicConfig', ['get']); + var configSpy = jasmine.createSpyObj('ionicConfig', ['get']); configSpy.get.andReturn(false); IonicStatsModule.__set__('ionicConfig', configSpy); IonicStats.t(); - expect(IonicStats.mp).toHaveBeenCalledWith('start', { cli_version: packageJson.version, email: false, account_id: false }); + expect(IonicStats.mp).toHaveBeenCalledWith('start', { + cli_version: packageJson.version, // eslint-disable-line camelcase + email: false, + account_id: false, // eslint-disable-line camelcase + os: 'Mac OS X El Capitan', + gulp: 'v3.0.0', + node: 'v5.10.1' + }); process.argv = oldprocessargv; }); }); From 5e283ce947679acf57f114ab0b4131d12e4edcb6 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Tue, 26 Apr 2016 11:20:26 -0500 Subject: [PATCH 0964/1100] fix(eslint): add parseInt to all request response checks because type coersion was removed. --- lib/cli.js | 2 +- lib/ionic/lib.js | 8 ++++---- lib/ionic/push.js | 6 +++--- lib/ionic/templates.js | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index 90cda0fd16..fb6cd9de5b 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -671,7 +671,7 @@ Cli.printNewsUpdates = function printNewsUpdates(skipNewsCheck) { var downloadUrl = 'http://code.ionicframework.com/content/cli-message.json'; request({ url: downloadUrl, proxy: proxy }, function(err, res, html) { - if (!err && res && res.statusCode === 200) { + if (!err && res && parseInt(res.statusCode, 10) === 200) { try { var newsId = IonicConfig.get('newsId'); var messagesJson = JSON.parse(html); diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index 1b3b018d8f..68007446f6 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -93,12 +93,12 @@ IonicTask.prototype.getVersionData = function(version) { q.reject(); return; } - if (res.statusCode === 404) { + if (parseInt(res.statusCode, 10) === 404) { log.error('Invalid version: ' + version); q.reject(); return; } - if (res.statusCode >= 400) { + if (parseInt(res.statusCode, 10) >= 400) { log.error('Unable to load version data'); q.reject(); return; @@ -220,11 +220,11 @@ IonicTask.prototype.downloadZip = function(version) { log.error(err); return; } - if (res.statusCode === 404 || res.statusCode === 406) { + if (parseInt(res.statusCode, 10) === 404 || parseInt(res.statusCode, 10) === 406) { log.error('Invalid version: ' + version); return; } - if (res.statusCode >= 400) { + if (parseInt(res.statusCode, 10) >= 400) { log.error('Unable to download zip (' + res.statusCode + ')'); return; } diff --git a/lib/ionic/push.js b/lib/ionic/push.js index 04073dba41..a7f6b31e6f 100644 --- a/lib/ionic/push.js +++ b/lib/ionic/push.js @@ -97,7 +97,7 @@ IonicTask.prototype.set_webhook_url = function(task, webhook_url) { // eslint-di return task.ionic.fail('Error setting Webhook URL: ' + err); } - if (response.statusCode === '200') { + if (parseInt(response.statusCode, 10) === '200') { log.info('Webhook URL set to', webhook_url); } else { return task.ionic.fail('App not found'); @@ -141,7 +141,7 @@ IonicTask.prototype.set_google_api_key = function(task, google_api_key) { // esl return task.ionic.fail('Error setting Google API Key: ' + err); } - if (response.statusCode === '200') { + if (parseInt(response.statusCode, 10) === '200') { log.info('Google API Key Saved'.green); } else { return task.ionic.fail('App not found'); @@ -232,7 +232,7 @@ IonicTask.prototype.send_push = function(task) { // eslint-disable-line camelcas }; request(options, function(err, response) { - if (!err && response.statusCode === 202) { + if (!err && parseInt(response.statusCode, 10) === 202) { log.info('Successfully queued push notification'); } else { log.error('Error queueing push notification', err); diff --git a/lib/ionic/templates.js b/lib/ionic/templates.js index 0c99f0490f..e7c30dc564 100644 --- a/lib/ionic/templates.js +++ b/lib/ionic/templates.js @@ -23,7 +23,7 @@ IonicTask.prototype.fetchStarterTemplates = function() { var proxy = process.env.PROXY || null; request({ url: downloadUrl, proxy: proxy }, function(err, res, html) { - if (!err && res && res.statusCode === 200) { + if (!err && res && parseInt(res.statusCode, 10) === 200) { var templatesJson = {}; try { templatesJson = JSON.parse(html); From 2dbd92ced0ae565e8adc7b03dfc4527f8568521e Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Tue, 26 Apr 2016 15:33:37 -0500 Subject: [PATCH 0965/1100] fix(login): correct email regex that was split to change decrease line length. --- lib/ionic/login.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 8678cbbe77..73a6f5f3b8 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -52,8 +52,7 @@ LoginTask.promptForLogin = function promptForLogin(argv) { var schema = [{ name: 'email', - pattern: new RegExp(["^[A-z0-9!#$%&'*+/=?^_{|}~-]+(?:.[A-z0-9!#$%&'*+/=?^_{|}~-]+)", - '*@(?:[A-z0-9](?:[A-z0-9-]*[A-z0-9])?.)+[A-z0-9](?:[A-z0-9-]*[A-z0-9])?$'].join('')), + pattern: /^[A-z0-9!#$%&'*+\/=?\^_{|}~\-]+(?:\.[A-z0-9!#$%&'*+\/=?\^_{|}~\-]+)*@(?:[A-z0-9](?:[A-z0-9\-]*[A-z0-9])?\.)+[A-z0-9](?:[A-z0-9\-]*[A-z0-9])?$/, // eslint-disable-line max-len description: 'Email:'.yellow.bold, required: true }, { From e89e28a4db23ab359cf10a1d1ba473ac747798dc Mon Sep 17 00:00:00 2001 From: Tim Lancina Date: Thu, 28 Apr 2016 16:10:38 -0500 Subject: [PATCH 0966/1100] chore(npm): add lint script --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 2329b09943..8fc1b2eb3f 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "scripts": { "bump": "node lib/tasks/bumpversion", "beta-bump": "node lib/tasks/bumpversion --level pre --identifier beta", + "lint": "eslint .", "publish-release": "node lib/tasks/bumpversion --npmPublish", "publish-tag": "node lib/tasks/bumpversion --level beta --npmPublishTag", "full-release": "node lib/tasks/bumpversion --npmInstall --git --npmPublish", From cd725539561caa95a847768ae13be2ba67903fdd Mon Sep 17 00:00:00 2001 From: Tim Lancina Date: Thu, 28 Apr 2016 16:11:11 -0500 Subject: [PATCH 0967/1100] fix(store): don't error if config isn't found It will be created on the first set --- lib/ionic/store.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/ionic/store.js b/lib/ionic/store.js index 978f1fc681..92c7eacc5f 100644 --- a/lib/ionic/store.js +++ b/lib/ionic/store.js @@ -23,9 +23,7 @@ function IonicStore(fileName) { try { this.data = JSON.parse(fs.readFileSync(this.filePath)); - } catch (e) { - throw e; - } + } catch (e) {} // eslint-disable-line no-empty return this; } From 9ead0beb95818bac64bdd4066bf70e57aee1f80a Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Fri, 29 Apr 2016 15:57:36 -0500 Subject: [PATCH 0968/1100] removed mp --- lib/ionic/stats.js | 532 ++------------------------------------------- 1 file changed, 19 insertions(+), 513 deletions(-) diff --git a/lib/ionic/stats.js b/lib/ionic/stats.js index 41d643da75..7fa788c7ec 100644 --- a/lib/ionic/stats.js +++ b/lib/ionic/stats.js @@ -1,522 +1,29 @@ -var MixpanelAPI, crypto, Buffer, http, querystring, util, path, fs, os, IonicConfig; -var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, - __slice = Array.prototype.slice; +var request = require('request'), + IonicConfig = require('./config'); -var path = require('path'), - http = require('http'), - querystring = require('querystring'), - crypto = require('crypto'), - Buffer = require('buffer').Buffer, - util = require('util'), - es = require('event-stream'), - fs = require('fs'), - os = require('os'), - IonicConfig = require('./config'); +var proxy = process.env.PROXY || process.env.http_proxy || null; -/* - Heavily inspired by the original js library copyright Mixpanel, Inc. - (http://mixpanel.com/) - - Copyright (c) 2012 Carl Sverre - - Released under the MIT license. -*/ - -var create_client = function(token, config) { - var metrics = {}; - - if(!token) { - throw new Error("The Mixpanel Client needs a Mixpanel token: `init(token)`"); - } - - metrics.config = { - test: false, - debug: false, - verbose: false - }; - - metrics.token = token; - - /** - send_request(data) - --- - this function sends an async GET request to mixpanel - - data:object the data to send in the request - callback:function(err:Error) callback is called when the request is - finished or an error occurs - */ - metrics.send_request = function(endpoint, data, callback) { - callback = callback || function() {}; - var event_data = new Buffer(JSON.stringify(data)); - var request_data = { - 'data': event_data.toString('base64'), - 'ip': 0, - 'verbose': metrics.config.verbose ? 1 : 0 - }; - - if (endpoint === '/import') { - var key = metrics.config.key; - if (!key) { - throw new Error("The Mixpanel Client needs a Mixpanel api key when importing old events: `init(token, { key: ... })`"); - } - request_data.api_key = key; - } - - var request_options = { - host: 'api.mixpanel.com', - port: 80, - headers: {} - }; - - if (metrics.config.test) { request_data.test = 1; } - - var query = querystring.stringify(request_data); - - request_options.path = [endpoint,"?",query].join(""); - - http.get(request_options, function(res) { - var data = ""; - res.on('data', function(chunk) { - data += chunk; - }); - - res.on('end', function() { - var e; - if(metrics.config.verbose) { - try { - var result = JSON.parse(data); - if(result.status != 1) { - e = new Error("Mixpanel Server Error: " + result.error); - } - } - catch(ex) { - e = new Error("Could not parse response from Mixpanel"); - } - } - else { - e = (data !== '1') ? new Error("Mixpanel Server Error: " + data) : undefined; - } - - callback(e); - }); - }).on('error', function(e) { - if(metrics.config.debug) { - console.log("Got Error: " + e.message); - } - callback(e); - }); - }; - - /** - track(event, properties, callback) - --- - this function sends an event to mixpanel. - - event:string the event name - properties:object additional event properties to send - callback:function(err:Error) callback is called when the request is - finished or an error occurs - */ - metrics.track = function(event, properties, callback) { - if (typeof(properties) === 'function' || !properties) { - callback = properties; - properties = {}; - } - - // if properties.time exists, use import endpoint - var endpoint = (typeof(properties.time) === 'number') ? '/import' : '/track'; - - properties.token = metrics.token; - properties.mp_lib = "node"; - - var data = { - 'event' : event, - 'properties' : properties - }; - - if (metrics.config.debug) { - console.log("Sending the following event to Mixpanel:"); - console.log(data); - } - - metrics.send_request(endpoint, data, callback); +var s = { + track: function(event, uuid, data, callback) { + var data = { + '_event' : event, + '_uuid': uuid, + 'data': data }; - /** - import(event, properties, callback) - --- - This function sends an event to mixpanel using the import - endpoint. The time argument should be either a Date or Number, - and should signify the time the event occurred. - - It is highly recommended that you specify the distinct_id - property for each event you import, otherwise the events will be - tied to the IP address of the sending machine. - - For more information look at: - https://mixpanel.com/docs/api-documentation/importing-events-older-than-31-days - - event:string the event name - time:date|number the time of the event - properties:object additional event properties to send - callback:function(err:Error) callback is called when the request is - finished or an error occurs - */ - metrics.import = function(event, time, properties, callback) { - if (typeof(properties) === 'function' || !properties) { - callback = properties; - properties = {}; - } - - if (time === void 0) { - throw new Error("The import method requires you to specify the time of the event"); - } else if (Object.prototype.toString.call(time) === '[object Date]') { - time = Math.floor(time.getTime() / 1000); - } - - properties.time = time; - - metrics.track(event, properties, callback); - }; - - /** - alias(distinct_id, alias) - --- - This function creates an alias for distinct_id - - For more information look at: - https://mixpanel.com/docs/integration-libraries/using-mixpanel-alias - - distinct_id:string the current identifier - alias:string the future alias - */ - metrics.alias = function(distinct_id, alias, callback) { - var properties = { - distinct_id: distinct_id, - alias: alias - }; - - metrics.track('$create_alias', properties, callback); - }; - - metrics.people = { - /** people.set_once(distinct_id, prop, to, callback) - --- - The same as people.set but in the words of mixpanel: - mixpanel.people.set_once - - " This method allows you to set a user attribute, only if - it is not currently set. It can be called multiple times - safely, so is perfect for storing things like the first date - you saw a user, or the referrer that brought them to your - website for the first time. " - - */ - set_once: function(distinct_id, prop, to, callback) { - var $set = {}, data = {}; - - if (typeof(prop) === 'object') { - callback = to; - $set = prop; - } else { - $set[prop] = to; - } - - this._set(distinct_id, $set, callback, { set_once: true }); - }, - - /** - people.set(distinct_id, prop, to, callback) - --- - set properties on an user record in engage - - usage: - - mixpanel.people.set('bob', 'gender', 'm'); - - mixpanel.people.set('joe', { - 'company': 'acme', - 'plan': 'premium' - }); - */ - set: function(distinct_id, prop, to, callback) { - var $set = {}, data = {}; - - if (typeof(prop) === 'object') { - callback = to; - $set = prop; - } else { - $set[prop] = to; - } - - this._set(distinct_id, $set, callback); - }, - - // used internally by set and set_once - _set: function(distinct_id, $set, callback, options) { - var set_key = (options && options.set_once) ? "$set_once" : "$set"; - - var data = { - '$token': metrics.token, - '$distinct_id': distinct_id - }; - data[set_key] = $set; - - if ('ip' in $set) { - data.$ip = $set.ip; - delete $set.ip; - } - - if ($set.$ignore_time) { - data.$ignore_time = $set.$ignore_time; - delete $set.$ignore_time; - } - - if(metrics.config.debug) { - console.log("Sending the following data to Mixpanel (Engage):"); - console.log(data); - } - - metrics.send_request('/engage', data, callback); - }, - - /** - people.increment(distinct_id, prop, to, callback) - --- - increment/decrement properties on an user record in engage - - usage: - - mixpanel.people.increment('bob', 'page_views', 1); - - // or, for convenience, if you're just incrementing a counter by 1, you can - // simply do - mixpanel.people.increment('bob', 'page_views'); - - // to decrement a counter, pass a negative number - mixpanel.people.increment('bob', 'credits_left', -1); - - // like mixpanel.people.set(), you can increment multiple properties at once: - mixpanel.people.increment('bob', { - counter1: 1, - counter2: 3, - counter3: -2 - }); - */ - increment: function(distinct_id, prop, by, callback) { - var $add = {}; - - if (typeof(prop) === 'object') { - callback = by; - Object.keys(prop).forEach(function(key) { - var val = prop[key]; - - if (isNaN(parseFloat(val))) { - if (metrics.config.debug) { - console.error("Invalid increment value passed to mixpanel.people.increment - must be a number"); - console.error("Passed " + key + ":" + val); - } - return; - } else { - $add[key] = val; - } - }); - } else { - if (!by) { by = 1; } - $add[prop] = by; - } - - var data = { - '$add': $add, - '$token': metrics.token, - '$distinct_id': distinct_id - }; - - if(metrics.config.debug) { - console.log("Sending the following data to Mixpanel (Engage):"); - console.log(data); - } - - metrics.send_request('/engage', data, callback); - }, - - /** - people.track_charge(distinct_id, amount, properties, callback) - --- - Record that you have charged the current user a certain - amount of money. - - usage: - - // charge a user $29.99 - mixpanel.people.track_charge('bob', 29.99); - - // charge a user $19 on the 1st of february - mixpanel.people.track_charge('bob', 19, { '$time': new Date('feb 1 2012') }); - */ - track_charge: function(distinct_id, amount, properties, callback) { - var $append = {}; - - if (!properties) { properties = {}; } - - if (typeof(amount) !== 'number') { - amount = parseFloat(amount); - if (isNaN(amount)) { - console.error("Invalid value passed to mixpanel.people.track_charge - must be a number"); - return; - } - } - - properties.$amount = amount; - - if (properties.hasOwnProperty('$time')) { - var time = properties.$time; - if (Object.prototype.toString.call(time) === '[object Date]') { - properties.$time = time.toISOString(); - } - } - - var data = { - '$append': { '$transactions': properties }, - '$token': metrics.token, - '$distinct_id': distinct_id - }; - - if(metrics.config.debug) { - console.log("Sending the following data to Mixpanel (Engage):"); - console.log(data); - } - - metrics.send_request('/engage', data, callback); - }, - - /** - people.clear_charges(distinct_id, callback) - --- - Clear all the current user's transactions. - - usage: - - mixpanel.people.clear_charges('bob'); - */ - clear_charges: function(distinct_id, callback) { - var data = { - '$set': { '$transactions': [] }, - '$token': metrics.token, - '$distinct_id': distinct_id - }; - - if(metrics.config.debug) { - console.log("Clearing this user's charges:", distinct_id); - } - - metrics.send_request('/engage', data, callback); - }, - - /** - people.delete_user(distinct_id, callback) - --- - delete an user record in engage - - usage: - - mixpanel.people.delete_user('bob'); - */ - delete_user: function(distinct_id, callback) { - var data = { - '$delete': distinct_id, - '$token': metrics.token, - '$distinct_id': distinct_id - }; - - if(metrics.config.debug) { - console.log("Deleting the user from engage:", distinct_id); - } - - metrics.send_request('/engage', data, callback); - }, - - /** - people.unset(distinct_id, prop, callback) - --- - delete a property on an user record in engage - - usage: - - mixpanel.people.unset('bob', 'page_views'); - - mixpanel.people.unset('bob', ['page_views', 'last_login']); - */ - unset: function(distinct_id, prop, callback) { - var $unset = []; - - if (util.isArray(prop)) { - $unset = prop; - } else if (typeof(prop) === 'string') { - $unset = [prop]; - } else { - if (metrics.config.debug) { - console.error("Invalid argument passed to mixpanel.people.unset - must be a string or array"); - console.error("Passed: " + prop); - } - return; - } - - var data = { - '$unset': $unset, - '$token': metrics.token, - '$distinct_id': distinct_id - }; - - if(metrics.config.debug) { - console.log("Sending the following data to Mixpanel (Engage):"); - console.log(data); - } - - metrics.send_request('/engage', data, callback); - } - }; - - /** - set_config(config) - --- - Modifies the mixpanel config - - config:object an object with properties to override in the - mixpanel client config - */ - metrics.set_config = function(config) { - for (var c in config) { - if (config.hasOwnProperty(c)) { - metrics.config[c] = config[c]; - } - } - }; - - if (config) { - metrics.set_config(config); - } - - return metrics; -}; - -// module exporting -/* -module.exports = { - Client: function(token) { - console.warn("The function `Client(token)` is deprecated. It is now called `init(token)`."); - return create_client(token); - }, - init: create_client + request({ + url: 'https://t.ionic.io/event/cli', + method: "POST", + json: data, + proxy: proxy + }, function(err, res, body) { + callback(err, res, body); + }); + } }; -*/ - -var mixpanel = create_client('69f7271aa8f3d43f2e1b6baf698159b7'); var ionicConfig = IonicConfig.load(); - exports.IonicStats = { t: function(additionalData) { try { @@ -618,8 +125,7 @@ exports.IonicStats = { this.createId(); unique_id = ionicConfig.get('ank'); } - d.distinct_id = unique_id; - mixpanel.track(e, d, function(err, data) { + s.track(e, unique_id, d, function(err, data) { }); }, createId: function() { From 6317e76dc8a35af45affd7cfa01a1c955b9c68d5 Mon Sep 17 00:00:00 2001 From: Ignat Ignatov Date: Mon, 2 May 2016 14:57:09 +0200 Subject: [PATCH 0969/1100] fix(share): use app-lib project utility --- lib/ionic/share.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ionic/share.js b/lib/ionic/share.js index 699caccae3..6c9fd33c3f 100644 --- a/lib/ionic/share.js +++ b/lib/ionic/share.js @@ -1,9 +1,9 @@ require('shelljs/global'); -var IonicProject = require('./project'); +var IonicAppLib = require('ionic-app-lib'); +var IonicProject = IonicAppLib.project; var Task = require('./task').Task; var LoginTask = require('./login'); -var IonicAppLib = require('ionic-app-lib'); var Share = IonicAppLib.share; var log = IonicAppLib.logging.logger; var Login = IonicAppLib.login; From db68467dd8204031fe7f8249b731281bfdd9d100 Mon Sep 17 00:00:00 2001 From: Tim Lancina Date: Mon, 2 May 2016 18:20:05 -0500 Subject: [PATCH 0970/1100] chore(): update CHANGELOG --- CHANGELOG.md | 219 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57483baf70..9ea3fdfda0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,222 @@ + +# [2.0.0-beta.25](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.24...v2.0.0-beta.25) (2016-04-20) + + + + +# [2.0.0-beta.24](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.23...v2.0.0-beta.24) (2016-04-04) + + +### Bug Fixes + +* **generate:** use project.get([61ff94f](https://github.com/driftyco/ionic-cli/commit/61ff94f)) +* **start:** don't run start commands if cancelled([021702a](https://github.com/driftyco/ionic-cli/commit/021702a)) + + +### Features + +* remove postinstall script([3ecebb5](https://github.com/driftyco/ionic-cli/commit/3ecebb5)) +* **app:** remove unused app task([ff99d14](https://github.com/driftyco/ionic-cli/commit/ff99d14)) + + +### BREAKING CHANGES + +* app: the 'app' task has been removed + + + + +# [2.0.0-beta.23](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.22...v2.0.0-beta.23) (2016-03-28) + + +### Bug Fixes + +* **cli:** report gulpfile errors([9c53ffc](https://github.com/driftyco/ionic-cli/commit/9c53ffc)) +* **cli:** use github for default gulpfile([86fbce6](https://github.com/driftyco/ionic-cli/commit/86fbce6)) +* **state:** fix state([984f46f](https://github.com/driftyco/ionic-cli/commit/984f46f)) + + +### Features + +* **cli:** install necessary packages for default gulpfile([89f560b](https://github.com/driftyco/ionic-cli/commit/89f560b)) + + + + +# [2.0.0-beta.22](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.21...v2.0.0-beta.22) (2016-03-28) + + + + +# [2.0.0-beta.21](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.20...v2.0.0-beta.21) (2016-03-28) + + +### Bug Fixes + +* **gulp:** check proper hook name([e897dac](https://github.com/driftyco/ionic-cli/commit/e897dac)) +* **hooks:** only warn about missing tasks if they are missing([0ce5642](https://github.com/driftyco/ionic-cli/commit/0ce5642)) + + +### Features + +* **console:** make console info messages cyan([912dceb](https://github.com/driftyco/ionic-cli/commit/912dceb)) +* **hooks:** download default gulpfile([408a660](https://github.com/driftyco/ionic-cli/commit/408a660)) +* **hooks:** print newline after gulp tasks have finished([c8643b7](https://github.com/driftyco/ionic-cli/commit/c8643b7)) +* **start:** add --skip-npm flag([375ad4b](https://github.com/driftyco/ionic-cli/commit/375ad4b)) + + + + +# [2.0.0-beta.20](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.19...v2.0.0-beta.20) (2016-03-28) + + +### Bug Fixes + +* **cordova:** use cordova command for now([f469d47](https://github.com/driftyco/ionic-cli/commit/f469d47)) +* **gulp:** don't error on missing hooks([0e36814](https://github.com/driftyco/ionic-cli/commit/0e36814)) +* **gulp:** don't warn about gulp hooks in v1 projects([a98b785](https://github.com/driftyco/ionic-cli/commit/a98b785)) +* **gulp:** handle gulpfile edge case([83bbab6](https://github.com/driftyco/ionic-cli/commit/83bbab6)) +* **gulp:** let gulp handle its own errors([5695cfa](https://github.com/driftyco/ionic-cli/commit/5695cfa)) +* **hooks:** don't require Gulpfile twice([c17d268](https://github.com/driftyco/ionic-cli/commit/c17d268)) +* **hooks:** only print build warning for v2([3d69f9d](https://github.com/driftyco/ionic-cli/commit/3d69f9d)) + + +### Features + +* **gulp:** run gulp tasks([33fc34a](https://github.com/driftyco/ionic-cli/commit/33fc34a)) +* add console.warn colors([745a259](https://github.com/driftyco/ionic-cli/commit/745a259)) +* **hooks:** don't run gulp if node_modules doesn't exist([52f1a8f](https://github.com/driftyco/ionic-cli/commit/52f1a8f)) +* **setup:** deprecate setup task([a48e7b9](https://github.com/driftyco/ionic-cli/commit/a48e7b9)) + + + + +# [2.0.0-beta.19](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.18...v2.0.0-beta.19) (2016-03-28) + + + + +# [2.0.0-beta.18](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.17...v2.0.0-beta.18) (2016-03-28) + + +### Features + +* **browser:** deprecate browser cmd([96b79ec](https://github.com/driftyco/ionic-cli/commit/96b79ec)) +* **generate:** support all 3 package locations([db3db0e](https://github.com/driftyco/ionic-cli/commit/db3db0e)) + + +### BREAKING CHANGES + +* browser: The browser command is deprecated in favor of using the +Cordova Crosswalk plugin. + + + + +# [2.0.0-beta.17](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.16...v2.0.0-beta.17) (2016-03-28) + + + + +# [2.0.0-beta.16](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.15...v2.0.0-beta.16) (2016-03-28) + + + + +# [2.0.0-beta.15](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.14...v2.0.0-beta.15) (2016-03-28) + + +### Features + +* **cordova:** copy html before building([ef84e59](https://github.com/driftyco/ionic-cli/commit/ef84e59)) + + + + +# [2.0.0-beta.14](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.13...v2.0.0-beta.14) (2016-03-28) + + +### Bug Fixes + +* **bundle:** rebuild native bindings postinstall([dfa3b03](https://github.com/driftyco/ionic-cli/commit/dfa3b03)) + + + + +# [2.0.0-beta.13](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.12...v2.0.0-beta.13) (2016-03-28) + + +### Bug Fixes + +* bundle before build, emulate, run and upload([c8930b8](https://github.com/driftyco/ionic-cli/commit/c8930b8)) +* **run:** fix livereload option for v2 run([cc1cc11](https://github.com/driftyco/ionic-cli/commit/cc1cc11)) + + +### Features + +* run Sass before build commands([d3a1673](https://github.com/driftyco/ionic-cli/commit/d3a1673)) + + + + +# [2.0.0-beta.1 - 2.0.0-beta.12](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.1...v2.0.0-beta.12) (2016-03-28) + + +### Features + +* **v2:** New v2 support([666ae35](https://github.com/driftyco/ionic-cli/commit/666ae35)) + + + + +## [1.7.14](https://github.com/driftyco/ionic-cli/compare/1.7.14...v1.7.14) (2016-02-01) + + +### Bug Fixes + +* add info back in([fec24f7](https://github.com/driftyco/ionic-cli/commit/fec24f7)) + + + + +## [1.7.13](https://github.com/driftyco/ionic-cli/compare/v1.7.12...v1.7.13) (2016-01-20) + + + + +## [1.7.12](https://github.com/driftyco/ionic-cli/compare/v1.7.11...v1.7.12) (2015-12-14) + +* Use bundledDependencies([40294f7])(https://github.com/driftyco/ionic-cli/commit/40294f7) + + + + +## [1.7.11](https://github.com/driftyco/ionic-cli/compare/v1.7.10...v1.7.11) (2015-12-02) + + + + +## [1.7.10](https://github.com/driftyco/ionic-cli/compare/v1.7.9...v1.7.10) (2015-11-17) + + + + +## [1.7.9](https://github.com/driftyco/ionic-cli/compare/v1.7.8...v1.7.9) (2015-11-17) + + +### Bug Fixes + +* **creator:** creator start zip([9ae25e8](https://github.com/driftyco/ionic-cli/commit/9ae25e8)) +* **state:** Pass the proper variable array to the state save plugin command([2c5c19b](https://github.com/driftyco/ionic-cli/commit/2c5c19b)) +* **docs:** Remove the tests starter([ff1997a](https://github.com/driftyco/ionic-cli/commit/ff1997a)) +* **cordova:** Update ionic build to build all installed platforms and ionic platform to list all available platforms([73f95f8](https://github.com/driftyco/ionic-cli/commit/73f95f8)) +* **emulate/run:** Update cordova run method to respect address for ionic emulate --address localhost - it will remove the --address arg as well as the following IP address to use for passing to cordova CLI.([9cf0ae2](https://github.com/driftyco/ionic-cli/commit/9cf0ae2)) + + + + +## [1.7.8](https://github.com/driftyco/ionic-cli/compare/1.7.7...v1.7.8) (2015-11-05) + ### 1.7.7 * `ionic-app-lib` updated to `0.6.3` From 645ded95cac2be34b85fe30a5f499ce3832aee86 Mon Sep 17 00:00:00 2001 From: Tim Lancina Date: Mon, 2 May 2016 20:25:50 -0500 Subject: [PATCH 0971/1100] chore(): update contributors --- package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package.json b/package.json index 8fc1b2eb3f..f22f60a6c0 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,10 @@ { "name": "Tim Lancina", "web": "https://twitter.com/timlancina" + }, + { + "name": "Josh Thomas", + "web": "https://twitter.com/jthoms1" } ], "license": "MIT", From 91548beeddc0bdc58a7b238c48eb26c8d6baac0a Mon Sep 17 00:00:00 2001 From: Tim Lancina Date: Mon, 2 May 2016 20:25:19 -0500 Subject: [PATCH 0972/1100] chore(): add release script --- lib/tasks/bumpversion.js | 57 ------------------------------------- lib/tasks/createDocsJson.js | 55 ----------------------------------- package.json | 14 ++++----- scripts/release.js | 31 ++++++++++++++++++++ 4 files changed, 37 insertions(+), 120 deletions(-) delete mode 100644 lib/tasks/bumpversion.js delete mode 100644 lib/tasks/createDocsJson.js create mode 100644 scripts/release.js diff --git a/lib/tasks/bumpversion.js b/lib/tasks/bumpversion.js deleted file mode 100644 index e8c4cd0aa9..0000000000 --- a/lib/tasks/bumpversion.js +++ /dev/null @@ -1,57 +0,0 @@ -require('colors'); -var path = require('path'); -var semver = require('semver'); -var shelljs = require('shelljs'); -var args = require('optimist').argv; -var fs = require('fs'); -var jsonPath = path.join(__dirname, '../', '../', 'package.json'); -var packageJson = require(jsonPath); -var version = null; -var bumpLevel = null; - -// version = args.version ? args.version : semver.inc(packageJson.version, bumpLevel), - -if (args.level && args.level === 'pre') { - var id = args.identifier || 'beta'; - bumpLevel = [args.level, id].join(''); - version = semver.inc(packageJson.version, args.level, id); -} else { - bumpLevel = args.level || 'patch'; - version = args.version ? args.version : semver.inc(packageJson.version, bumpLevel); -} - -var bumpVersionMessage = ['Bumping from ', packageJson.version, ' by ', bumpLevel, ' resulting in ', version].join(''); - -console.log(bumpVersionMessage.green); - -packageJson.version = version; - -if (args.npmInstall) { - shelljs.rm('-rf', 'node_modules'); - shelljs.exec('npm install'); -} - -shelljs.rm('npm-shrinkwrap.json'); - -fs.writeFileSync(jsonPath, JSON.stringify(packageJson, null, 2)); - -shelljs.exec('npm shrinkwrap'); - -if (args.git) { - console.log('git commands'); - - // shelljs.exec('git add npm-shrinkwrap.json'); - // shelljs.exec(['git commit -m "', bumpVersionMessage, '"'].join('')); - // shelljs.exec(['git tag ', version].join('')); - // shelljs.exec(['git push origin ', version].join('')) -} - -if (args.npmPublishTag) { - console.log('npm publish commands'); - var publishTagCmd = ['npm publish --tag ', version].join(''); - console.log(publishTagCmd); - - // shelljs.exec('npm publish'); -} else if (args.npmPublish) { - console.log('npm publish'); -} diff --git a/lib/tasks/createDocsJson.js b/lib/tasks/createDocsJson.js deleted file mode 100644 index 3132024eff..0000000000 --- a/lib/tasks/createDocsJson.js +++ /dev/null @@ -1,55 +0,0 @@ -var fs = require('fs'); -var path = require('path'); -var cl = console.log; - -function crawlDocsCreateJson() { - var docsJson = { - versions: [], - api: {} - }; - - var docsPath = path.resolve('docs'); - var docsApiPath = path.join(docsPath, 'api'); - - var docsDirs = fs.readdirSync(docsPath); - - docsDirs.forEach(function(doc) { - var isDocVersion = !isNaN(doc[0]); - if (isDocVersion) { - docsJson.versions.push(doc); - } - }); - - var apiDocsDirs = fs.readdirSync(docsApiPath); - - apiDocsDirs.forEach(function(docs) { - console.log('docs:', docs); - if (docs.indexOf('.md') !== -1) { - return; - } - docsJson.api[docs] = { id: docs, docs: [] }; - - var apiDocPath = path.join(docsApiPath, docs); - console.log('apiDocPath:', apiDocPath); - - var apiSubDocs = fs.readdirSync(apiDocPath); - - apiSubDocs.forEach(function(subdoc) { - console.log('subdoc: ', subdoc); - docsJson.api[docs].docs.push(subdoc); - }); - }); - - // cl('json:') - // cl(docsJson) - - // cl(docsJson.api.controller.docs) - // docsJson.api.controller.docs.forEach(function(e) { - // cl(e) - // }) - - cl(JSON.stringify(docsJson)); -} - - -crawlDocsCreateJson(); diff --git a/package.json b/package.json index f22f60a6c0..9620cf99fc 100644 --- a/package.json +++ b/package.json @@ -8,15 +8,11 @@ "ionic": "bin/ionic" }, "scripts": { - "bump": "node lib/tasks/bumpversion", - "beta-bump": "node lib/tasks/bumpversion --level pre --identifier beta", - "lint": "eslint .", - "publish-release": "node lib/tasks/bumpversion --npmPublish", - "publish-tag": "node lib/tasks/bumpversion --level beta --npmPublishTag", - "full-release": "node lib/tasks/bumpversion --npmInstall --git --npmPublish", - "test": "npm run jasmine", + "gh-release": "node scripts/release", "e2e": "jasmine-node --captureExceptions ./e2e", - "jasmine": "jasmine-node --captureExceptions ./spec" + "jasmine": "jasmine-node --captureExceptions ./spec", + "lint": "eslint .", + "test": "npm run jasmine" }, "keywords": [ "ionic", @@ -86,7 +82,9 @@ "unzip": "0.1.9" }, "devDependencies": { + "conventional-changelog-cli": "1.1.1", "eslint": "^2.8.0", + "github": "0.2.4", "jasmine-node": "1.14.5", "rewire": "2.5.1" }, diff --git a/scripts/release.js b/scripts/release.js new file mode 100644 index 0000000000..9ffa999755 --- /dev/null +++ b/scripts/release.js @@ -0,0 +1,31 @@ +var changelog = require('conventional-changelog'); +var GithubApi = require('github'); +var packageJSON = require('../package.json'); +var through = require('through2'); + +var github = new GithubApi({ + version: '3.0.0' +}); + +github.authenticate({ + type: 'oauth', + token: process.env.GH_TOKEN +}); + +return changelog({ + preset: 'angular' +}) +.pipe(through.obj(function(file, enc, cb){ + github.releases.createRelease({ + owner: 'driftyco', + repo: 'ionic-cli', + target_commitish: 'v2', + tag_name: 'v' + packageJSON.version, + name: packageJSON.version, + body: file.toString(), + prerelease: true, + }, function(err, data){ + if (err) console.log('error: ' + err); + console.log(data); + }); +})); From cf3746b20e6f3b47eebcdb5d6cf49b35168ef932 Mon Sep 17 00:00:00 2001 From: Tim Lancina Date: Mon, 2 May 2016 20:49:14 -0500 Subject: [PATCH 0973/1100] chore(): add changelog task --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 9620cf99fc..9fe637f100 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "ionic": "bin/ionic" }, "scripts": { + "changelog": "conventional-changelog -i CHANGELOG.md -s -p angular", "gh-release": "node scripts/release", "e2e": "jasmine-node --captureExceptions ./e2e", "jasmine": "jasmine-node --captureExceptions ./spec", From d54dca4c8a679ea4c3c8f83d286dbc7fcdff6079 Mon Sep 17 00:00:00 2001 From: Tim Lancina Date: Tue, 3 May 2016 10:38:43 -0500 Subject: [PATCH 0974/1100] chore(): fix CHANGELOG dates --- CHANGELOG.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ea3fdfda0..a1202c010b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,12 +43,12 @@ -# [2.0.0-beta.22](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.21...v2.0.0-beta.22) (2016-03-28) +# [2.0.0-beta.22](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.21...v2.0.0-beta.22) (2016-03-22) -# [2.0.0-beta.21](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.20...v2.0.0-beta.21) (2016-03-28) +# [2.0.0-beta.21](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.20...v2.0.0-beta.21) (2016-03-21) ### Bug Fixes @@ -67,7 +67,7 @@ -# [2.0.0-beta.20](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.19...v2.0.0-beta.20) (2016-03-28) +# [2.0.0-beta.20](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.19...v2.0.0-beta.20) (2016-03-17) ### Bug Fixes @@ -91,12 +91,12 @@ -# [2.0.0-beta.19](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.18...v2.0.0-beta.19) (2016-03-28) +# [2.0.0-beta.19](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.18...v2.0.0-beta.19) (2016-03-01) -# [2.0.0-beta.18](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.17...v2.0.0-beta.18) (2016-03-28) +# [2.0.0-beta.18](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.17...v2.0.0-beta.18) (2016-02-29) ### Features @@ -113,17 +113,17 @@ Cordova Crosswalk plugin. -# [2.0.0-beta.17](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.16...v2.0.0-beta.17) (2016-03-28) +# [2.0.0-beta.17](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.16...v2.0.0-beta.17) (2016-01-08) -# [2.0.0-beta.16](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.15...v2.0.0-beta.16) (2016-03-28) +# [2.0.0-beta.16](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.15...v2.0.0-beta.16) (2016-01-07) -# [2.0.0-beta.15](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.14...v2.0.0-beta.15) (2016-03-28) +# [2.0.0-beta.15](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.14...v2.0.0-beta.15) (2015-12-22) ### Features @@ -133,7 +133,7 @@ Cordova Crosswalk plugin. -# [2.0.0-beta.14](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.13...v2.0.0-beta.14) (2016-03-28) +# [2.0.0-beta.14](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.13...v2.0.0-beta.14) (2015-12-22) ### Bug Fixes @@ -143,7 +143,7 @@ Cordova Crosswalk plugin. -# [2.0.0-beta.13](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.12...v2.0.0-beta.13) (2016-03-28) +# [2.0.0-beta.13](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.12...v2.0.0-beta.13) (2015-12-18) ### Bug Fixes @@ -186,7 +186,7 @@ Cordova Crosswalk plugin. ## [1.7.12](https://github.com/driftyco/ionic-cli/compare/v1.7.11...v1.7.12) (2015-12-14) -* Use bundledDependencies([40294f7])(https://github.com/driftyco/ionic-cli/commit/40294f7) +* Use bundledDependencies([40294f7](https://github.com/driftyco/ionic-cli/commit/40294f7)) From dddd047fc5f873719615663647a829938cb97b45 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Fri, 20 May 2016 12:14:37 -0500 Subject: [PATCH 0975/1100] chore(stats): add cli release tag tracking to stats. --- lib/ionic/stats.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/ionic/stats.js b/lib/ionic/stats.js index 7fa788c7ec..f5ea26aca5 100644 --- a/lib/ionic/stats.js +++ b/lib/ionic/stats.js @@ -112,6 +112,11 @@ exports.IonicStats = { statsData.account_id = ionicConfig.get('id'); } catch (e2) {} + var releaseTag = statsData.cli_version.split('-')[1]; + if (releaseTag) { + statsData.cli_release_tag = releaseTag.split('.')[0]; + } + this.mp(cmdName, statsData); //console.log(cmdName, statsData); From 959cab01c6b36aaa97de1124ed2ba97a3caf91f5 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 23 May 2016 09:09:24 -0500 Subject: [PATCH 0976/1100] chore(stats): ensure that beta tags are no longer ignored for mp. --- lib/ionic/stats.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ionic/stats.js b/lib/ionic/stats.js index f5ea26aca5..4509a10bd3 100644 --- a/lib/ionic/stats.js +++ b/lib/ionic/stats.js @@ -101,7 +101,6 @@ exports.IonicStats = { // add which cli version is being used try { statsData.cli_version = require('../../package.json').version; - if(statsData.cli_version.indexOf('beta') > -1) return; } catch(e2) {} try { From 1e37f4fd57a77c84b6fb4e7492937b2bbd54ddea Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 23 May 2016 09:53:01 -0500 Subject: [PATCH 0977/1100] fix(serve): add mp to the ionic serve command. --- lib/ionic/serve.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 41f9e14549..38c7f8044d 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -43,6 +43,7 @@ IonicTask.prototype.run = function(ionic, argv) { options.nogulp = argv.nogulp; + IonicStats.t(); var promise; try { From e4b9393d82cd412e7fdd6cefb4a57fe50b5716af Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Mon, 23 May 2016 15:05:28 -0500 Subject: [PATCH 0978/1100] Copy adjustments --- lib/cli.js | 6 +++--- lib/ionic/start.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index 94a6b2c0f1..81dcf28ede 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -451,9 +451,9 @@ Cli.printNewsUpdates = function printNewsUpdates(skipNewsCheck) { return q.promise; } - process.stdout.write('+---------------------------------------------------------+\n'.green); + process.stdout.write('\n+---------------------------------------------------------+\n'); - var monthMessage = ['+ New Ionic Updates for ', monthNames[d.getMonth()], ' ', d.getFullYear(), '\n+\n'].join('').green; + var monthMessage = ['+ Extra! Extra! Fresh Ionic updates for ', monthNames[d.getMonth()], ' ', d.getFullYear(), '\n+\n'].join('').bold; process.stdout.write(monthMessage); for(var i = 0, j = messagesJson.list.length; i < j; i++) { @@ -461,7 +461,7 @@ Cli.printNewsUpdates = function printNewsUpdates(skipNewsCheck) { var entryMessage = ['+ ', entry.name, '\n', '+ ', entry.action.yellow, '\n+\n'].join(''); process.stdout.write(entryMessage); } - process.stdout.write('+---------------------------------------------------------+\n'.green); + process.stdout.write('+---------------------------------------------------------+\n\n'); } catch(ex) { console.log('ex', ex.stack); q.reject('Error occurred in downloading the CLI messages:', ex) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 5427d5ce41..543270cdad 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -28,7 +28,7 @@ IonicTask.prototype.run = function run(ionic, argv) { } if (argv._[1] == '.') { - console.log('Please name your Ionic project something meaningful other than \'.\''.red); + console.log('Please name your Ionic project something more meaningful than \'.\''.red); return } From 957596cecf029184f097778aef89ebe95072d56b Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 23 May 2016 15:35:05 -0500 Subject: [PATCH 0979/1100] chore(npm): update package.json file to use new ionic-app-lib and to deploy latest to npm. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0bc9fc67e0..8c0e844ea3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.7.14", + "version": "1.7.15", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -68,7 +68,7 @@ "finalhandler": "0.2.0", "form-data": "0.1.4", "gulp": "3.8.8", - "ionic-app-lib": "0.7.0", + "ionic-app-lib": "0.7.2", "moment": "^2.10.6", "ncp": "0.4.2", "npm": "2.1.3", From 6c7a0403d28a85841486120b9066b54a72d179a8 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 1 Jun 2016 15:20:47 -0500 Subject: [PATCH 0980/1100] Cli refactor (#969) * chore(test): add test coverage and ensure circleci runs coveralls. * chore(docs): update badges on readme to include appveyor and npm * refactor(cli.js): changed cli to allow for promises and created acceptance test to use those. * refactor(help): change help command to use new array print mechanism * refactor(task): remove the task parent prototype from all available ionic tasks * refactor(task): removed prototypes from tasks. * refactor(tasks): change ionic task so that their settings are decentralized and easier to maintain * refactor(cordova): split cordova commands off into seperate modules. * refactor(help): move help commands to help task and create a util to house helper functions. * fix(help): update help list so that it is ordered. * refactor(tasks): remove unneeded underscorejs dependency and replace with utils extend. * chore(test): setup tests for the build task. * refactor(task): pass processArguments on to child tasks. * refactor(tests): ensure all tests pass for cli and cordova commands * fix(store): ensure that cli can run even if home directory ionic file does not exist. * chore(tests): added coveralls to the branch and appveyor testing. * chore(tests): add istanbul to npm test and add tests for hooks and lib. * chore(tests): ensure all rewire tests reset themselves. * chore(tests): create tests for help, ionitron, and prompt utils. * chore(tests): add tests for gulp task before and after events. * fix(help): ensure help does not print duplicate lines between tasks. * chore(tests): add tests for table and template utils. * chore(tests): correct tests around stats and ensure stats is tracking cli_release_tag. * chore(tests): add test coverage to stats utility. * chore(tests): ensure cordova utlity functions are covered by tests. * chore(tests): add test coverage to bower util. * fix(stats): ensure stats code works on node 0.12 by using extend with only 2 arguments. * chore(tests): add tests to utility modules. * chore(tests): write tests for config task. * chore(tests): add tests for ionic task add. * chore(tests): add tests for list task. * chore(tests): add tests for add, io, start and state tasks. * chore(tests): add tests to cover info task. * chore(tests): add tests to the upload task. * chore(tests): add tests to the remove task. * chore(tests): add tests to the help task. * chore(tests): add tests for resources task. * chore(tests): add tests for share task. * chore(tests): write tests for login task. * refactor(): replace disableChangePwd with isProjectTask * chore(tests): add tests to cover compile task. * chore(tests): add tests to cover build task. * chore(tests): add tests to cover emulate task. * chore(tests): add tests to cover platform task. * chore(tests): add tests to cover plugin task. * chore(tests): add tests to cover prepare task. * chore(tests): add tests to cover run task. * chore(tests): add tests to cover the cordova util. * test(cli): warning if no node_modules and v2 * test(cli): change test description to match isProjectTask * test(cli): don't runWithGulp if no node_modules and not v2 * feat(cli): remove gulp upgrade help * test(): remove inquirer assertions * test(cli): warn if no gulpfile and cmd requires build step * test(cli): assert specific log message * feat(cli): add gulp functions to Cli object * feat(ionic): catch unhandled promise rejections * refactor(cli): refactor gulp logic * feat(cli): remove unnecessary lookupTask function * chore(cli): add comments, cleanup logs a little * feat(cli): pull out build command test into its own fn * test(cli): add runWithGulp test * test(cli): fix runWithGulp test * feat(cli): improve gulpfile error formatting * refactor(cli): don't run gulp unnecessarily if hook doesn't exist * feat(cli): remove old project upgrade check * fix(cli): remove unnecessary catch * fix(stats): only split version number for release number if version is available. * chore(): update gitignore * refactor(cli): move error handling out of loadGulpfile * test(cli): add loadGulpfile tests * chore(tests): Update address command to remove from serve file and add tests for it. * chore(tests): add more tests to the serve command. --- .gitignore | 4 +- README.md | 11 +- appveyor.yml | 31 + bin/ionic | 4 +- circle.yml | 6 + lib/cli.js | 720 ++++++++-------------- lib/config/commands.js | 79 +++ lib/ionic/add.js | 110 ++-- lib/ionic/address.js | 24 + lib/ionic/bower.js | 63 -- lib/ionic/browser.js | 31 +- lib/ionic/build.js | 67 ++ lib/ionic/compile.js | 31 + lib/ionic/config.js | 57 ++ lib/ionic/cordova.js | 450 -------------- lib/ionic/docs.js | 55 +- lib/ionic/emulate.js | 87 +++ lib/ionic/generate.js | 52 +- lib/ionic/help.js | 58 +- lib/ionic/hooks.js | 28 +- lib/ionic/info.js | 32 +- lib/ionic/io-config.js | 57 -- lib/ionic/{io-init.js => io.js} | 41 +- lib/ionic/ions.js | 60 -- lib/ionic/lib.js | 189 +++--- lib/ionic/link.js | 45 +- lib/ionic/list.js | 50 ++ lib/ionic/login.js | 42 +- lib/ionic/package.js | 62 +- lib/ionic/platform.js | 79 +++ lib/ionic/plugin.js | 78 +++ lib/ionic/prepare.js | 32 + lib/ionic/push.js | 137 +++-- lib/ionic/remove.js | 67 ++ lib/ionic/resources.js | 53 +- lib/ionic/run.js | 87 +++ lib/ionic/security.js | 70 ++- lib/ionic/serve.js | 83 ++- lib/ionic/service.js | 126 ++-- lib/ionic/setup.js | 22 +- lib/ionic/share.js | 38 +- lib/ionic/start.js | 75 ++- lib/ionic/starter-templates.json | 66 -- lib/ionic/state.js | 46 +- lib/ionic/stats.js | 657 -------------------- lib/ionic/task.js | 7 - lib/ionic/upload.js | 33 +- lib/ionic/utils.js | 46 -- lib/tasks/cliTasks.js | 528 ---------------- lib/utils/bower.js | 67 ++ lib/utils/cordova.js | 278 +++++++++ lib/utils/help.js | 243 ++++++++ lib/{ionic => utils}/ionitron.js | 19 +- lib/{ionic/assets => utils}/ionitron.txt | 0 lib/{ionic => utils}/prompt.js | 0 lib/utils/stats.js | 164 +++++ lib/{ionic => utils}/store.js | 2 +- lib/{ionic => utils}/table.js | 0 lib/{ionic => utils}/templates.js | 45 +- package.json | 8 +- spec/cli.spec.js | 748 +++++++++++++++++++---- spec/helpers/ionic.js | 19 + spec/serve.spec.js | 84 --- spec/start.spec.js | 13 - spec/stats.spec.js | 79 --- spec/tasks/add.spec.js | 134 ++++ spec/tasks/address.spec.js | 45 ++ spec/tasks/build.spec.js | 138 +++++ spec/tasks/compile.spec.js | 56 ++ spec/tasks/config.spec.js | 135 ++++ spec/tasks/docs.spec.js | 80 +++ spec/tasks/emulate.spec.js | 193 ++++++ spec/tasks/generate.spec.js | 0 spec/tasks/help.spec.js | 91 +++ spec/tasks/hooks.spec.js | 99 +++ spec/tasks/info.spec.js | 72 +++ spec/tasks/io.spec.js | 115 ++++ spec/tasks/lib.spec.js | 124 ++++ spec/tasks/link.spec.js | 0 spec/tasks/list.spec.js | 95 +++ spec/tasks/login.spec.js | 183 ++++++ spec/tasks/package.spec.js | 0 spec/tasks/platform.spec.js | 105 ++++ spec/tasks/plugin.spec.js | 102 ++++ spec/tasks/prepare.spec.js | 45 ++ spec/tasks/push.spec.js | 0 spec/tasks/remove.spec.js | 135 ++++ spec/tasks/resources.spec.js | 86 +++ spec/tasks/run.spec.js | 190 ++++++ spec/tasks/security.spec.js | 0 spec/tasks/serve.spec.js | 254 ++++++++ spec/tasks/service.spec.js | 0 spec/tasks/setup.spec.js | 0 spec/tasks/share.spec.js | 161 +++++ spec/tasks/start.spec.js | 179 ++++++ spec/tasks/state.spec.js | 154 +++++ spec/tasks/upload.spec.js | 99 +++ spec/utils/bower.spec.js | 174 ++++++ spec/utils/cordova.spec.js | 415 +++++++++++++ spec/utils/help.spec.js | 162 +++++ spec/utils/ionitron.spec.js | 31 + spec/utils/prompt.spec.js | 36 ++ spec/utils/stats.spec.js | 297 +++++++++ spec/utils/store.spec.js | 211 +++++++ spec/utils/table.spec.js | 63 ++ spec/utils/templates.spec.js | 162 +++++ 106 files changed, 7971 insertions(+), 3295 deletions(-) create mode 100644 appveyor.yml create mode 100644 circle.yml create mode 100644 lib/config/commands.js create mode 100644 lib/ionic/address.js delete mode 100644 lib/ionic/bower.js create mode 100644 lib/ionic/build.js create mode 100644 lib/ionic/compile.js create mode 100644 lib/ionic/config.js delete mode 100644 lib/ionic/cordova.js create mode 100644 lib/ionic/emulate.js delete mode 100644 lib/ionic/io-config.js rename lib/ionic/{io-init.js => io.js} (50%) delete mode 100644 lib/ionic/ions.js create mode 100644 lib/ionic/list.js create mode 100644 lib/ionic/platform.js create mode 100644 lib/ionic/plugin.js create mode 100644 lib/ionic/prepare.js create mode 100644 lib/ionic/remove.js create mode 100644 lib/ionic/run.js delete mode 100644 lib/ionic/starter-templates.json delete mode 100644 lib/ionic/stats.js delete mode 100644 lib/ionic/task.js delete mode 100644 lib/ionic/utils.js delete mode 100644 lib/tasks/cliTasks.js create mode 100644 lib/utils/bower.js create mode 100644 lib/utils/cordova.js create mode 100644 lib/utils/help.js rename lib/{ionic => utils}/ionitron.js (88%) rename lib/{ionic/assets => utils}/ionitron.txt (100%) rename lib/{ionic => utils}/prompt.js (100%) create mode 100644 lib/utils/stats.js rename lib/{ionic => utils}/store.js (97%) rename lib/{ionic => utils}/table.js (100%) rename lib/{ionic => utils}/templates.js (62%) create mode 100644 spec/helpers/ionic.js delete mode 100644 spec/serve.spec.js delete mode 100644 spec/start.spec.js delete mode 100644 spec/stats.spec.js create mode 100644 spec/tasks/add.spec.js create mode 100644 spec/tasks/address.spec.js create mode 100644 spec/tasks/build.spec.js create mode 100644 spec/tasks/compile.spec.js create mode 100644 spec/tasks/config.spec.js create mode 100644 spec/tasks/docs.spec.js create mode 100644 spec/tasks/emulate.spec.js create mode 100644 spec/tasks/generate.spec.js create mode 100644 spec/tasks/help.spec.js create mode 100644 spec/tasks/hooks.spec.js create mode 100644 spec/tasks/info.spec.js create mode 100644 spec/tasks/io.spec.js create mode 100644 spec/tasks/lib.spec.js create mode 100644 spec/tasks/link.spec.js create mode 100644 spec/tasks/list.spec.js create mode 100644 spec/tasks/login.spec.js create mode 100644 spec/tasks/package.spec.js create mode 100644 spec/tasks/platform.spec.js create mode 100644 spec/tasks/plugin.spec.js create mode 100644 spec/tasks/prepare.spec.js create mode 100644 spec/tasks/push.spec.js create mode 100644 spec/tasks/remove.spec.js create mode 100644 spec/tasks/resources.spec.js create mode 100644 spec/tasks/run.spec.js create mode 100644 spec/tasks/security.spec.js create mode 100644 spec/tasks/serve.spec.js create mode 100644 spec/tasks/service.spec.js create mode 100644 spec/tasks/setup.spec.js create mode 100644 spec/tasks/share.spec.js create mode 100644 spec/tasks/start.spec.js create mode 100644 spec/tasks/state.spec.js create mode 100644 spec/tasks/upload.spec.js create mode 100644 spec/utils/bower.spec.js create mode 100644 spec/utils/cordova.spec.js create mode 100644 spec/utils/help.spec.js create mode 100644 spec/utils/ionitron.spec.js create mode 100644 spec/utils/prompt.spec.js create mode 100644 spec/utils/stats.spec.js create mode 100644 spec/utils/store.spec.js create mode 100644 spec/utils/table.spec.js create mode 100644 spec/utils/templates.spec.js diff --git a/.gitignore b/.gitignore index 8a6b50dbe4..be0f60da64 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,7 @@ *.cookies node_modules/ .idea/ -coverage/ .vscode/ +coverage/ +.coveralls.yml +npm-debug.log diff --git a/README.md b/README.md index ede0734da3..c922812ee6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -[![Circle CI](https://circleci.com/gh/driftyco/ionic-cli.svg?style=svg)](https://circleci.com/gh/driftyco/ionic-cli) +[![Build Status][circle-badge]][circle-badge-url] +[![Build status][appveyor-badge]][appveyor-badge-url] +[![npm][npm-badge]][npm-badge-url] Ionic-Cli ========= @@ -583,3 +585,10 @@ Usage: List: View all generators: `ionic g --list`. + +[circle-badge]: https://circleci.com/gh/driftyco/ionic-cli.svg?style=shield +[circle-badge-url]: https://circleci.com/gh/driftyco/ionic-cli +[appveyor-badge]: https://ci.appveyor.com/api/projects/status/oqaqa7fdc7y9mma3?svg=true +[appveyor-badge-url]: https://ci.appveyor.com/project/jthoms1/ionic-cli +[npm-badge]: https://img.shields.io/npm/v/ionic.svg +[npm-badge-url]: https://www.npmjs.com/package/ionic diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000000..eac8ee9a94 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,31 @@ +# http://www.appveyor.com/docs/appveyor-yml + +# Test against these versions of Node.js. +environment: + matrix: + - nodejs_version: "0.12" + - nodejs_version: "4.2" + +# Install scripts. (runs after repo cloning) +install: + - git rev-parse HEAD + # Get the latest stable version of Node 0.STABLE.latest + - ps: Install-Product node $env:nodejs_version + # Typical npm stuff. + - npm version + - npm install + +cache: + - '%APPDATA%\npm-cache' + +# Post-install test scripts. +test_script: + # Output useful info for debugging. + - npm version + - cmd: npm run test + +# Don't actually build. +build: off + +# Set build version format here instead of in the admin panel. +version: "{build}" diff --git a/bin/ionic b/bin/ionic index 6734e73f1b..521a82b43f 100755 --- a/bin/ionic +++ b/bin/ionic @@ -10,4 +10,6 @@ process.on('uncaughtException', function(err) { var IonicCli = require('../lib/cli'); -IonicCli.run(process.argv); +IonicCli.run(process.argv).catch(function(err){ + console.error('Caught exception:\n', err.stack, '\n\nMind letting us know? https://github.com/driftyco/ionic-cli/issues\n'); +}); diff --git a/circle.yml b/circle.yml new file mode 100644 index 0000000000..a0d097163d --- /dev/null +++ b/circle.yml @@ -0,0 +1,6 @@ +test: + override: + - nvm use 0.12 && npm test + - nvm use 4.0 && npm test + post: + - npm run coveralls diff --git a/lib/cli.js b/lib/cli.js index fb6cd9de5b..b39089bb65 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -1,311 +1,281 @@ -var Cli = module.exports; +require('colors'); + +var Cli = {}; var IonicAppLib = require('ionic-app-lib'); -var IonicStats = require('./ionic/stats').IonicStats; -var IonicStore = require('./ionic/store').IonicStore; +var IonicStats = require('./utils/stats'); +var IonicStore = require('./utils/store'); var IonicConfig = new IonicStore('ionic.config'); var Info = IonicAppLib.info; var IonicProject = IonicAppLib.project; -var Ionitron = require('./ionic/ionitron'); var optimist = require('optimist'); var path = require('path'); var fs = require('fs'); var settings = require('../package.json'); -var Tasks = require('./tasks/cliTasks'); +var Tasks = require('./config/commands').allCommands; +var orderedListOfTasks = require('./config/commands').orderedListOfCommands; var Utils = IonicAppLib.utils; var Logging = IonicAppLib.logging; var log = Logging.logger; var Q = require('q'); -var semver = require('semver'); -var gutil = require('gulp-util'); -var prettyTime = require('pretty-hrtime'); - -var TASKS = Cli.Tasks = Tasks; +var helpUtil = require('./utils/help'); +Cli.ALL_TASKS = Tasks; Cli.IONIC_DASH = 'https://apps.ionic.io'; Cli.IONIC_API = '/api/v1/'; Cli.PRIVATE_PATH = '.ionic'; +Cli.VERSION = settings.version; // This is where the logger for the app-lib can be configured (or replaced). log.level = 'info'; - -// Cli.dev = function dev() { -// if (settings.version.contains('dev') || settings.version.contains('beta') || settings.version.contains('alpha')) { -// return true; -// } -// return false; -// }; - -// The main entry point for the CLI -// This takes the process.argv array for arguments -// The args passed should be unfiltered. -// From here, we will filter the options/args out -// using optimist. Each command is responsible for -// parsing out the args/options in the way they need. -// This way, we can still test with passing in arguments. +/** + * The main entry point for the CLI + * This takes the process.argv array for arguments + * The args passed should be unfiltered. + * From here, we will filter the options/args out + * using optimist. Each command is responsible for + * parsing out the args/options in the way they need. + * This way, we can still test with passing in arguments. + * + * @method run + * @param {Array} processArgv a list of command line arguments including + * @return {Promise} + */ Cli.run = function run(processArgv) { try { - // First we parse out the args to use them. - // Later, we will fetch the command they are trying to - // execute, grab those options, and reparse the arguments. - var argv = optimist(processArgv.slice(2)).argv; + /* + * First we parse out the args to use them. + * Later, we will fetch the command they are trying to + * execute, grab those options, and reparse the arguments. + */ + var rawCliArguments = processArgv.slice(2); + var argv = optimist(rawCliArguments).argv; + var taskList; Cli.attachErrorHandling(); - Cli.checkLatestVersion(); + Cli.latestVersion = Cli.checkLatestVersion(); process.on('exit', function() { - Cli.printVersionWarning(); + Cli.printVersionWarning(settings.version, Cli.npmVersion); }); - // Before taking any more steps, lets check their - // environment and display any upgrade warnings + /* + * Before taking any more steps, lets check their + * environment and display any upgrade warnings + */ Cli.doRuntimeCheck(settings.version); + /* + * Print version if '--version' or '--v' is an option + * TODO: version should probably also be a command + * and this should just map the option to a command + */ if ((argv.version || argv.v) && !argv._.length) { - return Cli.version(); + return Q.fcall(Cli.version); } + /* + * Print ionitron if '--ionitron' is an option + * TODO: ionitron should probably also be a command + * and this should just map the option to a command + */ if (argv.ionitron) { + var Ionitron = require('./utils/ionitron'); var lang = argv.es ? 'es' : 'en'; - return Ionitron.print(lang); + return Q.fcall(Ionitron.print, lang); } + /* + * Set log level to debug if '--verbose' is an option + * TODO: This might be better to handle the verbose flag + * in each individual command + */ if (argv.verbose) { log.level = 'debug'; } + /* + * Print help if '--help' or '-h' is an option + * TODO: help should probably be a command and option on commands + * $ ionic help start (ALREADY WORKS) + * $ ionic start --help + */ if (argv.help || argv.h) { - return Cli.printHelpLines(); + taskList = Cli.getAllTaskSettings(); + return Q.fcall(helpUtil.printTaskListUsage, taskList, Cli.VERSION); } + /* + * TODO: Change stats-opt-out to be a command that updates config rather + * than an option that is passed. + */ if (argv['stats-opt-out']) { IonicConfig.set('statsOptOut', true); IonicConfig.save(); log.info('Successful stats opt-out'); - return; - } else { - IonicStats.t(); + return Q.fcall(function() { + return 'saved'; + }); } - var taskSetting = Cli.tryBuildingTask(argv); - if (!taskSetting) { - return Cli.printAvailableTasks(); + // Gather stats on the current command execution + IonicStats.t(); + + var taskName = argv._[0]; + var task = Cli.getTaskSettingsByName(taskName); + log.debug('Task:', task); + + // If no task is found then just provide the list of tasks available + if (!task) { + taskList = Cli.getAllTaskSettings(); + return Q.fcall(helpUtil.printTaskListShortUsage, taskList, taskName, Cli.VERSION); } - log.debug('Task setting:', taskSetting); - var booleanOptions = Cli.getBooleanOptionsForTask(taskSetting); - argv = optimist(processArgv.slice(2)).boolean(booleanOptions).argv; - var TaskModule = Cli.lookupTask(taskSetting.module); - var taskInstance = new TaskModule(); - - // this should be tracked by each task - if (!taskSetting.disableChangePwd) { - var root = Utils.cdIonicRoot(); - var project = IonicProject.load(root); - argv.v2 = project && project.get('v2'); - argv.v2 && log.debug('Ionic 2 project.'); - - if (fs.existsSync('node_modules')) { - - // run with gulp hooks - return runWithGulp(argv, taskInstance); - } else if (argv.v2) { + + var booleanOptions = Cli.getListOfBooleanOptions(task.options); + + // Remove all possible task boolean options from the args + argv = optimist(rawCliArguments) + .boolean(booleanOptions) + .argv; + + // No need to do project specific actions, cd to root, etc. + if (!task.isProjectTask) { + return Q.fcall(task.run.bind(task), Cli, argv); + } + + var root = Utils.cdIonicRoot(); + var project = IonicProject.load(root); + argv.v2 = project && project.get('v2'); + + // For v1 projects ignore as this could have been skipped for faster start + // and gulp hooks aren't required + // For v2, print a warning as this is most likely not what they want + if (!fs.existsSync('node_modules')) { + if (argv.v2) { log.warn('WARN: No node_modules directory found, do you need to run npm install?'.yellow); } + log.debug('node_modules directory not found, not running gulp hooks'); + + return Q.fcall(task.run.bind(task), Cli, argv); + } + + try { + var gulpLoaded = Cli.loadGulpfile(); + } catch (e) { + if (e.code === 'MODULE_NOT_FOUND') { + log.info('Uh oh! Looks like you\'re missing a module in your gulpfile:'); + log.error(e.message); + log.info('\nDo you need to run `npm install`?\n'); + process.exit(1); + } + log.error('\nThere is an error in your gulpfile: '.red); + log.error(e.stack + '\n'); + process.exit(1); + } + + if (!gulpLoaded) { + + // warn if no gulpfile and it's a command that requires a build step + // but still run the command with no hooks + if (Cli.isBuildCommand(taskName) && argv.v2) { + log.warn('WARN: No gulpfile found!'); + log.warn('If your app requires a build step, you may want to ensure it runs before ' + taskName + '.\n'); + } + log.debug('No gulpfile found, not running gulp hooks'); + + return Q.fcall(task.run.bind(task), Cli, argv); } - return Q.fcall(taskInstance.run.bind(taskInstance), Cli, argv); + + return Cli.runWithGulp(argv, task); } catch (ex) { return Utils.fail(ex); } }; -function runWithGulp(argv, taskInstance) { +Cli.runWithGulp = function runWithGulp(argv, taskInstance) { var cmdName = argv._[0]; var beforeHook = cmdName + ':before'; var afterHook = cmdName + ':after'; - if (loadGulpfile()) { - log.verbose('Gulpfile found'); + try { + var gulp = require(path.resolve(process.cwd() + '/node_modules/gulp')); + } catch (e) { - try { - var gulp = require(path.resolve(process.cwd() + '/node_modules/gulp')); - } catch (e) { + // Empty gulpfile (or one that doesn't require gulp?), and no gulp + log.error('\nGulpfile detected, but gulp is not installed'.red); + log.error('Do you need to run `npm install`?\n'.red); + return process.exit(1); + } - // Empty gulpfile (or one that doesn't require gulp?), and no gulp - log.error('\nGulpfile detected, but gulp is not installed'.red); - log.error('Do you need to run `npm install`?\n'.red); - process.exit(1); - } - logEvents(gulp, [beforeHook, afterHook]); + // setup gulp logging + Cli.logEvents(gulp, [beforeHook, afterHook]); - if (gulp.tasks[beforeHook]) { - log.info('\nRunning \'' + beforeHook + '\' gulp task before ' + cmdName); - } - return Q.nfcall(gulp.start.bind(gulp), beforeHook).then( - function() { - - // Only some commands return promises, so wrap it - return Q.fcall(taskInstance.run.bind(taskInstance), Cli, argv); - }, - function(e) { // task error, let gulp handle it - - // if there is no hook task, but it's a v2 app and a command that usually needs a hook - if (e.missingTask && /serve|build|run|emulate|upload/.test(cmdName) && argv.v2) { - var taskName = (cmdName === 'serve') ? 'watch' : 'build'; - log.warn(('WARN: No \'' + beforeHook + '\' gulp task found!').yellow); - - // The task exists, but there are no hooks for it - // They have the old config file, so we're safe to yell at them - // With new config, may intentionally not have hooks if running their own build - if (gulp.tasks[taskName] && fs.existsSync('ionic.config.js')) { - log.info(('Your gulpfile contains a \'' + taskName + '\' task already! Add:\n').cyan); - log.info((' gulp.task(\'' + cmdName + ':before\', [\'' + taskName + '\']);\n').cyan); - log.info(('to your gulpfile to have Ionic CLI run \'' + taskName + '\' before ' + cmdName + '.\n').cyan); - } else { - log.warn(('If your app requires a build step, you may want to ensure it runs before ' + - cmdName + '.\n').yellow); - } - } + // if there is no before hook there's no need to run gulp + var beforeHookPromise = Q(true); + if (gulp.tasks[beforeHook]) { + log.info('\nRunning \'' + beforeHook + '\' gulp task before ' + cmdName); + beforeHookPromise = Cli.runGulpHook(gulp, beforeHook); - // Only some commands return promises, so wrap it - return Q.fcall(taskInstance.run.bind(taskInstance), Cli, argv); - } - ).then(function() { + } else if (Cli.isBuildCommand(cmdName) && argv.v2) { + log.warn(('WARN: No \'' + beforeHook + '\' gulp task found!').yellow); + log.warn(('If your app requires a build step, you may want to ensure it runs before ' + + cmdName + '.\n').yellow); + } + + // run beforeHook + return beforeHookPromise + + // run cmd + .then(function() { + return Q.fcall(taskInstance.run.bind(taskInstance), Cli, argv); + }) + + // run afterHook + .then(function() { if (gulp.tasks[afterHook]) { log.info('\nRunning \'' + afterHook + '\' gulp task after ' + cmdName); + + return Cli.runGulpHook(gulp, afterHook); } - return Q.nfcall(gulp.start.bind(gulp), afterHook); }); - } +}; - // No gulpfile - if (/serve|build|run|emulate|upload/.test(cmdName) && argv.v2) { - log.warn('WARN: No gulpfile found!'); - log.warn('If your app requires a build step, you may want to ensure it runs before ' + cmdName + '.\n'); - - if (fs.existsSync('ionic.config.js')) { - var inquirer = require('inquirer'); - var deferred = Q.defer(); - - log.info('Looks like you are using the deprecated ionic.config.js and have no gulpfile!'); - log.info('As of beta.21 the Ionic CLI relies on gulp hooks to build your web assets\n'); - inquirer.prompt([{ - type: 'confirm', - name: 'downloadGulp', - message: 'Would you like to download the default gulpfile?'.cyan - }], - function(answers) { - answers.downloadGulp ? deferred.resolve() : deferred.reject(); - }); - return deferred.promise.then( - function() { - return downloadDefaultGulpfile(cmdName).then( - installGulpfilePackages, - function(err) { - log.error('There was an error downloading the default gulpfile: ' + err); - process.exit(1); - } - ).then( - function() { - log.info('Npm packages installed successfully. Try running \'' + cmdName + '\' again.'); - }, - function() { - log.warn('There was an error installing the packages required by the gulpfile.'); - log.warn('You\'ll need to install the following packages manually: '); - } - ); - }, - function() { - return Q.fcall(taskInstance.run.bind(taskInstance), Cli, argv); - } - ); - } - } +Cli.runGulpHook = function runGulpHook(gulp, hookName) { - return Q.fcall(taskInstance.run.bind(taskInstance), Cli, argv); -} + // swallow errors because we already check to make sure the task exists + // so not a missingTask error, and gulp already reports its own errors + // which we set up with Cli.logEvents + return Q.nfcall(gulp.start.bind(gulp), hookName) + .catch(function() {}); +}; -function loadGulpfile() { +Cli.loadGulpfile = function loadGulpfile() { // TODO add babel support? var names = ['gulpfile.js', 'Gulpfile.js']; for (var i = 0, ii = names.length; i < ii; i += 1) { try { require(path.resolve(process.cwd() + '/' + names[i])); + log.verbose('Gulpfile found'); return true; } catch (e) { - if (e.code === 'MODULE_NOT_FOUND') { - if (e.message.indexOf(names[i]) === -1) { - log.info('Uh oh! Looks like you\'re missing a module in your gulpfile:'); - log.error(e.message); - log.info('\nDo you need to run `npm install`?\n'); - process.exit(1); - } else { + if (e.code === 'MODULE_NOT_FOUND' && e.message.indexOf(names[i]) !== -1) { - // ignore missing gulpfile - continue; - } + // ignore missing gulpfile + continue; } - log.error('There is an error in your gulpfile: '); - log.error(e.stack); - process.exit(1); + throw e; } } return false; -} - -function downloadDefaultGulpfile() { - var https = require('https'); - var fileStream = fs.createWriteStream('gulpfile.js'); - var deferred = Q.defer(); - var project = IonicProject.load(); - var branch = project.get('typescript') ? 'typescript' : 'master'; - var url = 'https://raw.githubusercontent.com/driftyco/ionic2-app-base/' + branch + '/gulpfile.js'; - - log.info('\nDownloading default gulpfile from: ' + url); - - fileStream.on('open', function() { - https.get(url, function(res) { - res.on('error', function(err) { - deferred.reject(err); - }); - - res.pipe(fileStream); - }); - }).on('error', function(err) { - deferred.reject(err); - }).on('finish', function() { - deferred.resolve(); - }); - - return deferred.promise; -} - -function installGulpfilePackages() { - var detective = require('detective'); - var requires = detective(fs.readFileSync('gulpfile.js')); - var deferred = Q.defer(); - - log.info('Successfully downloaded gulpfile.\n'.green); - log.info('Now installing npm packages used by the gulpfile: '); - log.info(' ' + requires.join(' ') + '\n'); - - var npmInstall = require('child_process').spawn('npm', ['install', '--save-dev'].concat(requires)); - npmInstall.stdout.on('data', function(data) { log.debug(data); }); - npmInstall.stderr.on('data', function(data) { log.debug(data); }); - npmInstall.on('error', function(err) { - log.error('Error in gulp npm install: ' + err.stack); - }); - npmInstall.on('exit', function(code) { - code !== 0 ? deferred.reject() : deferred.resolve(); - }); - - return deferred.promise; -} +}; +Cli.logEvents = function logEvents(gulpInst, finalTaskNames) { + var prettyTime = require('pretty-hrtime'); + var gutil = require('gulp-util'); -function logEvents(gulpInst, finalTaskNames) { gulpInst.on('task_start', function(e) { // TODO: batch these @@ -325,7 +295,7 @@ function logEvents(gulpInst, finalTaskNames) { }); gulpInst.on('task_err', function(e) { - var msg = formatGulpError(e); + var msg = Cli.formatGulpError(e); var time = prettyTime(e.hrDuration); gutil.log( '\'' + e.task.cyan + '\'', @@ -342,10 +312,10 @@ function logEvents(gulpInst, finalTaskNames) { // gutil.log('Please check the documentation for proper gulpfile formatting'); // process.exit(1); // }); -} +}; // Format orchestrator errors -function formatGulpError(e) { +Cli.formatGulpError = function formatGulpError(e) { if (!e.err) { return e.message; } @@ -362,63 +332,65 @@ function formatGulpError(e) { // Unknown (string, number, etc.) return new Error(String(e.err)).stack; -} +}; -Cli.getBooleanOptionsForTask = function getBooleanOptionsForTask(task) { - var availableTaskOptions = task.options; - var booleanOptions = []; +Cli.isBuildCommand = function isBuildCommand(cmdName) { + return /serve|build|run|emulate|upload/.test(cmdName); +}; - if (availableTaskOptions) { - for (var key in availableTaskOptions) { - if (typeof availableTaskOptions[key] == 'string') { - continue; - } - var optionWithPipe = key; - var optionsSplit = optionWithPipe.split('|'); - booleanOptions.push(optionsSplit[0].substring(2)); - if (optionsSplit.length === 2) { - booleanOptions.push(optionsSplit[1].substring(1)); - } - } +/** + * Method accepts an object of 'task options' and returns all + * boolean options that are available + * + * @method getListOfBooleanOptions + * @param {Object} taskOptionsObj + * @return {Array} Returns array of all available boolean options + */ +Cli.getListOfBooleanOptions = function getListOfBooleanOptions(taskOptionsObj) { + + if (typeof taskOptionsObj !== 'object' || Object.keys(taskOptionsObj) === 0) { + return []; } - return booleanOptions; + return Object.keys(taskOptionsObj) + .filter(function(key) { + return typeof taskOptionsObj[key] !== 'string'; + }) + .reduce(function(list, key) { + var keyItems = key + .split('|') + .map(function(item) { + return item.replace(/^-{1,2}/, ''); + }); + return list.concat(keyItems); + }, []); }; -Cli.lookupTask = function lookupTask(module) { - try { - var taskModule = require(module).IonicTask; - return taskModule; - } catch (ex) { - throw ex; - } -}; -Cli.printVersionWarning = function printVersionWarning() { - if (settings.version.indexOf('beta') > -1 || settings.version.indexOf('alpha') > -1) { +Cli.printVersionWarning = function printVersionWarning(version, npmVersion) { + if (version.indexOf('beta') > -1 || version.indexOf('alpha') > -1) { return; } - if (Cli.npmVersion && Cli.npmVersion !== settings.version.trim()) { - process.stdout.write('\n------------------------------------\n'.red); - process.stdout.write('Ionic CLI is out of date:\n'.bold.yellow); - process.stdout.write((' * Locally installed version: ' + settings.version + '\n').yellow); - process.stdout.write((' * Latest version: ' + Cli.npmVersion + '\n').yellow); - process.stdout.write((' * https://github.com/driftyco/ionic-cli/blob/master/CHANGELOG.md\n').yellow); - process.stdout.write(' * Run '.yellow + 'npm install -g ionic'.bold + ' to update\n'.yellow); - process.stdout.write('------------------------------------\n\n'.red); - Cli.npmVersion = null; + if (npmVersion && npmVersion !== version.trim()) { + log.warn('\n------------------------------------\n'.red); + log.warn('Ionic CLI is out of date:\n'.bold.yellow); + log.warn((' * Locally installed version: ' + version + '\n').yellow); + log.warn((' * Latest version: ' + npmVersion + '\n').yellow); + log.warn((' * https://github.com/driftyco/ionic-cli/blob/master/CHANGELOG.md\n').yellow); + log.warn(' * Run '.yellow + 'npm install -g ionic'.bold + ' to update\n'.yellow); + log.warn('------------------------------------\n\n'.red); } }; -Cli.checkLatestVersion = function checkLatestVersion() { - Cli.latestVersion = Q.defer(); +Cli.checkLatestVersion = function checkLatestVersion(settingsVersion) { + var resultsDeferred = Q.defer(); try { - if (settings.version.indexOf('beta') > -1 || settings.version.indexOf('alpha') > -1) { + if (settingsVersion.indexOf('beta') > -1 || settingsVersion.indexOf('alpha') > -1) { // don't bother checking if its a beta - Cli.latestVersion.resolve(); + resultsDeferred.resolve(); return; } @@ -426,12 +398,13 @@ Cli.checkLatestVersion = function checkLatestVersion() { if (versionCheck && ((versionCheck + 86400000) > Date.now())) { // we've recently checked for the latest version, so don't bother again - Cli.latestVersion.resolve(); + resultsDeferred.resolve(); return; } var proxy = process.env.PROXY || process.env.http_proxy || null; var request = require('request'); + request({ url: 'http://registry.npmjs.org/ionic/latest', proxy: proxy }, function(err, res, body) { if (err) { return console.log(err); @@ -443,201 +416,46 @@ Cli.checkLatestVersion = function checkLatestVersion() { } catch (e) { console.log(e); } - Cli.latestVersion.resolve(); + resultsDeferred.resolve(); }); } catch (e) { - Cli.latestVersion.resolve(); - } - - return Cli.latestVersion.promise; -}; - -Cli.tryBuildingTask = function tryBuildingTask(argv) { - if (argv._.length === 0) { - return false; - } - var taskName = argv._[0]; - - return Cli.getTaskWithName(taskName); -}; - -Cli.getTaskWithName = function getTaskWithName(name) { - for (var i = 0; i < TASKS.length; i += 1) { - var t = TASKS[i]; - if (t.name === name) { - return t; - } - if (t.alt) { - for (var j = 0; j < t.alt.length; j += 1) { - var alt = t.alt[j]; - if (alt === name) { - return t; - } - } - } - } -}; - -Cli.printIonic = function printIonic() { - function w(s) { - process.stdout.write(s); + resultsDeferred.resolve(); } - w(' _ _ \n'); - w(' (_) (_) \n'); - w(' _ ___ _ __ _ ___ \n'); - w(' | |/ _ \\| \'_ \\| |/ __|\n'); - w(' | | (_) | | | | | (__ \n'); - w(' |_|\\___/|_| |_|_|\\___| CLI v' + settings.version + '\n'); + return resultsDeferred.promise; }; -Cli.printAvailableTasks = function printAvailableTasks() { - Cli.printIonic(); - process.stderr.write('\nUsage: ionic task args\n\n=======================\n\n'); - - if (process.argv.length > 2) { - process.stderr.write((process.argv[2] + ' is not a valid task\n\n').bold.red); - } - process.stderr.write('Available tasks: '.bold); - process.stderr.write('(use --help or -h for more info)\n\n'); +/** + * @method getTaskSettingsByName + * @param {String} taskName task name to look for + * @return {Object} Returns the task settings object that matches + */ +Cli.getTaskSettingsByName = function getTaskSettingsByName(taskName) { + var task; - for (var i = 0; i < TASKS.length; i += 1) { - var task = TASKS[i]; - if (task.summary) { - var name = ' ' + task.name + ' '; - var dots = ''; - while ((name + dots).length < 20) { - dots += '.'; - } - process.stderr.write(name.green.bold + dots.grey + ' ' + task.summary.bold + '\n'); + Object.keys(Cli.ALL_TASKS).every(function(listName) { + if (listName === taskName) { + task = require(Cli.ALL_TASKS[listName]); + return false; } - } + return true; + }); - process.stderr.write('\n'); - Cli.processExit(1); + return task; }; -Cli.printHelpLines = function printHelpLines() { - Cli.printIonic(); - process.stderr.write('\n=======================\n'); - for (var i = 0; i < TASKS.length; i += 1) { - var task = TASKS[i]; - if (task.summary) { - Cli.printUsage(task); - } - } - - process.stderr.write('\n'); - Cli.processExit(1); +/** + * @method getAllTaskSettings + * @return {Array} Returns an array of task settings objects + */ +Cli.getAllTaskSettings = function getAllTaskSettings() { + return orderedListOfTasks.map(function(listName) { + return require(Cli.ALL_TASKS[listName]); + }); }; -Cli.printUsage = function printUsage(d) { - function w(s) { - process.stdout.write(s); - } - - w('\n'); - - var rightColumn = 45; - var dots = ''; - var indent = ''; - var x; - var arg; - - var taskArgs = d.title; - - for (arg in d.args) { - if ({}.hasOwnProperty.call(d.args, arg)) { - taskArgs += ' ' + arg; - } - } - - w(taskArgs.green.bold); - - while ((taskArgs + dots).length < rightColumn + 1) { - dots += '.'; - } - - w(' ' + dots.grey + ' '); - - if (d.summary) { - w(d.summary.bold); - } - - for (arg in d.args) { - if (!d.args[arg]) continue; - - indent = ''; - w('\n'); - while (indent.length < rightColumn) { - indent += ' '; - } - w((indent + ' ' + arg + ' ').bold); - - var argDescs = d.args[arg].split('\n'); - var argIndent = indent + ' '; - - for (x = 0; x < arg.length + 1; x += 1) { - argIndent += ' '; - } - - for (x = 0; x < argDescs.length; x += 1) { - if (x === 0) { - w(argDescs[x].bold); - } else { - w('\n' + argIndent + argDescs[x].bold); - } - } - } - - indent = ''; - while (indent.length < d.name.length + 1) { - indent += ' '; - } - - var optIndent = indent; - while (optIndent.length < rightColumn + 4) { - optIndent += ' '; - } - - for (var opt in d.options) { - if ({}.hasOwnProperty.call(d.options, opt)) { - w('\n'); - dots = ''; - - var optLine = indent + '[' + opt + '] '; - - w(optLine.yellow.bold); - - if (d.options[opt]) { - while ((dots.length + optLine.length - 2) < rightColumn) { - dots += '.'; - } - w(dots.grey + ' '); - - var taskOpt = d.options[opt]; - var optDescs; - - if (typeof taskOpt == 'string') { - optDescs = taskOpt.split('\n'); - } else { - optDescs = taskOpt.title.split('\n'); - } - for (x = 0; x < optDescs.length; x += 1) { - if (x === 0) { - w(optDescs[x].bold); - } else { - w('\n' + optIndent + optDescs[x].bold); - } - } - } - } - - w('\n'); - } -}; Cli.processExit = function processExit(code) { if (Cli.cliNews && Cli.cliNews.promise) { @@ -652,10 +470,12 @@ Cli.processExit = function processExit(code) { } }; + Cli.version = function version() { log.info(settings.version + '\n'); }; + Cli.printNewsUpdates = function printNewsUpdates(skipNewsCheck) { if (typeof skipNewsCheck == 'undefined') { skipNewsCheck = true; @@ -675,6 +495,7 @@ Cli.printNewsUpdates = function printNewsUpdates(skipNewsCheck) { try { var newsId = IonicConfig.get('newsId'); var messagesJson = JSON.parse(html); + if (skipNewsCheck || typeof newsId == 'undefined' || newsId !== messagesJson.id) { IonicConfig.set('newsId', messagesJson.id); IonicConfig.save(); @@ -683,28 +504,28 @@ Cli.printNewsUpdates = function printNewsUpdates(skipNewsCheck) { return q.promise; } - process.stdout.write('+---------------------------------------------------------+\n'); + log.info('+---------------------------------------------------------+\n'); var monthMessage = ['+ Extra! Extra! Ionic Updates for ', monthNames[d.getMonth()], ' ', d.getFullYear(), '\n+\n'].join(''); - process.stdout.write(monthMessage); + log.info(monthMessage); for (var i = 0, j = messagesJson.list.length; i < j; i += 1) { var entry = messagesJson.list[i]; var entryMessage = ['+ ', entry.name, '\n', '+ ', entry.action.blue.bold, '\n+\n'].join(''); - process.stdout.write(entryMessage); + log.info(entryMessage); } - process.stdout.write('+---------------------------------------------------------+\n'.green); + log.info('+---------------------------------------------------------+\n'.green); } catch (ex) { q.reject('Error occurred in downloading the CLI messages:', ex); Utils.fail(ex); } q.resolve(messagesJson); } else { - process.stdout.write('Unable to fetch', err, res.statusCode); + log.error('Unable to fetch', err, res.statusCode); q.reject(res); } }); @@ -719,7 +540,7 @@ Cli.gatherInfo = function gatherInfo() { }; Cli.handleUncaughtExceptions = function handleUncaughtExceptions(err) { - console.log('An uncaught exception occurred and has been reported to Ionic'.error.bold); + log.error('An uncaught exception occurred and has been reported to Ionic'.red.bold); var errorMessage = typeof err === 'string' ? err : err.message; Utils.errorHandler(errorMessage); process.exit(1); @@ -757,8 +578,6 @@ Cli.attachErrorHandling = function attachErrorHandling() { // Backwards compatability for those commands that havent been // converted yet. Cli.fail = function fail(err, taskHelp) { - - // var error = typeof err == 'string' ? new Error(err) : err; Utils.fail(err, taskHelp); }; @@ -767,6 +586,7 @@ Cli.getContentSrc = function getContentSrc() { }; Cli.doRuntimeCheck = function doRuntimeCheck(version) { + var semver = require('semver'); var lastVersionChecked = IonicConfig.get('lastVersionChecked'); var versionHasBeenChecked; @@ -782,3 +602,5 @@ Cli.doRuntimeCheck = function doRuntimeCheck(version) { IonicConfig.save(); } }; + +module.exports = Cli; diff --git a/lib/config/commands.js b/lib/config/commands.js new file mode 100644 index 0000000000..203b4eff41 --- /dev/null +++ b/lib/config/commands.js @@ -0,0 +1,79 @@ +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var path = require('path'); + +var COMMAND_DIRECTORY = 'ionic'; + +/** + * List of shorthands that are available for each command + */ +var shorthands = { + g: 'generate', + ls: 'list', + rm: 'remove', + up: 'upload' +}; + +/** + * List of affordances that are available for each command + * Sometimes we will add misspellings here or if we change a command name + */ +var affordances = { + platforms: 'platform', + plugins: 'plugin', + address: 'serve' +}; + +/** + * List of commands that are available from ionic cli + * Each command as a 1-to-1 mapping to a module with the same name + */ +var orderedListOfCommands = [ + 'start', + 'serve', + 'platform', + 'run', + 'emulate', + 'build', + 'plugin', + 'resources', + 'upload', + 'share', + 'lib', + 'setup', + 'login', + 'io', + 'security', + 'push', + 'package', + 'config', + 'browser', + 'service', + 'add', + 'remove', + 'list', + 'info', + 'help', + 'link', + 'hooks', + 'state', + 'docs' +]; + +var cmdMap = orderedListOfCommands.reduce(function(completeObj, commandName) { + completeObj[commandName] = commandName; + return completeObj; +}, {}); + +var allCommands = extend(extend(extend({}, shorthands), affordances), cmdMap); +allCommands = Object.keys(allCommands).reduce(function(allCmds, commandName) { + allCmds[commandName] = './' + path.join(COMMAND_DIRECTORY, allCommands[commandName]); + return allCmds; +}, {}); + + +module.exports = { + orderedListOfCommands: orderedListOfCommands, + allCommands: allCommands +}; diff --git a/lib/ionic/add.js b/lib/ionic/add.js index 6770fd9464..5073376ce2 100644 --- a/lib/ionic/add.js +++ b/lib/ionic/add.js @@ -1,22 +1,29 @@ -require('shelljs/global'); +'use strict'; -var path = require('path'); -var exec = require('child_process').exec; +require('colors'); -var Task = require('./task').Task; +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var childProcess = require('child_process'); var IonicAppLib = require('ionic-app-lib'); +var appLibUtils = IonicAppLib.utils; var ioLib = IonicAppLib.ioConfig; var log = IonicAppLib.logging.logger; -var bower = require('./bower'); - -function IonicTask() {} - -IonicTask.prototype = new Task(); +var bower = require('../utils/bower'); + +var settings = { + title: 'add', + name: 'add', + summary: 'Add an Ion, bower component, or addon to the project', + args: { + '[name]': 'The name of the ion, bower component, or addon you wish to install' + }, + isProjectTask: true +}; -IonicTask.prototype.installBowerComponent = function installBowerComponent(componentName) { +function installBowerComponent(componentName) { var bowerInstallCommand = 'bower install --save-dev ' + componentName; - var result = exec(bowerInstallCommand); + var result = childProcess.exec(bowerInstallCommand); if (result.code !== 0) { @@ -24,82 +31,41 @@ IonicTask.prototype.installBowerComponent = function installBowerComponent(compo var errorMessage = 'Bower error, check that "'.red.bold + componentName.verbose + '"'.red.bold + ' exists,'.red.bold + '\nor try running "'.red.bold + bowerInstallCommand.verbose + '" for more info.'.red.bold; - this.ionic.fail(errorMessage, 'add'); + appLibUtils.fail(errorMessage, 'add'); } else { log.info('Bower component installed - ' + componentName); } -}; - -IonicTask.prototype.uninstallBowerComponent = function uninstallBowerComponent(componentName) { - var bowerUninstallCommand = 'bower uninstall --save-dev ' + componentName; - - var result = exec(bowerUninstallCommand); - - if (result.code !== 0) { - var errorMessage = 'Failed to find the bower component "'.red.bold + componentName.verbose + - '"'.red.bold + '.\nAre you sure it exists?'.red.bold; - - this.ionic.fail(errorMessage, 'add'); - } else { - var message = 'Bower component removed - ' + componentName; - log.info(message.red); - } -}; - -IonicTask.prototype.listComponents = function listComponents() { - try { - var bowerJson = require(path.join(process.cwd(), 'bower.json')); - log.info('Ions, bower components, or addons installed: '); - for (var bowerCompIndex in bowerJson.devDependencies) { - if ({}.hasOwnProperty.call(bowerJson.devDependencies, bowerCompIndex)) { - log.info(bowerCompIndex.green); - } - } - } catch (ex) { - log.error('This command can only be used when in an Ionic project directory'.red.bold); - } -}; - -// Need to look at bower.json of package just installed and look for any cordova plugins required -// Check the directory in the projects `.bowerrc` file -// Then go to /path/to/bower/components//ionic-plugins.json - 'plugins' -// For each plugins - call 'ionic add plugin ' -IonicTask.prototype.run = function(ionic, argv) { - this.ionic = ionic; - - if (!bower.IonicBower.checkForBower()) { - this.ionic.fail(bower.IonicBower.installMessage, 'add'); +} + +/** + * Need to look at bower.json of package just installed and look for any cordova plugins required + * Check the directory in the projects `.bowerrc` file + * Then go to /path/to/bower/components//ionic-plugins.json - 'plugins' + * For each plugins - call 'ionic add plugin ' + */ +function run(ionic, argv) { + + if (!bower.checkForBower()) { + appLibUtils.fail(bower.installMessage, 'add'); return; } - var action = argv._[0]; var componentName = argv._[1]; var ioSet = false; try { - switch (action) { - case 'add': - ioSet = true; - this.installBowerComponent(componentName); - break; - case 'remove': - case 'rm': - this.uninstallBowerComponent(componentName); - break; - case 'list': - case 'ls': - case '': - this.listComponents(); - break; - } + ioSet = true; + installBowerComponent(componentName); } catch (error) { var errorMessage = error.message ? error.message : error; - this.ionic.fail(errorMessage, 'service'); + appLibUtils.fail(errorMessage, 'service'); } // Inject the component into our index.html, if necessary, and save the app_id ioLib.injectIoComponent(ioSet, componentName); ioLib.warnMissingData(); -}; +} -exports.IonicTask = IonicTask; +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/address.js b/lib/ionic/address.js new file mode 100644 index 0000000000..035049cc78 --- /dev/null +++ b/lib/ionic/address.js @@ -0,0 +1,24 @@ +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var IonicAppLib = require('ionic-app-lib'); +var Serve = IonicAppLib.serve; + +var settings = { + title: 'address', + name: 'address', + isProjectTask: true +}; + +function run(ionic, argv) { // eslint-disable-line no-unused-vars + + // reset any address configs + var ionicConfig = IonicAppLib.config.load(); + ionicConfig.set('ionicServeAddress', null); + ionicConfig.set('platformServeAddress', null); + return Serve.getAddress({ isAddressCmd: true }); +} + +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/bower.js b/lib/ionic/bower.js deleted file mode 100644 index d031f983cb..0000000000 --- a/lib/ionic/bower.js +++ /dev/null @@ -1,63 +0,0 @@ -require('shelljs/global'); -require('colors'); - -var fs = require('fs'); -var path = require('path'); -var exec = require('child_process').exec; - -exports.IonicBower = { - - setIonicVersion: function(newVersion) { - var bowerData = this.getData(); - if (!bowerData.devDependencies) bowerData.devDependencies = {}; - bowerData.devDependencies.ionic = 'driftyco/ionic-bower#' + newVersion; - this.saveData(bowerData); - }, - - setAppName: function(newAppName) { - var bowerData = this.getData(); - bowerData.name = newAppName; - this.saveData(bowerData); - }, - - getData: function() { - var bowerFilePath = path.resolve('bower.json'); - - try { - if (fs.existsSync(bowerFilePath)) { - return require(bowerFilePath); - } - } catch (e) { - throw e; - } - - return { - name: 'HelloIonic', - private: 'true' - }; - }, - - saveData: function(bowerData) { - try { - var bowerFilePath = path.resolve('bower.json'); - fs.writeFileSync(bowerFilePath, JSON.stringify(bowerData, null, 2)); - } catch (e) { - console.log(('Error saving ' + bowerFilePath + ': ' + e).red.bold); - } - }, - - checkForBower: function checkForBower() { - var installed = false; - try { - var result = exec('bower -v', { silent: true }); - if (result.output.indexOf('command not found') === -1 && result.output.indexOf('not recognized') === -1) { - installed = true; - } - } catch (ex) { - throw ex; - } - return installed; - }, - - installMessage: 'You must have bower installed to continue. \nType `npm install -g bower`' -}; diff --git a/lib/ionic/browser.js b/lib/ionic/browser.js index f5a91df996..bb9249dbb5 100644 --- a/lib/ionic/browser.js +++ b/lib/ionic/browser.js @@ -1,16 +1,35 @@ // Deprecated -var Task = require('./task').Task; +'use strict'; -function IonicTask() {} -IonicTask.prototype = new Task(); +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle -IonicTask.prototype.run = function run() { +var settings = { + title: 'browser', + name: 'browser', + summary: 'Add another browser for a platform ' + '(beta)'.yellow, + args: { + '': '"add remove rm info versions upgrade list ls revert"', + '[browser]': 'The browser you wish to add or remove (Crosswalk)' + }, + options: { + '--nosave|-n': { + title: 'Do not save the platforms and plugins to the package.json file', + boolean: true + } + }, + module: './ionic/browser', + isProjectTask: true +}; + +function run() { console.log( 'The browser task has been deprecated.\n' + 'Please use the Cordova Crosswalk plugin instead:\n\n' + 'ionic platform add android\n' + 'ionic plugin add cordova-plugin-crosswalk-webview\n' ); -}; +} -exports.IonicTask = IonicTask; +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/build.js b/lib/ionic/build.js new file mode 100644 index 0000000000..85eb723527 --- /dev/null +++ b/lib/ionic/build.js @@ -0,0 +1,67 @@ +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var os = require('os'); +var Q = require('q'); +var IonicAppLib = require('ionic-app-lib'); +var ConfigXml = IonicAppLib.configXml; +var cordovaUtils = require('../utils/cordova'); + +var settings = { + title: 'build', + name: 'build', + summary: 'Build (prepare + compile) an Ionic project for a given platform.\n', + args: { + '[options]': '', + '': '' + }, + options: { + '--nohooks|-n': { + title: 'Do not add default Ionic hooks for Cordova', + boolean: true + } + }, + isProjectTask: true +}; + +function run(ionic, argv, rawCliArguments) { + var appDirectory = process.cwd(); + var rawArgs = rawCliArguments.slice(0); + var cmdName = settings.name; + + // If platform was not passed then add it to the rawArgs + var runPlatform = argv._[1]; + if (!runPlatform) { + runPlatform = 'ios'; + rawArgs.push(runPlatform); + } + + if (runPlatform === 'ios' && os.platform() !== 'darwin') { + return Q.reject('✗ You cannot run iOS unless you are on Mac OSX.'); + } + + var promiseList = [] + .concat(!cordovaUtils.isPlatformInstalled(runPlatform, appDirectory) ? + cordovaUtils.installPlatform(runPlatform) : + []) + .concat(!cordovaUtils.arePluginsInstalled(appDirectory) ? + cordovaUtils.installPlugins() : + []); + + return Q.all(promiseList).then(function() { + + // ensure the content node was set back to its original + return ConfigXml.setConfigXml(appDirectory, { + resetContent: true, + errorWhenNotFound: false + }); + }) + .then(function() { + var optionList = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawArgs); + return cordovaUtils.execCordovaCommand(optionList); + }); +} + +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/compile.js b/lib/ionic/compile.js new file mode 100644 index 0000000000..b368179e0d --- /dev/null +++ b/lib/ionic/compile.js @@ -0,0 +1,31 @@ +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var IonicAppLib = require('ionic-app-lib'); +var ConfigXml = IonicAppLib.configXml; +var cordovaUtils = require('../utils/cordova'); + +var settings = { + title: 'compile', + name: 'compile', + isProjectTask: true +}; + +function run(ionic, argv, rawCliArguments) { + var appDirectory = process.cwd(); + var rawArgs = rawCliArguments.slice(0); + var cmdName = argv._[0].toLowerCase(); + + // ensure the content node was set back to its original + return ConfigXml.setConfigXml(appDirectory, { + resetContent: true, + errorWhenNotFound: false + }).then(function() { + var optionList = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawArgs); + return cordovaUtils.execCordovaCommand(optionList); + }); +} + +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/config.js b/lib/ionic/config.js new file mode 100644 index 0000000000..c06c35eeab --- /dev/null +++ b/lib/ionic/config.js @@ -0,0 +1,57 @@ +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var IonicAppLib = require('ionic-app-lib'); +var ioLib = IonicAppLib.ioConfig; +var IonicProject = IonicAppLib.project; +var appLibUtils = IonicAppLib.utils; + +var settings = { + title: 'config', + name: 'config', + summary: 'Set configuration variables for your ionic app ' + '(alpha)'.red, + args: { + '': 'set'.yellow + ', ' + 'unset'.yellow + ', ' + 'build'.yellow + ', or ' + 'info'.yellow, + '[key]': 'The key to set', + '[value]': 'The value to set' + }, + isProjectTask: true +}; + +function run(ionic, argv) { + + try { + IonicProject.load(); + } catch (ex) { + return appLibUtils.fail(ex.message); + } + + var set = false; + var key = ''; + var val = ''; + + switch (argv['_'][1]) { + case 'build': + return ioLib.writeIoConfig(false, false, true); + case 'info': + return ioLib.listConfig(); + case 'set': + case 'unset': + if (!argv['_'][2]) { + return appLibUtils.fail("Invalid syntax, use 'ionic config key value'"); + } + set = argv['_'][1] === 'set'; + key = argv['_'][2]; + if (argv['_'][3] && set) { + val = argv['_'][3]; + } + ioLib.writeIoConfig(key, val, set); + break; + default: + return appLibUtils.fail("Invalid command, must use 'set', 'unset', 'build', or 'info'"); + } +} + +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/cordova.js b/lib/ionic/cordova.js deleted file mode 100644 index effdd38b36..0000000000 --- a/lib/ionic/cordova.js +++ /dev/null @@ -1,450 +0,0 @@ -var Task = require('./task').Task; -var fs = require('fs'); -var os = require('os'); -var Q = require('q'); -var path = require('path'); -var exec = require('child_process').exec; -var shelljs = require('shelljs'); -var IonicAppLib = require('ionic-app-lib'); -var IonicResources = IonicAppLib.resources; -var ConfigXml = IonicAppLib.configXml; -var Cordova = IonicAppLib.cordova; -var State = IonicAppLib.state; -var Utils = IonicAppLib.utils; -var log = IonicAppLib.logging.logger; -var Serve = IonicAppLib.serve; -var IonicProject = IonicAppLib.project; - -function IonicTask() {} -IonicTask.prototype = new Task(); - -IonicTask.prototype.run = function(ionic, argv) { - this.ionic = ionic; - - var self = this; - var cmdName = process.argv[2].toLowerCase(); - var appDirectory = process.cwd(); - var q; - - try { - - // Patch - before doing a `ionic run ios`, we need to make sure they have that platform installed. - // if not, try to install it. - if (argv._.indexOf('run') !== -1 || argv._.indexOf('build') !== -1 || argv._.indexOf('emulate') !== -1) { - - if (runPlatform === 'ios' && os.platform() !== 'darwin') { - return log.error('✗ You cannot run iOS unless you are on Mac OSX.'); - } - - var runPlatform = argv._[1] || 'ios'; - var platformInstalled = self.checkIfPlatformInstalled(runPlatform); - var pluginsInstalled = self.checkForPlugins(); - - if (!platformInstalled) { - var notInstalledMsg = [ - '• You\'re trying to build for ', - runPlatform, - ', but don\'t have the platform installed yet.'].join('').yellow; - var installingMsg = ['∆ Installing ', runPlatform, ' for you.'].join(''); - var installedMessage = ['√ Installed platform ', runPlatform].join(''); - var installCmd = 'cordova platform add ' + runPlatform; - - log.info(notInstalledMsg); - log.info(installingMsg); - shelljs.exec(installCmd); - log.info(installedMessage); - } - - if (!pluginsInstalled) { - q = Q(true); - } else { - q = Q(false); - } - } else { - q = Q(false); - } - - this.isLiveReload = ((cmdName === 'run' || cmdName === 'emulate') && - (argv.livereload || argv['live-reload'] || argv.l)); - - this.addIcons = !(argv.noresources || argv.r); - - var addDefaultResources = false; - var isPlatformCmd = argv._.indexOf('platform') !== -1 || argv._.indexOf('platforms') !== -1; - var isAddCmd = argv._.indexOf('add') !== -1; - var isPluginCmd = argv._.indexOf('plugin') !== -1 || argv._.indexOf('plugins') !== -1; - var isRmCmd = argv._.indexOf('rm') !== -1 || argv._.indexOf('remove') !== -1; - - // if (isAddCmd || isRmCmd) { - // ('this this this', isAddCmd, isRmCmd) - // Hooks.setHooksPermission(process.cwd()); - // } - - if (isPlatformCmd && isAddCmd) { - addDefaultResources = true; - } - - return q.then(function(installPlugins) { - if (!installPlugins) { - return; - } - var plugins = [ - 'cordova-plugin-device', - 'cordova-plugin-console', - 'cordova-plugin-whitelist', - 'cordova-plugin-splashscreen', - 'cordova-plugin-statusbar', - 'ionic-plugin-keyboard' - ]; - - plugins.forEach(function(plugin) { - log.info(['Installing ', plugin].join('')); - shelljs.exec('cordova plugin add --save' + plugin); - }); - }) - .then(function() { - if (self.isLiveReload) { - return self.setupLiveReload(argv); - } else { - - // ensure the content node was set back to its original - return ConfigXml.setConfigXml(process.cwd(), { - resetContent: true, - errorWhenNotFound: false - }); - } - }) - .then(function(serveOptions) { - if (self.isLiveReload) { - self.options = serveOptions; - } - if (addDefaultResources && self.addIcons) { - return IonicResources.copyIconFilesIntoResources(appDirectory) - .then(function() { - return IonicResources.addIonicIcons(appDirectory, argv._[2]); - }); - } else { - return false; - } - }) - .then(function() { - return self.runCordova(cmdName, argv); - }) - .then(function(runCode) { - - // We dont want to do anything if the cordova command failed - if (runCode !== 0 || argv.nosave) { - return; - } - var argumentName = argv._[2]; - if (isPlatformCmd && isAddCmd) { - addDefaultResources = true; - log.info('Saving platform to package.json file'); - - return State.savePlatform(process.cwd(), argumentName); - } else if (isPlatformCmd && isRmCmd) { - log.info('Removing platform from package.json file'); - - return State.removePlatform(process.cwd(), argumentName); - } else if (isPluginCmd && isAddCmd) { - var variables; - - log.info('Saving plugin to package.json file'); - if (argv.variable && (typeof argv.variable === 'string')) { - variables = [argv.variable]; - } else { - variables = argv.variable; - } - return State.savePlugin(process.cwd(), argumentName, variables); - } else if (isPluginCmd && isRmCmd) { - log.info('Removing plugin from package.json file'); - return State.removePlugin(process.cwd(), argumentName); - } - }) - .catch(function(ex) { - log.error('Error happened', ex); - log.error(ex.stack); - throw ex; - }); - } catch (ex) { - Utils.fail('An error occurred running a Cordova command:' + ex); - } -}; - -IonicTask.prototype.runCordova = function(cmdName, argv) { - var deferred = Q.defer(); - var self = this; - var cmdArgs = (process.argv.length > 3 ? process.argv.slice(3) : []); - var cmdArg; - var x; - var y; - - // backwards compatibility prior to fully wrapping cordova cmds - if (cmdName === 'platform' && cmdArgs.length > 0) { - - // `ionic platform ` used to actually run `ionic platform add ` - // if a cordova platform cmd isn't the cmd then automatically insert `add` - var hasCordovaCmd = false; - var validCommands = 'add remove rm list ls update up check'.split(' '); - for (x = 0; x < cmdArgs.length; x += 1) { - cmdArg = cmdArgs[x].toLowerCase(); - for (y = 0; y < validCommands.length; y += 1) { - if (cmdArg === validCommands[y]) { - hasCordovaCmd = true; - break; - } - } - } - - if (!hasCordovaCmd) { - cmdArgs.unshift('add'); - } - } - - cmdArgs.unshift(cmdName); - - // clean out any cmds that may confuse cordova - var cleanArgs = []; - var port = argv.port || argv.p || ''; - var liveReloadPort = argv.livereloadport || argv['livereload-port'] || argv.r || ''; - var ignoreCmds = '--livereload -l --consolelogs -c --serverlogs -s --port -p --livereload-port' + - ' -i -r --address'.split(' '); - var isValdCmd; - var foundAddressCmd = false; - - for (x = 0; x < cmdArgs.length; x += 1) { - cmdArg = cmdArgs[x]; - if (port && cmdArg === port) continue; - if (liveReloadPort && cmdArg === liveReloadPort) continue; - isValdCmd = true; - for (y = 0; y < ignoreCmds.length; y += 1) { - if (cmdArg === ignoreCmds[y]) { - isValdCmd = false; - - // if address is passed, the arg after it will need to be taken out as well. - // also note, if they use --address="localhost", this will not happen - if (cmdArg === '--address') { - foundAddressCmd = true; - } - break; - } - } - if (isValdCmd && !foundAddressCmd) { - - // make sure --target= has double quotes around it (process.argv removes them) - if (cmdArg.indexOf('--target=') === 0 && cmdArg.indexOf('"') === -1) { - cmdArg = cmdArg.replace('--target=', '--target="') + '"'; - } - - cleanArgs.push(cmdArg); - } else { - foundAddressCmd = true; - } - } - var cordovaIsInstalled = Utils.cordovaInstalled(); - - log.debug('Cordova is installed:', cordovaIsInstalled); - if (!cordovaIsInstalled) { - log.debug('Executing cordova without CLI: ' + cleanArgs); - - // If no cordova CLI is installed, fall back to local cordova-lib. - // TODO: Add ability to be smart = only use this if cordova CLI is not installed. - // TODO: Enable passing variables with the plugins - // TODO: Enable checking for multiple platforms/plugins/ls - // ex: (multiple plugin ids) ionic plugin add cordova-plugin-camera cordova-plugin-geolocation - // (multiple platforms) ionic platform add ios android - // (listing) ionic plugin ls - // (version) ionic platform add ios@8.1.1 - // (variables) ionic plugin add cordova-plugin-facebook --variable APP_ID=1 --variable APP_NAME="Goofballs" - try { - var promise; - if (cleanArgs[0] === 'run') { - promise = Cordova.runPlatform(process.cwd(), cleanArgs[1]); - } else if (cleanArgs[0] === 'platform') { - if (cleanArgs[1] === 'add') { - promise = Cordova.addPlatform(process.cwd(), cleanArgs[2], true); - } else if (cleanArgs[1] === 'remove') { - promise = Cordova.removePlatform(process.cwd(), cleanArgs[2], true); - } - } else if (cleanArgs[0] === 'plugin') { - if (cleanArgs[1] === 'add') { - promise = Cordova.addPlugin(process.cwd(), cleanArgs[2], null, true); - } else if (cleanArgs[1] === 'remove') { - promise = Cordova.removePlugin(process.cwd(), cleanArgs[2]); - } - } - - return promise.then(function(result) { - log.info('cleanArgs[0]', cleanArgs[0], 'completed'); - log.debug('Result from cordova-lib:', result); - deferred.resolve(result); - }) - .catch(function(ex) { - deferred.reject(ex); - }); - } catch (ex) { - log.error('An Error occurred trying to fall back to Cordova-lib execution:', ex); - throw ex; - } - } - - log.debug('Executing cordova cli: ' + cleanArgs.join(' ')); - var cordovaProcess = exec('cordova ' + cleanArgs.join(' ')); - - cordovaProcess.stdout.on('data', function(data) { - process.stdout.write(data); - }); - - cordovaProcess.stderr.on('data', function(data) { - if (data) { - process.stderr.write(data.toString().bold); - } - }); - - cordovaProcess.on('close', function(code) { - deferred.resolve(code); - }); - - if (self.isLiveReload) { - cordovaProcess.on('exit', function() { - - // Serve.showFinishedServeMessage(self.options); - Serve.printCommandTips(self.options); - setTimeout(function() { - - // set it back to the original src after a few seconds - ConfigXml.setConfigXml(process.cwd(), { - resetContent: true, - errorWhenNotFound: true - }); - - // deferred.resolve(); - }, 5000); - }); - - process.on('exit', function() { - - // verify it was set back - ConfigXml.setConfigXml(process.cwd(), { - resetContent: true, - errorWhenNotFound: false - }); - }); - - var readLine = require('readline'); - if (process.platform === 'win32') { - var rl = readLine.createInterface({ - input: process.stdin, - output: process.stdout - }); - - rl.on('SIGINT', function() { - process.emit('SIGINT'); - }); - } - - process.on('SIGINT', function() { - process.exit(); - }); - } - - return deferred.promise; -}; - - -IonicTask.prototype.setupLiveReload = function(argv) { - var d = Q.defer(); - - log.info('Setup Live Reload'.green.bold); - - var project = null; - - try { - var cwd = process.cwd(); - project = IonicProject.load(cwd); - } catch (ex) { - log.info('Error occured', ex); - return Utils.fail(ex.message); - } - - var options = Serve.loadSettings(argv, project); - - // options.address = '0.0.0.0'; - options.appDirectory = process.cwd(); // called from cli - ionic serve - cwd is this - options.runLivereload = true; - options.launchBrowser = false; - options.launchLab = false; - options.isPlatformServe = true; - - // First ask user for the IP selection - // Check ports not used - // Set up config.xml src url - // run the cordova command - - var promise; - - if (argv.all) { - promise = Q(); - log.info('Defaulting address to 0.0.0.0'); - options.address = '0.0.0.0'; - } else if (argv.address) { - options.address = argv.address; - promise = Q(); - } else { - promise = Serve.getAddress(options); - } - - return promise - .then(function() { - options.devServer = Serve.host(options.address, options.port); - return Serve.checkPorts(true, options.port, options.address, options); - }) - .then(function() { - if (options.runLivereload) { - return Serve.checkPorts(false, options.liveReloadPort, options.address, options); - } - }) - .then(function() { - ConfigXml.setConfigXml(process.cwd(), { - devServer: Serve.host(options.address, options.port) - }).then(function() { - d.resolve(); - }); - return Serve.start(options); - }) - .then(function() { - Serve.showFinishedServeMessage(options); - return options; - }) - .catch(function(error) { - log.info('There was an error serving your Ionic application for run', error); - log.info(error.stack); - throw error; - }); -}; - -IonicTask.prototype.checkIfPlatformInstalled = function checkIfPlatformInstalled(platform) { - platform = platform || 'ios'; - - var platformPath = path.join(process.cwd(), 'platforms', platform); - - try { - fs.statSync(platformPath); - return true; - } catch (ex) { - return false; - } -}; - -IonicTask.prototype.checkForPlugins = function checkForPlugins() { - var pluginPath = path.join(process.cwd(), 'plugins'); - - try { - fs.statSync(pluginPath); - return true; - } catch (ex) { - return false; - } -}; - -exports.IonicTask = IonicTask; diff --git a/lib/ionic/docs.js b/lib/ionic/docs.js index 566a7f6694..d2cb860ea3 100644 --- a/lib/ionic/docs.js +++ b/lib/ionic/docs.js @@ -1,23 +1,27 @@ -var argv = require('optimist').argv; +'use strict'; + var prompt = require('prompt'); -var Task = require('./task').Task; var _ = require('underscore'); +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle var IonicDocs = require('./resources/docs.json'); var log = require('ionic-app-lib').logging.logger; var COLUMN_LIMIT = 3; - -var Docs = module.exports; - -function IonicTask() {} - -IonicTask.prototype = new Task(); +var settings = { + title: 'docs', + name: 'docs', + summary: 'Opens up the documentation for Ionic', + args: { + '': 'the topic to view help documentation for. Use "ls" to view all topics' + }, + isProjectTask: false +}; function sanitizeUrl(url) { return url.replace(/\$/g, '%24'); } -Docs.list = function list() { +function list() { try { var Table = require('cli-table'); var docsList = IonicDocs.api; @@ -46,9 +50,9 @@ Docs.list = function list() { log.error('Error listing docs:', ex); } log.info('Type "ionic docs " to open the help document for that doc.'.blue.bold); -}; +} -Docs.openDefault = function openDefault() { +function openDefault() { var Info = require('ionic-app-lib').info; var envInfo = Info.gatherInfo(); @@ -60,9 +64,9 @@ Docs.openDefault = function openDefault() { log.info('Ionic version:', ionicVersion); } return require('open')(sanitizeUrl(url)); -}; +} -Docs.lookUpCommand = function lookUpCommand(helpDoc) { +function lookUpCommand(helpDoc) { // log.info('Going thru doc commands', helpDoc) // Go through all the different help topics @@ -148,28 +152,27 @@ Docs.lookUpCommand = function lookUpCommand(helpDoc) { log.info('Opening Ionic document:'.green.bold, url.green.bold); return require('open')(url); } -}; - -IonicTask.prototype.run = function run(ionic) { - this.ionic = ionic; +} +function run(ionic, argv) { var docCmd = argv._[1]; - // log.info('docCmd', docCmd); - if (docCmd === 'ls') { - Docs.list(); + list(); } else if (!docCmd) { - // log.info('openDefault') - Docs.openDefault(); + openDefault(); } else { // log.info('look up command', docCmd) // Look up what it was - Docs.lookUpCommand(docCmd); + lookUpCommand(docCmd); } +} -}; - -exports.IonicTask = IonicTask; +module.exports = extend(settings, { + list: list, + openDefault: openDefault, + lookUpCommand: lookUpCommand, + run: run +}); diff --git a/lib/ionic/emulate.js b/lib/ionic/emulate.js new file mode 100644 index 0000000000..c54e0f6981 --- /dev/null +++ b/lib/ionic/emulate.js @@ -0,0 +1,87 @@ +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var os = require('os'); +var Q = require('q'); +var IonicAppLib = require('ionic-app-lib'); +var ConfigXml = IonicAppLib.configXml; +var cordovaUtils = require('../utils/cordova'); + +var cordovaRunEmulateOptions = { + '--livereload|-l': 'Live reload app dev files from the device' + ' (beta)'.yellow, + '--address': 'Use specific address (livereload req.)', + '--port|-p': 'Dev server HTTP port (8100 default, livereload req.)', + '--livereload-port|-r': 'Live Reload port (35729 default, livereload req.)', + '--consolelogs|-c': { + title: 'Print app console logs to Ionic CLI (livereload req.)', + boolean: true + }, + '--serverlogs|-s': { + title: 'Print dev server logs to Ionic CLI (livereload req.)', + boolean: true + }, + '--debug|--release': { + title: '', + boolean: true + }, + '--device|--emulator|--target=FOO': '' +}; + +var settings = { + title: 'emulate', + name: 'emulate', + summary: 'Emulate an Ionic project on a simulator or emulator', + args: { + '[options]': '', + '': '' + }, + options: cordovaRunEmulateOptions, + isProjectTask: true +}; + +function run(ionic, argv, rawCliArguments) { + var appDirectory = process.cwd(); + var rawArgs = rawCliArguments.slice(0); + var cmdName = argv._[0].toLowerCase(); + + var isLiveReload = argv.livereload || argv['live-reload'] || argv.l || false; + + // If platform was not passed then add it to the rawArgs + var runPlatform = argv._[1]; + if (!runPlatform) { + runPlatform = 'ios'; + rawArgs.push(runPlatform); + } + + if (runPlatform === 'ios' && os.platform() !== 'darwin') { + return Q.reject('✗ You cannot run iOS unless you are on Mac OSX.'); + } + + var promiseList = [] + .concat(!cordovaUtils.isPlatformInstalled(runPlatform, appDirectory) ? + cordovaUtils.installPlatform(runPlatform) : + []) + .concat(!cordovaUtils.arePluginsInstalled(appDirectory) ? + cordovaUtils.installPlugins() : + []); + + return Q.all(promiseList).then(function() { + if (isLiveReload) { + return cordovaUtils.setupLiveReload(argv); + } + + // ensure the content node was set back to its original + return ConfigXml.setConfigXml(process.cwd(), { + resetContent: true, + errorWhenNotFound: false + }); + }) + .then(function(serveOptions) { + var optionList = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawArgs); + return cordovaUtils.execCordovaCommand(optionList, isLiveReload, serveOptions); + }); +} + +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/generate.js b/lib/ionic/generate.js index d54a7b8e45..f98d760daa 100644 --- a/lib/ionic/generate.js +++ b/lib/ionic/generate.js @@ -1,31 +1,47 @@ +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle var path = require('path'); -var Task = require('./task').Task; var ionicAppLib = require('ionic-app-lib'); -var Utils = ionicAppLib.utils; var log = ionicAppLib.logging.logger; var Project = ionicAppLib.project; +var fail = ionicAppLib.utils.fail; + +var settings = { + title: 'generate', + alt: ['g'], + name: 'generate', + summary: 'Generate pages and components', + options: { + '--list': { + title: 'List available generators', + boolean: true + }, + '--typescript|--ts': { + boolean: true, + title: '(with --v2 only) Use TypeScript in generation' + } + }, + isProjectTask: true +}; -function IonicTask() {} - -IonicTask.prototype = new Task(); - -IonicTask.prototype.run = function(ionic, argv) { +function run(ionic, argv) { try { if (!argv.list && argv._.length < 3) { // TODO should have a mechanism for printing usage on invalid tasks - Utils.fail('Invalid arguments. Usage: ionic generate '); + fail('Invalid arguments. Usage: ionic generate '); } if (!argv.v2) { - return Utils.fail('Generators are only available for Ionic 2 projects'); + return fail('Generators are only available for Ionic 2 projects'); } var project; try { project = Project.load(process.cwd()); } catch (err) { - return Utils.fail(err); + return fail(err); } var generator = argv._[1]; @@ -48,14 +64,14 @@ IonicTask.prototype.run = function(ionic, argv) { ionicModule.Generate.printAvailableGenerators(); return; } - return Utils.fail(err); + return fail(err); } return promise; } catch (err) { - Utils.fail('There was an error generating your item:', err); + fail('There was an error generating your item:', err); } -}; +} function loadToolingModule() { @@ -70,7 +86,7 @@ function loadToolingModule() { // if this isn't found, that's fine, check for ionic-framework if (err.code !== 'MODULE_NOT_FOUND') { - Utils.fail('Error when requiring ' + toolingPath + ':\n ' + err); + fail('Error when requiring ' + toolingPath + ':\n ' + err); } } @@ -80,7 +96,7 @@ function loadToolingModule() { ionicModule = require(path.join(process.cwd(), 'node_modules', 'ionic-framework', 'tooling')); } catch (err) { if (err.code === 'MODULE_NOT_FOUND') { - Utils.fail('No ionic-angular package found, do you have Ionic installed?'); + fail('No ionic-angular package found, do you have Ionic installed?'); } } } @@ -90,10 +106,12 @@ function loadToolingModule() { try { ionicModule = require(path.join(process.cwd(), 'node_modules', 'ionic-framework')); } catch (err) { - Utils.fail(err); + fail(err); } } return ionicModule; } -exports.IonicTask = IonicTask; +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/help.js b/lib/ionic/help.js index ddc18b699d..9aa180dccd 100644 --- a/lib/ionic/help.js +++ b/lib/ionic/help.js @@ -1,34 +1,36 @@ -var argv = require('optimist').argv; -var Task = require('./task').Task; - -function IonicTask() {} - -IonicTask.prototype = new Task(); - -IonicTask.prototype.run = function(ionic) { - - // var self = this; - - // self.ionic = ionic; - - var command = argv._[1] ? argv._[1] : ''; +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var helpUtil = require('../utils/help'); + +var settings = { + title: 'help', + name: 'help', + summary: 'Provides help for a certain command', + args: { + '[command]': 'The command you desire help with' + }, + isProjectTask: false +}; - var task = ionic.getTaskWithName(command); +function run(ionic, argv) { + var taskName = argv._[1] ? argv._[1] : ''; - if (command === '') { - ionic.printHelpLines(); - return; - } else if (!task) { - console.log('Command not found.'.red.bold); - return; + // If no task name is provided then print all help available + if (taskName === '') { + var taskList = ionic.getAllTaskSettings(); + return helpUtil.printTaskListUsage(taskList, ionic.VERSION); } - ionic.printIonic(); - process.stderr.write('\n=======================\n'); - - ionic.printUsage(task); + // If a task name is provided and is valid then print Usage + var task = ionic.getTaskSettingsByName(taskName); + if (task) { + return helpUtil.printTaskUsage(task, ionic.VERSION); + } - process.stderr.write('\n\n'); -}; + return console.log('Command not found.'.red.bold); +} -exports.IonicTask = IonicTask; +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/hooks.js b/lib/ionic/hooks.js index 01bea3c577..0b5b9f67d1 100644 --- a/lib/ionic/hooks.js +++ b/lib/ionic/hooks.js @@ -1,19 +1,24 @@ -var argv = require('optimist').argv; +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle var shelljs = require('shelljs'); -var Task = require('./task').Task; var IonicAppLib = require('ionic-app-lib'); var log = IonicAppLib.logging.logger; var Hooks = IonicAppLib.hooks; shelljs.config.silent = true; -function IonicTask() {} - -IonicTask.prototype = new Task(); - -IonicTask.prototype.run = function run(ionic) { - this.ionic = ionic; +var settings = { + title: 'hooks', + name: 'hooks', + summary: 'Manage your Ionic Cordova hooks', + args: { + '[add|remove|permissions|perm]': 'Add, remove, or modify permissions on the default Ionic Cordova hooks' + }, + isProjectTask: true +}; +function run(ionic, argv) { var cmd = argv._[1]; try { @@ -34,7 +39,8 @@ IonicTask.prototype.run = function run(ionic) { } catch (ex) { log.error('There was an error running the hooks command: ', ex.stack); } +} -}; - -exports.IonicTask = IonicTask; +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/info.js b/lib/ionic/info.js index 6ebcde1a98..db3692644f 100644 --- a/lib/ionic/info.js +++ b/lib/ionic/info.js @@ -1,15 +1,19 @@ -var path = require('path'); -var Task = require('./task').Task; -var ionicAppLib = require('ionic-app-lib'); -var Info = ionicAppLib.info; - -function IonicTask() {} - -IonicTask.prototype = new Task(); +'use strict'; -IonicTask.prototype.run = function(ionic) { - this.ionic = ionic; +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var path = require('path'); +var IonicAppLib = require('ionic-app-lib'); +var Info = IonicAppLib.info; +var appLibUtils = IonicAppLib.utils; + +var settings = { + title: 'info', + name: 'info', + summary: 'List information about the users runtime environment', + isProjectTask: false +}; +function run() { try { var info = Info.gatherInfo(); @@ -21,8 +25,10 @@ IonicTask.prototype.run = function(ionic) { Info.checkRuntime(); } catch (ex) { - this.ionic.fail('There was an error retrieving your environment information:', ex); + appLibUtils.fail('There was an error retrieving your environment information:', ex); } -}; +} -exports.IonicTask = IonicTask; +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/io-config.js b/lib/ionic/io-config.js deleted file mode 100644 index 4de448bfc1..0000000000 --- a/lib/ionic/io-config.js +++ /dev/null @@ -1,57 +0,0 @@ -var IonicAppLib = require('ionic-app-lib'); -var ioLib = IonicAppLib.ioConfig; -var argv = require('optimist').argv; -var IonicProject = IonicAppLib.project; -var Task = require('./task').Task; - -function IonicTask() {} - -IonicTask.prototype = new Task(); - -IonicTask.prototype.run = function(ionic) { - var self = this; - self.ionic = ionic; - - self.project = null; - - try { - self.project = IonicProject.load(); - } catch (ex) { - self.ionic.fail(ex.message); - return; - } - - var set = false; - var write = false; - var key = ''; - var val = ''; - - if (argv['_'][1] === 'set' || argv['_'][1] === 'unset' || argv['_'][1] === 'build' || argv['_'][1] === 'info') { - if (argv['_'][1] === 'build') { - write = true; - ioLib.writeIoConfig(false, false, true); - } else if (argv['_'][1] === 'info') { - write = true; - ioLib.listConfig(); - } else { - if (argv['_'][1] === 'set') { - set = true; - } - if (argv['_'][2]) { - key = argv['_'][2]; - if (argv['_'][3] && set) { - val = argv['_'][3]; - } - } else { - self.ionic.fail("Invalid syntax, use 'ionic config key value'"); - } - } - } else { - self.ionic.fail("Invalid command, must use 'set', 'unset', 'build', or 'info'"); - } - if (!write) { - ioLib.writeIoConfig(key, val, set); - } -}; - -exports.IonicTask = IonicTask; diff --git a/lib/ionic/io-init.js b/lib/ionic/io.js similarity index 50% rename from lib/ionic/io-init.js rename to lib/ionic/io.js index 7fe31ee03b..32510d3fc7 100644 --- a/lib/ionic/io-init.js +++ b/lib/ionic/io.js @@ -1,31 +1,34 @@ +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle var IonicAppLib = require('ionic-app-lib'); var ioLib = IonicAppLib.ioConfig; -var argv = require('optimist').argv; var IonicProject = IonicAppLib.project; var log = IonicAppLib.logging.logger; -var Task = require('./task').Task; +var appLibUtils = IonicAppLib.utils; var Login = IonicAppLib.login; var LoginTask = require('./login'); -var Utils = require('./utils'); - -function IonicTask() {} - -IonicTask.prototype = new Task(); -IonicTask.prototype.run = function(ionic) { - var self = this; - self.ionic = ionic; +var settings = { + title: 'io', + name: 'io', + summary: 'Integrate your app with the ionic.io platform services ' + '(alpha)'.red, + args: { + '': 'init'.yellow + }, + isProjectTask: true +}; - self.project = null; +function run(ionic, argv) { try { - self.project = IonicProject.load(); + IonicProject.load(); } catch (ex) { - self.ionic.fail(ex.message); + appLibUtils.fail(ex.message); return; } - Login.retrieveLogin() + return Login.retrieveLogin() .then(function(jar) { if (!jar) { log.info('No previous login existed. Attempting to log in now.'); @@ -37,12 +40,14 @@ IonicTask.prototype.run = function(ionic) { if (argv['_'][1] === 'init') { ioLib.initIoPlatform(process.cwd(), jar); } else { - self.ionic.fail('Invalid command'); + appLibUtils.fail('Invalid command'); } }) .catch(function(ex) { - Utils.fail(ex); + return appLibUtils.fail(ex); }); -}; +} -exports.IonicTask = IonicTask; +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/ions.js b/lib/ionic/ions.js deleted file mode 100644 index d04c4eb4d1..0000000000 --- a/lib/ionic/ions.js +++ /dev/null @@ -1,60 +0,0 @@ -var log = require('ionic-app-lib').logging.logger; -var Task = require('./task').Task; -var _ = require('underscore'); - -var ions = [ - { - ion: 'ionic-ion-header-shrink', - name: 'Header Shrink', - description: 'A shrinking header effect like Facebook\'s' - }, - { - ion: 'ionic-ion-drawer', - name: 'Android Drawer', - description: 'Android-style drawer menu' - }, - { - ion: 'ionic-ion-ios-buttons', - name: 'iOS Rounded Buttons', - description: 'iOS "Squircle" style icons' - }, - { - ion: 'ionic-ion-swipe-cards', - name: 'Swipeable Cards', - description: 'Swiping interaction as seen in Jelly' - }, - { - ion: 'ionic-ion-tinder-cards', - name: 'Tinder Cards', - description: 'Tinder style card swiping interaction' - } -]; - -function IonicTask() {} - -IonicTask.prototype = new Task(); - -IonicTask.prototype.run = function() { - log.info('Ionic Ions - ions power your app to be awesome.'); - log.info('A curated collection of useful addons, components, and ux interactions for extending ionic.'); - log.info('\n'); - - _.each(ions, function(ion) { - - var rightColumn = 20; - var dots = ''; - var startStr = ion.name; - - - while ((startStr + dots).length < rightColumn + 1) { - dots += '.'; - } - - // Header Shrink ... A shrinking header effect like Facebook\'s .. - var installStr = ['\'ionic add ', ion.ion, '\''].join(''); - log.info(ion.name, dots, installStr); - log.info(ion.description, '\n'); - }); -}; - -exports.IonicTask = IonicTask; diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index 68007446f6..8e9c015c7e 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -1,5 +1,8 @@ +'use strict'; + require('colors'); +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle var fs = require('fs'); var path = require('path'); var shell = require('shelljs'); @@ -7,76 +10,91 @@ var request = require('request'); var unzip = require('unzip'); var argv = require('optimist').argv; var prompt = require('prompt'); +var IonicAppLib = require('ionic-app-lib'); +var utils = IonicAppLib.utils; var exec = require('child_process').exec; var Q = require('q'); var log = require('ionic-app-lib').logging.logger; -var Task = require('./task').Task; - -function IonicTask() {} -IonicTask.prototype = new Task(); +var settings = { + title: 'lib', + name: 'lib', + summary: 'Gets Ionic library version or updates the Ionic library', + args: { + '[options]': '', + '[update]': 'Updates the Ionic Framework in www/lib/ionic' + }, + options: { + '--version|-v': 'Specific Ionic version\nOtherwise it defaults to the latest version' + }, + isProjectTask: true +}; -IonicTask.prototype.run = function(ionic) { - var self = this; - self.local = {}; - self.versionData = {}; - self.usesBower = false; +function run(ionic, argv) { if (!fs.existsSync(path.resolve('www'))) { - return ionic.fail('"www" directory cannot be found. Make sure the working directory ' + + return utils.fail('"www" directory cannot be found. Make sure the working directory ' + 'is at the top level of an Ionic project.', 'lib'); } - this.loadVersionData(); + // Gather version data from version.json or bower.json files + var data = loadVersionData(); - if (argv._.length > 1 && (argv._[1].toLowerCase() === 'update' || argv._[1].toLowerCase() === 'up')) { - this.updateLibVersion(); - } else { - - // just version info - this.printLibVersions(); + // If this is an update command then do an update and exit + if (argv._.length > 1 && + (argv._[1].toLowerCase() === 'update' || argv._[1].toLowerCase() === 'up')) { + return updateLibVersion(data); } -}; + // Otherwise lets print the version info + return printLibVersions(data); +} -IonicTask.prototype.loadVersionData = function() { - this.versionFilePath = path.resolve('www/lib/ionic/version.json'); +function loadVersionData() { - if (!fs.existsSync(this.versionFilePath)) { - this.versionFilePath = path.resolve('www/lib/ionic/bower.json'); - this.usesBower = fs.existsSync(this.versionFilePath); + var versionFilePath = path.resolve('www/lib/ionic/version.json'); + var usesBower = false; + var local = {}; + + if (!fs.existsSync(versionFilePath)) { + versionFilePath = path.resolve('www/lib/ionic/bower.json'); + usesBower = fs.existsSync(versionFilePath); } try { - this.local = require(this.versionFilePath); + local = JSON.parse(fs.readFileSync(versionFilePath)); } catch (e) { log.error('Unable to load ionic lib version information'); } -}; + return { + versionFilePath: versionFilePath, + usesBower: usesBower, + local: local + }; +} -IonicTask.prototype.printLibVersions = function() { - var self = this; - log.info('Local Ionic version: '.bold.green + this.local.version + ' (' + this.versionFilePath + ')'); +function printLibVersions(data) { - this.getVersionData('latest').then(function() { - log.info('Latest Ionic version: '.bold.green + self.versionData.version_number + - ' (released ' + self.versionData.release_date + ')'); + log.info('Local Ionic version: '.bold.green + data.local.version + ' (' + data.versionFilePath + ')'); - if (self.local.version !== self.versionData.version_number) { + getVersionData('latest').then(function(versionData) { + log.info('Latest Ionic version: '.bold.green + versionData.version_number + + ' (released ' + versionData.release_date + ')'); + + if (data.local.version !== versionData.version_number) { log.info(' * Local version is out of date'.yellow); } else { log.info(' * Local version up to date'.green); } }); -}; +} -IonicTask.prototype.getVersionData = function(version) { +function getVersionData(data, version) { var q = Q.defer(); - var self = this; var url = 'http://code.ionicframework.com'; if (version === 'latest') { @@ -103,12 +121,12 @@ IonicTask.prototype.getVersionData = function(version) { q.reject(); return; } - self.versionData = JSON.parse(body); - self.versionData['version_number'] = self.versionData.version_number || self.versionData.version; - self.versionData['version_codename'] = self.versionData.version_codename || self.versionData.codename; - self.versionData['release_date'] = self.versionData.release_date || self.versionData.date; + var versionData = JSON.parse(body); + versionData['version_number'] = versionData.version_number || versionData.version; + versionData['version_codename'] = versionData.version_codename || versionData.codename; + versionData['release_date'] = versionData.release_date || versionData.date; - q.resolve(); + q.resolve(versionData); } catch (e) { log.error('Error loading ' + version.bold + ' version information'); q.reject(); @@ -116,13 +134,12 @@ IonicTask.prototype.getVersionData = function(version) { }); return q.promise; -}; +} -IonicTask.prototype.updateLibVersion = function() { - var self = this; +function updateLibVersion(data) { - if (self.usesBower) { + if (data.usesBower) { var bowerCmd = exec('bower update ionic'); bowerCmd.stdout.on('data', function(data) { @@ -162,15 +179,13 @@ IonicTask.prototype.updateLibVersion = function() { var areYouSure = promptResult.areYouSure.toLowerCase().trim(); if (areYouSure === 'yes' || areYouSure === 'y') { - self.getLatest(); + getLatest(data); } }); -}; - +} -IonicTask.prototype.getLatest = function() { - var self = this; +function getLatest() { var dirPath = path.resolve('www/lib/'); if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath); @@ -186,30 +201,27 @@ IonicTask.prototype.getLatest = function() { version = 'latest'; } - this.getVersionData(version).then(function() { + getVersionData(version).then(function(versionData) { if (version === 'latest') { - log.info('Latest version: '.bold.green + self.versionData.version_number + - ' (released ' + self.versionData.release_date + ')'); + log.info('Latest version: '.bold.green + versionData.version_number + + ' (released ' + versionData.release_date + ')'); } else { - log.info('Version: '.bold.green + self.versionData.version_number + - ' (released ' + self.versionData.release_date + ')'); + log.info('Version: '.bold.green + versionData.version_number + + ' (released ' + versionData.release_date + ')'); } - self.downloadZip(self.versionData.version_number); + downloadZip(versionData.version_number); }); +} -}; - - -IonicTask.prototype.downloadZip = function(version) { - var self = this; +function downloadZip(data, version) { var archivePath = 'https://github.com/driftyco/ionic-bower/archive/v' + version + '.zip'; - self.tmpExtractPath = path.resolve('www/lib/.ionic'); - self.tmpZipPath = path.join(self.tmpExtractPath, 'ionic.zip'); + var tmpExtractPath = path.resolve('www/lib/.ionic'); + var tmpZipPath = path.join(data.tmpExtractPath, 'ionic.zip'); - if (!fs.existsSync(self.tmpExtractPath)) { - fs.mkdirSync(self.tmpExtractPath); + if (!fs.existsSync(data.tmpExtractPath)) { + fs.mkdirSync(data.tmpExtractPath); } log.info('Downloading: '.green.bold + archivePath); @@ -229,8 +241,8 @@ IonicTask.prototype.downloadZip = function(version) { return; } try { - fs.writeFileSync(self.tmpZipPath, body); - self.updateFiles(); + fs.writeFileSync(tmpZipPath, body); + updateFiles(data, tmpZipPath, tmpExtractPath); } catch (e) { log.error(e); } @@ -254,40 +266,38 @@ IonicTask.prototype.downloadZip = function(version) { }).on('error', function(err) { try { - fs.unlink(self.tmpZipPath); + fs.unlink(tmpZipPath); } catch (e) { log.error(e); } log.error(err); }); - -}; +} -IonicTask.prototype.updateFiles = function() { - var self = this; +function updateFiles(data, tmpZipPath, tmpExtractPath) { var ionicLibDir = path.resolve('www/lib/ionic'); try { - var readStream = fs.createReadStream(self.tmpZipPath); + var readStream = fs.createReadStream(tmpZipPath); readStream.on('error', function(err) { log.debug('updateFiles readStream error: ' + err); }); - var writeStream = unzip.Extract({ path: self.tmpExtractPath }); // eslint-disable-line new-cap + var writeStream = unzip.Extract({ path: tmpExtractPath }); // eslint-disable-line new-cap writeStream.on('close', function() { try { - shell.rm('-rf', self.tmpZipPath); + shell.rm('-rf', tmpZipPath); } catch (e) { log.error(e); } try { - var dir = fs.readdirSync(self.tmpExtractPath); + var dir = fs.readdirSync(tmpExtractPath); var copyFrom; for (var x = 0; x < dir.length; x += 1) { if (dir[x].indexOf('ionic') === 0) { - copyFrom = path.join(self.tmpExtractPath, dir[x], '*'); + copyFrom = path.join(tmpExtractPath, dir[x], '*'); } } if (!copyFrom) { @@ -298,7 +308,7 @@ IonicTask.prototype.updateFiles = function() { shell.cp('-Rf', copyFrom, ionicLibDir); try { - shell.rm('-rf', self.tmpExtractPath); + shell.rm('-rf', tmpExtractPath); } catch (e) { log.error(e); } @@ -315,12 +325,12 @@ IonicTask.prototype.updateFiles = function() { log.error(e); } - log.info('Ionic version updated to: '.bold.green + self.versionData.version_number.bold); + log.info('Ionic version updated to: '.bold.green + data.versionData.version_number.bold); - self.writeVersionData(); + writeVersionData(data); - var ionicBower = require('./bower').IonicBower; - ionicBower.setIonicVersion(self.versionData.version_number); + var ionicBower = require('../utils/bower').IonicBower; + ionicBower.setIonicVersion(data.versionData.version_number); } catch (e) { log.error('updateFiles Error: ' + e); @@ -334,15 +344,14 @@ IonicTask.prototype.updateFiles = function() { } catch (e) { log.error('updateFiles Error: ' + e); } +} -}; - -IonicTask.prototype.writeVersionData = function() { +function writeVersionData(data) { try { var versionData = { - version: this.versionData.version_number || this.versionData.version, - codename: this.versionData.version_codename || this.versionData.codename, - date: this.versionData.release_date || this.versionData.date + version: data.versionData.version_number || data.versionData.version, + codename: data.versionData.version_codename || data.versionData.codename, + date: data.versionData.release_date || data.versionData.date }; fs.writeFileSync(path.resolve('www/lib/ionic/version.json'), @@ -351,6 +360,8 @@ IonicTask.prototype.writeVersionData = function() { } catch (e) { log.error('Error writing version data: ' + e); } -}; +} -exports.IonicTask = IonicTask; +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/link.js b/lib/ionic/link.js index 57796d86b1..31f92016b0 100644 --- a/lib/ionic/link.js +++ b/lib/ionic/link.js @@ -1,18 +1,30 @@ +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle var fs = require('fs'); var path = require('path'); -var argv = require('optimist').argv; var IonicAppLib = require('ionic-app-lib'); var log = IonicAppLib.logging.logger; +var fail = IonicAppLib.utils.fail; var IonicProject = IonicAppLib.project; -var Task = require('./task').Task; - -function IonicTask() {} - -IonicTask.prototype = new Task(); -IonicTask.prototype.run = function run(ionic) { - this.ionic = ionic; +var settings = { + title: 'link', + name: 'link', + summary: 'Sets your Ionic App ID for your project', + args: { + '[appId]': 'The app ID you wish to set for this project' + }, + options: { + '--reset|-r': { + title: 'This will reset the Ionic App ID', + boolean: true + } + }, + isProjectTask: true +}; +function run(ionic, argv) { var ionicProjectFile = path.resolve('.', 'ionic.project'); if (!fs.existsSync(ionicProjectFile)) { @@ -26,15 +38,15 @@ IonicTask.prototype.run = function run(ionic) { } if (argv.reset || argv.r) { - this.setAppId(''); + setAppId(ionic, ''); } else { - this.setAppId(argv._[1]); + setAppId(ionic, argv._[1]); } log.info('Your Ionic App ID was set'.green); -}; +} -IonicTask.prototype.setAppId = function setAppId(appId) { +function setAppId(ionic, appId) { var project = null; try { @@ -42,9 +54,12 @@ IonicTask.prototype.setAppId = function setAppId(appId) { project.set('app_id', appId); project.save(); } catch (ex) { - this.ionic.fail(ex.message); + fail(ex.message); return; } -}; +} -exports.IonicTask = IonicTask; +module.exports = extend(settings, { + run: run, + setAppId: setAppId +}); diff --git a/lib/ionic/list.js b/lib/ionic/list.js new file mode 100644 index 0000000000..dd84ccc511 --- /dev/null +++ b/lib/ionic/list.js @@ -0,0 +1,50 @@ +'use strict'; + +require('colors'); + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var path = require('path'); +var IonicAppLib = require('ionic-app-lib'); +var appLibUtils = IonicAppLib.utils; +var log = IonicAppLib.logging.logger; +var fs = require('fs'); + +var settings = { + title: 'list', + name: 'list', + summary: 'List Ions, bower components, or addons in the project', + isProjectTask: true +}; + +function listComponents() { + try { + var bowerJson = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'bower.json'))); + log.info('Ions, bower components, or addons installed:'); + + Object.keys(bowerJson.devDependencies).forEach(function(bowerComponentName) { + log.info(bowerComponentName.green); + }); + } catch (ex) { + log.error('This command can only be used when in an Ionic project directory'.red.bold); + } +} + +/** + * Need to look at bower.json of package just installed and look for any cordova plugins required + * Check the directory in the projects `.bowerrc` file + * Then go to /path/to/bower/components//ionic-plugins.json - 'plugins' + * For each plugins - call 'ionic add plugin ' + */ +function run() { + + try { + listComponents(); + } catch (error) { + var errorMessage = error.message ? error.message : error; + appLibUtils.fail(errorMessage, 'list'); + } +} + +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 73a6f5f3b8..a4cf977eb0 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -1,12 +1,22 @@ +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle var IonicAppLib = require('ionic-app-lib'); var Login = IonicAppLib.login; var log = IonicAppLib.logging.logger; var prompt = require('prompt'); var Q = require('q'); -var settings = IonicAppLib.settings; -var Task = require('./task').Task; +var appLibSettings = IonicAppLib.settings; + +var settings = { + title: 'login', + name: 'login', + isProjectTask: false +}; -var LoginTask = module.exports; +function run(ionic, argv) { + return login(argv); +} // Login is set up a little differently than other tasks // First of all - we need a way to prompt the user for email/pw @@ -14,7 +24,7 @@ var LoginTask = module.exports; // Any other calls to login in the lib take email/pw to login // The login task itself just runs LoginTask.login() -LoginTask.login = function login(argv) { +function login(argv) { var email = argv.email || argv.e || process.env.IONIC_EMAIL; var password = argv.password || argv.p || process.env.IONIC_PASSWORD; @@ -23,7 +33,7 @@ LoginTask.login = function login(argv) { if (email && password) { promise = Q({ email: email, password: password }); } else { - promise = LoginTask.promptForLogin(argv); + promise = promptForLogin(argv); } return promise @@ -41,13 +51,9 @@ LoginTask.login = function login(argv) { // log.info(ex); // log.info(ex.stack); }); -}; - -function IonicTask() {} +} -IonicTask.prototype = new Task(); - -LoginTask.promptForLogin = function promptForLogin(argv) { +function promptForLogin(argv) { var q = Q.defer(); var schema = [{ @@ -64,7 +70,7 @@ LoginTask.promptForLogin = function promptForLogin(argv) { // prompt for log log.info('\nTo continue, please login to your Ionic account.'.bold.green); - log.info('Don\'t have one? Create one at: '.bold + (settings.IONIC_DASH + '/signup').bold + '\n'); + log.info('Don\'t have one? Create one at: '.bold + (appLibSettings.IONIC_DASH + '/signup').bold + '\n'); prompt.override = argv; prompt.message = ''; @@ -80,10 +86,10 @@ LoginTask.promptForLogin = function promptForLogin(argv) { }); return q.promise; -}; - -IonicTask.prototype.run = function(ionic, argv) { - return LoginTask.login(argv); -}; +} -LoginTask.IonicTask = IonicTask; +module.exports = extend(settings, { + run: run, + login: login, + promptForLogin: promptForLogin +}); diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 956b89eba0..3cb921a6ea 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -1,26 +1,46 @@ +'use strict'; + var path = require('path'); var _ = require('underscore'); +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle var Q = require('q'); var moment = require('moment'); var expandTilde = require('expand-tilde'); var ProgressBar = require('progress'); var IonicAppLib = require('ionic-app-lib'); -var Utils = IonicAppLib.utils; +var fail = IonicAppLib.utils.fail; var Login = IonicAppLib.login; var log = IonicAppLib.logging.logger; var Package = IonicAppLib.package; var LoginTask = require('./login'); -var Table = require('./table'); -var Task = require('./task').Task; +var Table = require('../utils/table'); var Project = IonicAppLib.project; -function IonicTask() {} - -IonicTask.prototype = new Task(); - -IonicTask.prototype.run = function(ionic, argv) { +var settings = { + title: 'package', + name: 'package', + summary: 'Use Ionic Package to build your app ' + '(alpha)'.red, + args: { + '': 'build android'.yellow + ', ' + 'build ios'.yellow + + ', ' + 'list'.yellow + ', ' + 'info'.yellow + ', or ' + 'download'.yellow, + '[options]': '' + }, + options: { + '--release': '(' + 'build '.yellow + + ') Mark this build as a release', + '--profile|-p ': '(' + 'build '.yellow + + ') Specify the Security Profile to use with this build', + '--noresources': '(' + 'build '.yellow + + ') Do not generate icon and splash screen resources during this build', + '--destination|-d ': '(' + 'download'.yellow + + ') Specify the destination directory to download your packaged app.' + }, + module: './ionic/package', + isProjectTask: true +}; +function run(ionic, argv) { var cmd; if (argv._.length < 2) { @@ -42,7 +62,7 @@ IonicTask.prototype.run = function(ionic, argv) { throw new Error('Missing Ionic App ID.'); } } catch (ex) { - return Utils.fail(ex, 'package'); + return fail(ex, 'package'); } switch (cmd) { @@ -56,13 +76,13 @@ IonicTask.prototype.run = function(ionic, argv) { return packageDownload(ionic, argv, dir, project, appId); } - return Utils.fail("Unknown subcommand '" + cmd + "'.", 'package'); -}; + return fail("Unknown subcommand '" + cmd + "'.", 'package'); +} function packageBuild(ionic, argv, dir, project, appId) { if (argv._.length < 3) { - return Utils.fail('Specify a valid platform (android or ios).', 'package'); + return fail('Specify a valid platform (android or ios).', 'package'); } var jar; @@ -70,7 +90,7 @@ function packageBuild(ionic, argv, dir, project, appId) { var buildMode = argv.release ? 'release' : 'debug'; if (!_.contains(['android', 'ios'], platform)) { - return Utils.fail("Invalid platform '" + platform + "', please choose either 'android' or 'ios'.", 'package'); + return fail("Invalid platform '" + platform + "', please choose either 'android' or 'ios'.", 'package'); } return Login.retrieveLogin() @@ -98,14 +118,14 @@ function packageBuild(ionic, argv, dir, project, appId) { return Package.buildAndroidDebug(dir, jar, appId, options); } else if (buildMode === 'release') { if (typeof argv.profile === 'undefined') { - return Utils.fail('Must specify security profile for android release builds (--profile ).', 'package'); + return fail('Must specify security profile for android release builds (--profile ).', 'package'); } return Package.buildAndroidRelease(dir, jar, appId, argv.profile, options); } } else if (platform === 'ios') { if (typeof argv.profile === 'undefined') { - return Utils.fail('Must specify security profile for ios builds (--profile ).', 'package'); + return fail('Must specify security profile for ios builds (--profile ).', 'package'); } return Package.buildIOS(dir, jar, appId, argv.profile, buildMode, options); @@ -114,7 +134,7 @@ function packageBuild(ionic, argv, dir, project, appId) { return Q.reject('Unrecognized platform/build mode.'); }) .catch(function(ex) { - Utils.fail(ex, 'package'); + fail(ex, 'package'); }); } @@ -198,7 +218,7 @@ function packageList(ionic, argv, dir, project, appId) { } }) .catch(function(ex) { - Utils.fail(ex, 'package'); + fail(ex, 'package'); }); } @@ -256,7 +276,7 @@ function packageInfo(ionic, argv, dir, project, appId) { }) .catch(function(ex) { - Utils.fail(ex, 'package'); + fail(ex, 'package'); }); } @@ -316,9 +336,11 @@ function packageDownload(ionic, argv, dir, project, appId) { bar.tick(state.received); }) .catch(function(ex) { - Utils.fail(ex); + fail(ex); }); } -exports.IonicTask = IonicTask; +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/platform.js b/lib/ionic/platform.js new file mode 100644 index 0000000000..5f4a4d17d2 --- /dev/null +++ b/lib/ionic/platform.js @@ -0,0 +1,79 @@ +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var IonicAppLib = require('ionic-app-lib'); +var IonicResources = IonicAppLib.resources; +var ConfigXml = IonicAppLib.configXml; +var State = IonicAppLib.state; +var log = IonicAppLib.logging.logger; +var cordovaUtils = require('../utils/cordova'); + +var settings = { + title: 'platform', + name: 'platform', + summary: 'Add platform target for building an Ionic app', + args: { + '[options]': '', + '': '' + }, + options: { + '--noresources|-r': { + title: 'Do not add default Ionic icons and splash screen resources', + boolean: true + }, + '--nosave|-e': { + title: 'Do not save the platform to the package.json file', + boolean: true + } + }, + isProjectTask: true +}; + +function run(ionic, argv, rawCliArguments) { + var appDirectory = process.cwd(); + var cmdName = process.argv[2].toLowerCase(); + var rawArgs = rawCliArguments.slice(0); + var argumentName = argv._[2]; + + var isAddCmd = argv._.indexOf('add') !== -1; + var isRmCmd = argv._.indexOf('rm') !== -1 || argv._.indexOf('remove') !== -1; + var addResources = isAddCmd && !(argv.noresources || argv.r); + + // ensure the content node was set back to its original + return ConfigXml.setConfigXml(appDirectory, { + resetContent: true, + errorWhenNotFound: false + }) + .then(function() { + if (addResources) { + return IonicResources.copyIconFilesIntoResources(appDirectory) + .then(function() { + return IonicResources.addIonicIcons(appDirectory, argumentName); + }); + } + }) + .then(function() { + var optionList = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawArgs); + return cordovaUtils.execCordovaCommand(optionList); + }) + .then(function(runCode) { + + // We dont want to do anything if the cordova command failed + if (runCode !== 0 || argv.nosave) { + return; + } + + if (isAddCmd) { + log.info('Saving platform to package.json file'); + return State.savePlatform(appDirectory, argumentName); + } + if (isRmCmd) { + log.info('Removing platform from package.json file'); + return State.removePlatform(appDirectory, argumentName); + } + }); +} + +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/plugin.js b/lib/ionic/plugin.js new file mode 100644 index 0000000000..f5493e0735 --- /dev/null +++ b/lib/ionic/plugin.js @@ -0,0 +1,78 @@ +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var IonicAppLib = require('ionic-app-lib'); +var ConfigXml = IonicAppLib.configXml; +var State = IonicAppLib.state; +var log = IonicAppLib.logging.logger; +var cordovaUtils = require('../utils/cordova'); + +var settings = { + title: 'plugin add', + name: 'plugin', + summary: 'Add a Cordova plugin', + args: { + '[options]': '', + '': 'Can be a plugin ID, a local path, or a git URL.' + }, + options: { + '--searchpath ': 'When looking up plugins by ID, look in this directory\n' + + 'and subdirectories first for the plugin before\n' + + 'looking it up in the registry.', + '--nosave|-e': { + title: 'Do not save the plugin to the package.json file', + boolean: true + } + }, + isProjectTask: true +}; + +function run(ionic, argv, rawCliArguments) { + var appDirectory = process.cwd(); + var rawArgs = rawCliArguments.slice(0); + var cmdName = settings.name; + var argumentName = argv._[2]; + + var isAddCmd = argv._.indexOf('add') !== -1; + var isRmCmd = argv._.indexOf('rm') !== -1 || argv._.indexOf('remove') !== -1; + + // ensure the content node was set back to its original + return ConfigXml.setConfigXml(appDirectory, { + resetContent: true, + errorWhenNotFound: false + }) + .then(function() { + var optionList = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawArgs); + return cordovaUtils.execCordovaCommand(optionList); + }) + .then(function(runCode) { + + // We dont want to do anything if the cordova command failed + if (runCode !== 0 || argv.nosave) { + return; + } + + // Is the plugin being added + if (isAddCmd) { + var variables; + + log.info('Saving plugin to package.json file'); + if (argv.variable && (typeof argv.variable === 'string')) { + variables = [argv.variable]; + } else { + variables = argv.variable; + } + return State.savePlugin(appDirectory, argumentName, variables); + } + + // Is the plugin being removed + if (isRmCmd) { + log.info('Removing plugin from package.json file'); + return State.removePlugin(appDirectory, argumentName); + } + }); +} + +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/prepare.js b/lib/ionic/prepare.js new file mode 100644 index 0000000000..f780130e66 --- /dev/null +++ b/lib/ionic/prepare.js @@ -0,0 +1,32 @@ +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var IonicAppLib = require('ionic-app-lib'); +var ConfigXml = IonicAppLib.configXml; +var cordovaUtils = require('../utils/cordova'); + +var settings = { + title: 'prepare', + name: 'prepare', + isProjectTask: true +}; + +function run(ionic, argv, rawCliArguments) { + var appDirectory = process.cwd(); + var rawArgs = rawCliArguments.slice(0); + var cmdName = argv._[0].toLowerCase(); + + // ensure the content node was set back to its original + return ConfigXml.setConfigXml(appDirectory, { + resetContent: true, + errorWhenNotFound: false + }) + .then(function() { + var optionList = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawArgs); + return cordovaUtils.execCordovaCommand(optionList); + }); +} + +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/push.js b/lib/ionic/push.js index a7f6b31e6f..5f93b4bcbe 100644 --- a/lib/ionic/push.js +++ b/lib/ionic/push.js @@ -1,3 +1,6 @@ +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle var fs = require('fs'); var path = require('path'); var parseUrl = require('url').parse; @@ -5,25 +8,35 @@ var request = require('request'); var argv = require('optimist').argv; var prompt = require('prompt'); var FormData = require('form-data'); -var Task = require('./task').Task; var IonicAppLib = require('ionic-app-lib'); var IonicProject = IonicAppLib.project; var Login = IonicAppLib.login; var log = IonicAppLib.logging.logger; +var fail = IonicAppLib.utils.fail; var LoginTask = require('./login'); -function IonicTask() {} - -IonicTask.prototype = new Task(); +var settings = { + title: 'push', + name: 'push', + summary: 'Upload APNS and GCM credentials to Ionic Push ' + '(alpha)'.red, + options: { + '--ios-dev-cert': 'Upload your development .p12 file to Ionic Push', + '--ios-prod-cert': 'Upload your production .p12 file to Ionic Push', + '--production-mode=y,n': 'Tell Ionic Push to use production (y) or sandbox (n) APNS servers', + '--google-api-key ': "Set your app's GCM API key on Ionic Push" + }, + isProjectTask: true +}; -IonicTask.prototype.run = function(ionic, argv) { - var self = this; - self.ionic = ionic; - self.inputValues = {}; - self.inputFiles = {}; - self.useCmdArgs = false; +function run(ionic, argv) { + var task = { + ionic: ionic, + inputValues: {}, + inputFiles: {}, + useCmdArgs: false + }; - this.getCmdLineOptions(); + getCmdLineOptions(); Login.retrieveLogin() .then(function(jar) { @@ -34,38 +47,38 @@ IonicTask.prototype.run = function(ionic, argv) { return jar; }) .then(function(jar) { - self.jar = jar; + task.jar = jar; if (argv._[1] && argv._[1].trim() === 'webhook_url') { if (argv._[2]) { - self.set_webhook_url(self, argv._[2].trim()); + set_webhook_url(task, argv._[2].trim()); } else { log.error('You need to specify a webhook url!'.bold.red); } } else if (argv['google-api-key']) { - self.set_google_api_key(self, argv['google-api-key'].trim()); + set_google_api_key(task, argv['google-api-key'].trim()); } else if (argv['send'] || argv.s) { // Dev wants to send a push notification - self.send_push(self); + send_push(task); } else if (argv['ios-dev-cert'] || argv['ios-prod-cert'] || argv.d || argv.p) { // Dev wants to upload an ios push cert - self.upload_cert(self); + upload_cert(task); } else if (argv['production-mode'] || argv.l) { // Dev wants to switch between test/live mode // this will look like --live-mode=[y/n] // where y will put into live and n will put into dev - self.set_production_mode(self); + set_production_mode(task); } }) .catch(function(ex) { log.error('An error occurred', ex); }); -}; +} -IonicTask.prototype.set_webhook_url = function(task, webhook_url) { // eslint-disable-line camelcase +function set_webhook_url(task, webhook_url) { // eslint-disable-line camelcase var project = IonicProject.load(); if (!project.get('app_id')) { @@ -77,7 +90,7 @@ IonicTask.prototype.set_webhook_url = function(task, webhook_url) { // eslint-di var params = parseUrl(url); var form = new FormData(); - form.append('csrfmiddlewaretoken', task.getCsrfToken(task.jar)); + form.append('csrfmiddlewaretoken', getCsrfToken(task.jar)); form.append('webhook_url', webhook_url); form.submit({ @@ -94,22 +107,22 @@ IonicTask.prototype.set_webhook_url = function(task, webhook_url) { // eslint-di function(err, response) { response.on('data', function() { if (err) { - return task.ionic.fail('Error setting Webhook URL: ' + err); + return fail('Error setting Webhook URL: ' + err); } if (parseInt(response.statusCode, 10) === '200') { log.info('Webhook URL set to', webhook_url); } else { - return task.ionic.fail('App not found'); + return fail('App not found'); } }); }); return true; -}; +} -IonicTask.prototype.set_google_api_key = function(task, google_api_key) { // eslint-disable-line camelcase +function set_google_api_key(task, google_api_key) { // eslint-disable-line camelcase var project = IonicProject.load(); if (!project.get('app_id')) { @@ -121,7 +134,7 @@ IonicTask.prototype.set_google_api_key = function(task, google_api_key) { // esl var params = parseUrl(url); var form = new FormData(); - form.append('csrfmiddlewaretoken', task.getCsrfToken(task.jar)); + form.append('csrfmiddlewaretoken', getCsrfToken(task.jar)); form.append('android_api_key', google_api_key); form.submit({ @@ -138,22 +151,22 @@ IonicTask.prototype.set_google_api_key = function(task, google_api_key) { // esl function(err, response) { response.on('data', function() { if (err) { - return task.ionic.fail('Error setting Google API Key: ' + err); + return fail('Error setting Google API Key: ' + err); } if (parseInt(response.statusCode, 10) === '200') { log.info('Google API Key Saved'.green); } else { - return task.ionic.fail('App not found'); + return fail('App not found'); } }); }); return true; -}; +} -IonicTask.prototype.send_push = function(task) { // eslint-disable-line camelcase +function send_push() { // eslint-disable-line camelcase var project = IonicProject.load(); if (!project.get('app_id')) { @@ -203,7 +216,7 @@ IonicTask.prototype.send_push = function(task) { // eslint-disable-line camelcas prompt.get({ properties: promptProperties }, function(err, promptResult) { if (err) { - return task.ionic.fail('Error: ' + err); + return fail('Error: ' + err); } var apiKey = promptResult['push-api-key']; @@ -239,9 +252,9 @@ IonicTask.prototype.send_push = function(task) { // eslint-disable-line camelcas } }); }); -}; +} -IonicTask.prototype.set_production_mode = function(task) { // eslint-disable-line camelcase +function set_production_mode(task) { // eslint-disable-line camelcase var project = IonicProject.load(); if (!project.get('app_id')) { @@ -265,7 +278,7 @@ IonicTask.prototype.set_production_mode = function(task) { // eslint-disable-lin var params = parseUrl(url); var form = new FormData(); - form.append('csrfmiddlewaretoken', task.getCsrfToken(task.jar)); + form.append('csrfmiddlewaretoken', getCsrfToken(task.jar)); form.append('production_mode', task.inputValues['production-mode']); form.submit({ @@ -281,13 +294,13 @@ IonicTask.prototype.set_production_mode = function(task) { // eslint-disable-lin }, function(err, response) { if (err) { - return task.ionic.fail('Error uploading certificate: ' + err); + return fail('Error uploading certificate: ' + err); } response.setEncoding('utf8'); response.on('data', function(data) { if (err) { - return task.ionic.fail('Error setting mode: ' + err); + return fail('Error setting mode: ' + err); } try { @@ -296,7 +309,7 @@ IonicTask.prototype.set_production_mode = function(task) { // eslint-disable-lin for (var j = 0; j < d.errors.length; j += 1) { log.error((d.errors[j]).bold.red); } - return task.ionic.fail('Unable to set mode'); + return fail('Unable to set mode'); } if (task.inputValues['production-mode'] === 'y') { @@ -306,13 +319,13 @@ IonicTask.prototype.set_production_mode = function(task) { // eslint-disable-lin } } catch (parseEx) { - return task.ionic.fail('Error response: ' + parseEx); + return fail('Error response: ' + parseEx); } }); }); -}; +} -IonicTask.prototype.upload_cert = function(task) { // eslint-disable-line camelcase +function upload_cert(task) { // eslint-disable-line camelcase var project = IonicProject.load(); if (!project.get('app_id')) { @@ -348,14 +361,14 @@ IonicTask.prototype.upload_cert = function(task) { // eslint-disable-line camelc prompt.get({ properties: promptProperties }, function(err, promptResult) { if (err) { - return task.ionic.fail('Error: ' + err); + return fail('Error: ' + err); } var url = task.ionic.IONIC_DASH + task.ionic.IONIC_API + 'app/' + project.get('app_id') + '/push/upload-push-cert'; var params = parseUrl(url); var form = new FormData(); - form.append('csrfmiddlewaretoken', task.getCsrfToken(task.jar)); + form.append('csrfmiddlewaretoken', getCsrfToken(task.jar)); var inputFile; @@ -363,7 +376,7 @@ IonicTask.prototype.upload_cert = function(task) { // eslint-disable-line camelc inputFile = promptResult['ios-push-cert'].replace(/\\ /g, ' ').trim(); form.append('ios_push_cert', fs.createReadStream(resolvePath(inputFile))); } catch (e) { - return task.ionic.fail('Error loading ' + resolvePath(inputFile)); + return fail('Error loading ' + resolvePath(inputFile)); } // Set the flag for if this is a production or dev cert @@ -382,13 +395,13 @@ IonicTask.prototype.upload_cert = function(task) { // eslint-disable-line camelc }, function(err, response) { if (err) { - return task.ionic.fail('Error uploading certificate: ' + err); + return fail('Error uploading certificate: ' + err); } response.setEncoding('utf8'); response.on('data', function(data) { if (err) { - return task.ionic.fail('Error uploading certificate: ' + err); + return fail('Error uploading certificate: ' + err); } try { @@ -397,27 +410,26 @@ IonicTask.prototype.upload_cert = function(task) { // eslint-disable-line camelc for (var j = 0; j < d.errors.length; j += 1) { log.error((d.errors[j]).bold.red); } - return task.ionic.fail('Unable to upload certificate'); + return fail('Unable to upload certificate'); } log.info('Successfully uploaded certificate'); } catch (parseEx) { - return task.ionic.fail('Error response: ' + parseEx); + return fail('Error response: ' + parseEx); } }); }); }); -}; +} -IonicTask.prototype.getCmdLineOptions = function() { - var self = this; +function getCmdLineOptions(task) { function getCmdArgValue(propertyName, shortName) { var value = argv[propertyName] || argv[shortName]; if (value) { - self.inputValues[propertyName] = value; - self.useCmdArgs = true; + task.inputValues[propertyName] = value; + task.useCmdArgs = true; } } @@ -425,10 +437,10 @@ IonicTask.prototype.getCmdLineOptions = function() { var value = argv[propertyName] || argv[shortName]; if (value) { if (!fileExists(value)) { - return self.ionic.fail('Unable to find file: ' + argv[propertyName]); + return fail('Unable to find file: ' + argv[propertyName]); } - self.inputFiles[propertyName] = value; - self.useCmdArgs = true; + task.inputFiles[propertyName] = value; + task.useCmdArgs = true; } } @@ -436,16 +448,16 @@ IonicTask.prototype.getCmdLineOptions = function() { getCmdArgFile('ios-push-dev-cert', 'd'); getCmdArgFile('ios-push-prod-cert', 'p'); -}; +} -IonicTask.prototype.getCsrfToken = function(jar) { +function getCsrfToken(jar) { for (var i = 0; i < jar.length; i += 1) { if (jar[i].key === 'csrftoken') { return jar[i].value; } } return ''; -}; +} function fileExists(filePath) { @@ -461,4 +473,13 @@ function resolvePath(p) { return path.resolve(p); } -exports.IonicTask = IonicTask; +module.exports = extend(settings, { + run: run, + set_webhook_url: set_webhook_url, // eslint-disable-line camelcase + set_google_api_key: set_google_api_key, // eslint-disable-line camelcase + send_push: send_push, // eslint-disable-line camelcase + set_production_mode: set_production_mode, // eslint-disable-line camelcase + upload_cert: upload_cert, // eslint-disable-line camelcase + getCmdLineOptions: getCmdLineOptions, + getCsrfToken: getCsrfToken +}); diff --git a/lib/ionic/remove.js b/lib/ionic/remove.js new file mode 100644 index 0000000000..17f429b5b7 --- /dev/null +++ b/lib/ionic/remove.js @@ -0,0 +1,67 @@ +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var childProcess = require('child_process'); +var IonicAppLib = require('ionic-app-lib'); +var appLibUtils = IonicAppLib.utils; +var ioLib = IonicAppLib.ioConfig; +var log = IonicAppLib.logging.logger; +var bower = require('../utils/bower'); + +var settings = { + title: 'remove', + name: 'remove', + summary: 'Remove an Ion, bower component, or addon from the project', + args: { + '[name]': 'The name of the Ion, bower component, or addon you wish to remove' + }, + isProjectTask: true +}; + +function uninstallBowerComponent(componentName) { + var bowerUninstallCommand = 'bower uninstall --save-dev ' + componentName; + + var result = childProcess.exec(bowerUninstallCommand); + + if (result.code !== 0) { + var errorMessage = 'Failed to find the bower component "'.red.bold + componentName.verbose + + '"'.red.bold + '.\nAre you sure it exists?'.red.bold; + + appLibUtils.fail(errorMessage, 'remove'); + } else { + var message = 'Bower component removed - ' + componentName; + log.info(message.red); + } +} + +/** + * Need to look at bower.json of package just installed and look for any cordova plugins required + * Check the directory in the projects `.bowerrc` file + * Then go to /path/to/bower/components//ionic-plugins.json - 'plugins' + * For each plugins - call 'ionic add plugin ' + */ +function run(ionic, argv) { + + if (!bower.checkForBower()) { + appLibUtils.fail(bower.installMessage, 'remove'); + return; + } + + var componentName = argv._[1]; + var ioSet = false; + + try { + uninstallBowerComponent(componentName); + } catch (error) { + var errorMessage = error.message ? error.message : error; + appLibUtils.fail(errorMessage, 'service'); + } + + // Inject the component into our index.html, if necessary, and save the app_id + ioLib.injectIoComponent(ioSet, componentName); + ioLib.warnMissingData(); +} + +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/resources.js b/lib/ionic/resources.js index 9d81bd4c69..9f02faad48 100644 --- a/lib/ionic/resources.js +++ b/lib/ionic/resources.js @@ -1,38 +1,57 @@ -var argv = require('optimist').argv; -var Task = require('./task').Task; +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle var IonicAppLib = require('ionic-app-lib'); var IonicResources = IonicAppLib.resources; -var IonicStats = require('./stats').IonicStats; -var Utils = IonicAppLib.utils; +var appLibUtils = IonicAppLib.utils; var Project = IonicAppLib.project; -function IonicTask() {} - -IonicTask.prototype = new Task(); +var resourcesSummary = [ + 'Automatically create icon and splash screen resources' + ' (beta)'.yellow, + 'Put your images in the ./resources directory, named splash or icon.', + 'Accepted file types are .png, .ai, and .psd.', + 'Icons should be 192x192 px without rounded corners.', + 'Splashscreens should be 2208x2208 px, with the image centered in the middle.\n' +].join('\n\t\t '); + +var settings = { + title: 'resources', + name: 'resources', + summary: resourcesSummary, + options: { + '--icon|-i': { + title: 'Generate icon resources', + boolean: true + }, + '--splash|-s': { + title: 'Generate splash screen resources', + boolean: true + } + }, + isProjectTask: true +}; -IonicTask.prototype.run = function() { +function run(ionic, argv) { var dir = null; try { dir = process.cwd(); Project.load(dir); } catch (ex) { - return Utils.fail(ex, 'resources'); + return appLibUtils.fail(ex, 'resources'); } var options = argv; options.platforms = argv._; - IonicResources.generate(dir, options) + return IonicResources.generate(dir, options) .catch(function(ex) { if (ex instanceof Error) { - Utils.fail(ex, 'resources'); + appLibUtils.fail(ex, 'resources'); } }); +} - IonicStats.t(); -}; - -module.exports = { - IonicTask: IonicTask -}; +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/run.js b/lib/ionic/run.js new file mode 100644 index 0000000000..c0b2cc01d5 --- /dev/null +++ b/lib/ionic/run.js @@ -0,0 +1,87 @@ +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var os = require('os'); +var Q = require('q'); +var IonicAppLib = require('ionic-app-lib'); +var ConfigXml = IonicAppLib.configXml; +var cordovaUtils = require('../utils/cordova'); + +var cordovaRunEmulateOptions = { + '--livereload|-l': 'Live reload app dev files from the device' + ' (beta)'.yellow, + '--address': 'Use specific address (livereload req.)', + '--port|-p': 'Dev server HTTP port (8100 default, livereload req.)', + '--livereload-port|-r': 'Live Reload port (35729 default, livereload req.)', + '--consolelogs|-c': { + title: 'Print app console logs to Ionic CLI (livereload req.)', + boolean: true + }, + '--serverlogs|-s': { + title: 'Print dev server logs to Ionic CLI (livereload req.)', + boolean: true + }, + '--debug|--release': { + title: '', + boolean: true + }, + '--device|--emulator|--target=FOO': '', + isProjectTask: true +}; + +var settings = { + title: 'run', + name: 'run', + summary: 'Run an Ionic project on a connected device', + args: { + '[options]': '', + '': '' + }, + options: cordovaRunEmulateOptions +}; + +function run(ionic, argv, rawCliArguments) { + var appDirectory = process.cwd(); + var rawArgs = rawCliArguments.slice(0); + var cmdName = argv._[0].toLowerCase(); + + var isLiveReload = argv.livereload || argv['live-reload'] || argv.l || false; + + // If platform was not passed then add it to the rawArgs + var runPlatform = argv._[1]; + if (!runPlatform) { + runPlatform = 'ios'; + rawArgs.push(runPlatform); + } + + if (runPlatform === 'ios' && os.platform() !== 'darwin') { + return Q.reject('✗ You cannot run iOS unless you are on Mac OSX.'); + } + + var promiseList = [] + .concat(!cordovaUtils.isPlatformInstalled(runPlatform, appDirectory) ? + cordovaUtils.installPlatform(runPlatform) : + []) + .concat(!cordovaUtils.arePluginsInstalled(appDirectory) ? + cordovaUtils.installPlugins() : + []); + + return Q.all(promiseList).then(function() { + if (isLiveReload) { + return cordovaUtils.setupLiveReload(argv); + } + + // ensure the content node was set back to its original + return ConfigXml.setConfigXml(process.cwd(), { + resetContent: true, + errorWhenNotFound: false + }); + }) + .then(function(serveOptions) { + var optionList = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawArgs); + return cordovaUtils.execCordovaCommand(optionList, isLiveReload, serveOptions); + }); +} + +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/security.js b/lib/ionic/security.js index 4b5945c5a0..42b7fdbfa3 100644 --- a/lib/ionic/security.js +++ b/lib/ionic/security.js @@ -1,25 +1,53 @@ +'use strict'; + var fs = require('fs'); var path = require('path'); var Q = require('q'); var expandTilde = require('expand-tilde'); var _ = require('underscore'); +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle var IonicAppLib = require('ionic-app-lib'); -var Utils = IonicAppLib.utils; +var fail = IonicAppLib.utils.fail; var Login = IonicAppLib.login; var Security = IonicAppLib.security; var log = IonicAppLib.logging.logger; var LoginTask = require('./login'); -var Prompt = require('./prompt'); -var Table = require('./table'); -var Task = require('./task').Task; +var Prompt = require('../utils/prompt'); +var Table = require('../utils/table'); var Project = IonicAppLib.project; -function IonicTask() {} - -IonicTask.prototype = new Task(); +var settings = { + title: 'security', + name: 'security', + summary: 'Store your app\'s credentials for the Ionic Platform ' + '(alpha)'.red, + args: { + '': 'profiles list'.yellow + ', ' + 'profiles add ""'.yellow + ', ' + + 'credentials android'.yellow + ', or ' + 'credentials ios'.yellow, + '[options]': '' + }, + options: { + '--profile ': '(' + 'credentials '.yellow + + ') Specify the profile on which these credentials are saved', + '--keystore|-s ': '(' + 'credentials android'.yellow + + ') Specify the location of your keystore file', + '--keystore-password|-p ': '(' + 'credentials android'.yellow + + ') Specify your keystore password (exclude for prompt)', + '--key-alias|-k ': '(' + 'credentials android'.yellow + + ') Specify your key alias for this app', + '--key-password|-w ': '(' + 'credentials android'.yellow + + ') Specify your key password for this app (exclude for prompt)', + '--cert|-c ': '(' + 'credentials ios'.yellow + + ') Specify the location of your .p12 file', + '--cert-password|-p ': '(' + 'credentials ios'.yellow + + ') Specify your certificate password (exclude for prompt)', + '--provisioning-profile|-r ': '(' + 'credentials ios'.yellow + + ') Specify the location of your .mobileprovision file' + }, + isProjectTask: true +}; -IonicTask.prototype.run = function(ionic, argv) { +function run(ionic, argv) { var cmd; var subcmd; @@ -43,7 +71,7 @@ IonicTask.prototype.run = function(ionic, argv) { throw new Error('Missing Ionic App ID.'); } } catch (ex) { - return Utils.fail(ex, 'security'); + return fail(ex, 'security'); } switch (cmd) { @@ -60,19 +88,19 @@ IonicTask.prototype.run = function(ionic, argv) { case 'list': return listSecurityProfiles(ionic, argv, dir, appId); default: - return Utils.fail("Unknown subcommand 'profiles " + subcmd + "'.", 'security'); + return fail("Unknown subcommand 'profiles " + subcmd + "'.", 'security'); } case 'credentials': return addSecurityCredentials(ionic, argv, dir, appId); } - return Utils.fail("Unknown subcommand '" + cmd + "'.", 'security'); -}; + return fail("Unknown subcommand '" + cmd + "'.", 'security'); +} function addSecurityProfile(ionic, argv, dir, appId) { if (argv._.length < 4) { - return Utils.fail('Specify a name for this Security Profile. Example: ' + + return fail('Specify a name for this Security Profile. Example: ' + "'ionic security profile add \"My New Profile\"'.", 'security'); } @@ -104,7 +132,7 @@ function addSecurityProfile(ionic, argv, dir, appId) { } }) .catch(function(ex) { - Utils.fail(ex, 'package'); + fail(ex, 'package'); }); } @@ -148,18 +176,18 @@ function listSecurityProfiles(ionic, argv, dir, appId) { } }) .catch(function(ex) { - Utils.fail(ex, 'security'); + fail(ex, 'security'); }); } function addSecurityCredentials(ionic, argv, dir, appId) { if (argv._.length < 3) { - return Utils.fail('Specify a valid platform (android or ios).', 'security'); + return fail('Specify a valid platform (android or ios).', 'security'); } if (typeof argv.profile === 'undefined') { - return Utils.fail('Specify the Security Profile on which these credentials are saved ' + + return fail('Specify the Security Profile on which these credentials are saved ' + '(--profile ).', 'security'); } @@ -168,7 +196,7 @@ function addSecurityCredentials(ionic, argv, dir, appId) { var profile = argv.profile; if (!_.contains(['android', 'ios'], platform)) { - return Utils.fail("Invalid platform '" + platform + "', please choose either 'android' or 'ios'.", 'security'); + return fail("Invalid platform '" + platform + "', please choose either 'android' or 'ios'.", 'security'); } return Login.retrieveLogin() @@ -278,8 +306,10 @@ function addSecurityCredentials(ionic, argv, dir, appId) { } }) .catch(function(ex) { - Utils.fail(ex, 'package'); + fail(ex, 'package'); }); } -exports.IonicTask = IonicTask; +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index c66d67f78a..147ba75488 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -1,30 +1,65 @@ +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle var IonicAppLib = require('ionic-app-lib'); -var Utils = IonicAppLib.utils; +var appLibUtils = IonicAppLib.utils; var events = IonicAppLib.events; var Q = require('q'); -var Task = require('./task').Task; var Serve = IonicAppLib.serve; var log = IonicAppLib.logging.logger; var IonicProject = IonicAppLib.project; -function IonicTask() {} - -IonicTask.prototype = new Task(); - -IonicTask.prototype.run = function(ionic, argv) { - this.ionic = ionic; - this.port = argv._[1]; - this.liveReloadPort = argv._[2]; - - if (argv._[0] === 'address') { - - // reset any address configs - var ionicConfig = IonicAppLib.config.load(); - ionicConfig.set('ionicServeAddress', null); - ionicConfig.set('platformServeAddress', null); - return Serve.getAddress({ isAddressCmd: true }); - } +var settings = { + title: 'serve', + name: 'serve', + summary: 'Start a local development server for app dev/testing', + args: { + '[options]': '' + }, + options: { + '--consolelogs|-c': { + title: 'Print app console logs to Ionic CLI', + boolean: true + }, + '--serverlogs|-s': { + title: 'Print dev server logs to Ionic CLI', + boolean: true + }, + '--port|-p': 'Dev server HTTP port (8100 default)', + '--livereload-port|-r': 'Live Reload port (35729 default)', + '--nobrowser|-b': { + title: 'Disable launching a browser', + boolean: true + }, + '--nolivereload|-d': { + title: 'Do not start live reload', + boolean: true + }, + '--noproxy|-x': { + title: 'Do not add proxies', + boolean: true + }, + '--address': 'Use specific address or return with failure', + '--all|-a': { + title: 'Have the server listen on all addresses (0.0.0.0)', + boolean: true + }, + '--browser|-w': 'Specifies the browser to use (safari, firefox, chrome)', + '--browseroption|-o': 'Specifies a path to open to (/#/tab/dash)', + '--lab|-l': { + title: 'Test your apps on multiple screen sizes and platform types', + boolean: true + }, + '--nogulp': { + title: 'Disable running gulp during serve', + boolean: true + }, + '--platform|-t': 'Start serve with a specific platform (ios/android)' + }, + isProjectTask: true +}; +function run(ionic, argv) { var project = null; var cwd = process.cwd(); @@ -32,7 +67,7 @@ IonicTask.prototype.run = function(ionic, argv) { project = IonicProject.load(cwd); } catch (ex) { log.error('Error occured', ex); - return Utils.fail(ex.message); + return appLibUtils.fail(ex.message); } var options = Serve.loadSettings(argv, project); @@ -83,8 +118,10 @@ IonicTask.prototype.run = function(ionic, argv) { throw error; }); } catch (ex) { - Utils.fail('Error with serve- ' + ex); + appLibUtils.fail('Error with serve- ' + ex); } -}; +} -exports.IonicTask = IonicTask; +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/service.js b/lib/ionic/service.js index 2d5ffe2dbc..3feeb7e0f3 100644 --- a/lib/ionic/service.js +++ b/lib/ionic/service.js @@ -1,18 +1,27 @@ -require('shelljs/global'); +'use strict'; -var Task = require('./task').Task; var fs = require('fs'); var path = require('path'); var exec = require('child_process').exec; var _ = require('underscore'); -var bower = require('./bower'); +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var bower = require('../utils/bower'); var log = require('ionic-app-lib').logging.logger; +var IonicAppLib = require('ionic-app-lib'); +var fail = IonicAppLib.utils.fail; + +var settings = { + title: 'service add', + name: 'service', + summary: 'Add an Ionic service package and install any required plugins', + args: { + '[options]': '', + '': 'Can be a service name or a git url' + }, + isProjectTask: true +}; -function IonicTask() {} - -IonicTask.prototype = new Task(); - -IonicTask.prototype.readIonicProjectJson = function readIonicProjectJson() { +function readIonicProjectJson() { var ionicProjectFile = path.join(process.cwd(), 'ionic.project'); var ionicProjectJson = null; try { @@ -23,13 +32,13 @@ IonicTask.prototype.readIonicProjectJson = function readIonicProjectJson() { } return ionicProjectJson; -}; +} -IonicTask.prototype.addServiceToIonicJson = function addServiceToIonicJson(serviceName) { +function addServiceToIonicJson(serviceName) { var ionicProjectFile = path.join(process.cwd(), 'ionic.project'); try { - var ionicProjectJson = this.readIonicProjectJson() || {}; + var ionicProjectJson = readIonicProjectJson() || {}; if (!ionicProjectJson.services) { ionicProjectJson.services = []; @@ -44,11 +53,11 @@ IonicTask.prototype.addServiceToIonicJson = function addServiceToIonicJson(servi fs.writeFileSync(ionicProjectFile, JSON.stringify(ionicProjectJson, null, 2), 'utf8'); } catch (ex) { - this.ionic.fail('Failed to update the ionic.project settings for the service', 'service'); + fail('Failed to update the ionic.project settings for the service', 'service'); } -}; +} -IonicTask.prototype.installBowerComponent = function installBowerComponent(serviceName) { +function installBowerComponent(serviceName) { var bowerInstallCommand = 'bower link ionic-service-' + serviceName; var result = exec(bowerInstallCommand); @@ -57,13 +66,13 @@ IonicTask.prototype.installBowerComponent = function installBowerComponent(servi // Error happened, report it. var errorMessage = 'Failed to find the service "'.bold + serviceName.verbose + '"'.bold + '.\nAre you sure it exists?'.bold; - this.ionic.fail(errorMessage, 'service'); + fail(errorMessage, 'service'); } else { - this.addServiceToIonicJson(serviceName); + addServiceToIonicJson(serviceName); } -}; +} -IonicTask.prototype.uninstallBowerComponent = function uninstallBowerComponent(serviceName) { +function uninstallBowerComponent(serviceName) { var bowerUninstallCommand = 'bower unlink ionic-service-' + serviceName; var result = exec(bowerUninstallCommand); @@ -71,11 +80,11 @@ IonicTask.prototype.uninstallBowerComponent = function uninstallBowerComponent(s if (result.code !== 0) { var errorMessage = 'Failed to find the service "'.bold + serviceName.verbose + '"'.bold + '.\nAre you sure it exists?'.bold; - this.ionic.fail(errorMessage, 'service'); + fail(errorMessage, 'service'); } -}; +} -IonicTask.prototype.getBowerComponentsLocation = function getBowerComponentsLocation() { +function getBowerComponentsLocation() { var bowerRcFileLocation = path.join(process.cwd(), '.bowerrc'); var bowerRc = null; @@ -93,16 +102,16 @@ IonicTask.prototype.getBowerComponentsLocation = function getBowerComponentsLoca } return directory; -}; +} -IonicTask.prototype.getBowerJson = function getBowerJson(directory, serviceName) { +function getBowerJson(directory, serviceName) { var bowerJsonLocation = path.join(process.cwd(), directory, 'ionic-service-' + serviceName, 'ionic-plugins.json'); var packageBowerJson = require(bowerJsonLocation); return packageBowerJson; -}; +} -IonicTask.prototype.installBowerPlugins = function installBowerPlugins(directory, serviceName) { - var packageBowerJson = this.getBowerJson(directory, serviceName); +function installBowerPlugins(directory, serviceName) { + var packageBowerJson = getBowerJson(directory, serviceName); _.each(packageBowerJson.plugins, function(plugin) { log.info('Installing cordova plugin - ' + plugin.name + ' (' + plugin.id + ')'); @@ -112,12 +121,12 @@ IonicTask.prototype.installBowerPlugins = function installBowerPlugins(directory if (pluginInstallResult.code !== 0) { var errorMessage = 'Failed to find the plugin "'.bold + plugin.name.verbose + '"'.bold + '.'.bold; - this.ionic.fail(errorMessage, 'service'); + fail(errorMessage, 'service'); } }); -}; +} -IonicTask.prototype.uninstallBowerPlugins = function uninstallBowerPlugins(bowerJson) { +function uninstallBowerPlugins(bowerJson) { _.each(bowerJson.plugins, function(plugin) { log.info('Uninstalling cordova plugin - ' + plugin.name); var uninstallPluginCmd = 'ionic plugin rm ' + plugin.id; @@ -126,43 +135,39 @@ IonicTask.prototype.uninstallBowerPlugins = function uninstallBowerPlugins(bower if (pluginRemoveResult.code !== 0) { var errorMessage = 'Failed to find the plugin to remove "'.bold + plugin.name.verbose + '"'.bold + '.'.bold; - this.ionic.fail(errorMessage, 'service'); + fail(errorMessage, 'service'); } }); -}; +} -IonicTask.prototype.addService = function addService(serviceName) { - this.installBowerComponent(serviceName); +function addService(serviceName) { + installBowerComponent(serviceName); - var directory = this.getBowerComponentsLocation(); + var directory = getBowerComponentsLocation(); log.info('Checking for any plugins required by service package'); - this.installBowerPlugins(directory, serviceName); + installBowerPlugins(directory, serviceName); log.info('ionic service add completed'); -}; +} -IonicTask.prototype.removeService = function removeService(serviceName) { - var directory = this.getBowerComponentsLocation(); +function removeService(serviceName) { + var directory = getBowerComponentsLocation(); var packageBowerJson = this.getBowerJson(directory, serviceName); - this.uninstallBowerComponent(serviceName); + uninstallBowerComponent(serviceName); - this.uninstallBowerPlugins(packageBowerJson); -}; - -IonicTask.prototype.listServices = function listServices() { -}; + uninstallBowerPlugins(packageBowerJson); +} // Need to look at bower.json of package just installed and look for any cordova plugins required // Check the directory in the projects `.bowerrc` file // Then go to /path/to/bower/components//ionic-plugins.json - 'plugins' // For each plugins - call 'ionic add plugin ' -IonicTask.prototype.run = function run(ionic, argv) { - this.ionic = ionic; +function run(ionic, argv) { if (!bower.IonicBower.checkForBower()) { - this.ionic.fail(bower.IonicBower.installMessage, 'service'); + fail(bower.IonicBower.installMessage, 'service'); return; } @@ -172,19 +177,28 @@ IonicTask.prototype.run = function run(ionic, argv) { try { switch (action) { case 'add': - this.addService(serviceName); + addService(serviceName); break; case 'remove': - this.removeService(serviceName); - break; - case 'list': - this.listServices(); + removeService(serviceName); break; } } catch (error) { var errorMessage = error.message ? error.message : error; - this.ionic.fail(errorMessage, 'service'); + fail(errorMessage, 'service'); } -}; - -exports.IonicTask = IonicTask; +} + +module.exports = extend(settings, { + readIonicProjectJson: readIonicProjectJson, + addServiceToIonicJson: addServiceToIonicJson, + installBowerComponent: installBowerComponent, + uninstallBowerComponent: uninstallBowerComponent, + getBowerComponentsLocation: getBowerComponentsLocation, + getBowerJson: getBowerJson, + installBowerPlugins: installBowerPlugins, + uninstallBowerPlugins: uninstallBowerPlugins, + addService: addService, + removeService: removeService, + run: run +}); diff --git a/lib/ionic/setup.js b/lib/ionic/setup.js index 5bba986374..e27ce3d4ff 100644 --- a/lib/ionic/setup.js +++ b/lib/ionic/setup.js @@ -1,13 +1,23 @@ -var Task = require('./task').Task; +'use strict'; -function IonicTask() {} +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle -IonicTask.prototype = new Task(); +var settings = { + title: 'setup', + name: 'setup', + summary: 'Configure the project with a build tool ' + '(beta)'.yellow, + args: { + '[sass]': 'Setup the project to use Sass CSS precompiling' + }, + isProjectTask: true +}; -IonicTask.prototype.run = function() { +function run() { // TODO link to gulp hook docs console.log('The setup task has been deprecated.\n'); -}; +} -exports.IonicTask = IonicTask; +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/share.js b/lib/ionic/share.js index 6c9fd33c3f..cbdb8c909e 100644 --- a/lib/ionic/share.js +++ b/lib/ionic/share.js @@ -1,40 +1,46 @@ -require('shelljs/global'); +'use strict'; +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var LoginTask = require('./login'); var IonicAppLib = require('ionic-app-lib'); var IonicProject = IonicAppLib.project; -var Task = require('./task').Task; -var LoginTask = require('./login'); var Share = IonicAppLib.share; var log = IonicAppLib.logging.logger; var Login = IonicAppLib.login; -var Utils = IonicAppLib.utils; - -function IonicTask() {} +var appLibUtils = IonicAppLib.utils; -IonicTask.prototype = new Task(); +var settings = { + title: 'share', + name: 'share', + summary: 'Share an app with a client, co-worker, friend, or customer', + args: { + '': 'The email to share the app with' + }, + isProjectTask: true +}; -IonicTask.prototype.run = function(ionic, argv) { +function run(ionic, argv) { var project; if (argv._.length < 2) { - return ionic.fail('Invalid command', 'share'); + return appLibUtils.fail('Invalid command', 'share'); } try { project = IonicProject.load(); } catch (ex) { - ionic.fail(ex.message); + appLibUtils.fail(ex.message); return; } if (project.get('app_id') === '') { - return ionic.fail('You must first upload the app to share it'); + return appLibUtils.fail('You must first upload the app to share it'); } var email = argv._[1]; if (email.indexOf('@') < 0) { - return ionic.fail('Invalid email address', 'share'); + return appLibUtils.fail('Invalid email address', 'share'); } log.info(['Sharing app ', project.get('name'), ' (', project.get('app_id'), ') with ', email, '.'].join('').green); @@ -51,8 +57,10 @@ IonicTask.prototype.run = function(ionic, argv) { return Share.shareApp(process.cwd(), jar, email); }) .catch(function(ex) { - return Utils.fail(ex); + return appLibUtils.fail(ex); }); -}; +} -exports.IonicTask = IonicTask; +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 07debdbb54..72d540c10b 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -1,38 +1,77 @@ require('shelljs/global'); require('colors'); +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle var fs = require('fs'); var Q = require('q'); -var IonicTemplates = require('./templates').IonicTask; -var Task = require('./task').Task; +var templateUtils = require('../utils/templates'); var IonicAppLib = require('ionic-app-lib'); var Start = IonicAppLib.start; var log = IonicAppLib.logging.logger; -var Utils = IonicAppLib.utils; +var appLibUtils = IonicAppLib.utils; +var settings = { + title: 'start', + name: 'start', + summary: 'Starts a new Ionic project in the specified PATH', + args: { + '[options]': 'any flags for the command', + '': 'directory for the new project', + '[template]': 'Starter templates can either come from a named template, \n' + + '(ex: tabs, sidemenu, blank),\n' + + 'a Github repo, a Codepen url, or a local directory.\n' + + 'Codepen url, ex: http://codepen.io/ionic/pen/odqCz\n' + + 'Defaults to Ionic "tabs" starter template' + }, + options: { + '--appname|-a': 'Human readable name for the app (Use quotes around the name)', + '--id|-i': 'Package name for config, ex: com.mycompany.myapp', + '--skip-npm': { + title: 'Skip npm package installation', + boolean: true + }, + '--no-cordova|-w': { + title: 'Create a basic structure without Cordova requirements', + boolean: true + }, + '--sass|-s': { + title: 'Setup the project to use Sass CSS precompiling', + boolean: true + }, + '--list|-l': { + title: 'List starter templates available', + boolean: true + }, + '--io-app-id': 'The Ionic.io app ID to use', + '--template|-t': 'Project starter template', + '--v2|-v': { + boolean: true, + title: 'Start a Ionic v2 project' + }, + '--typescript|--ts': { + boolean: true, + title: '(with --v2 only) Use TypeScript in starter' + }, + '--zip-file|-z': 'URL to download zipfile for starter template' + }, + isProjectTask: false +}; -function IonicTask() {} - -IonicTask.prototype = new Task(); - -IonicTask.prototype.run = function run(ionic, argv) { - +function run(ionic, argv) { if (argv.list || argv.l) { - new IonicTemplates().run(ionic); - return; + return templateUtils.listTemplates(); } if (argv._.length < 2) { - return ionic.fail('Invalid command', 'start'); + return appLibUtils.fail('Invalid command', 'start'); } if (argv._[1] === '.') { - log.error('Please name your Ionic project something meaningful other than \'.\''.red); - return; + return log.error('Please name your Ionic project something meaningful other than \'.\''.red); } var promptPromise; - var options = Utils.preprocessCliOptions(argv); + var options = appLibUtils.preprocessCliOptions(argv); var startingApp = true; // Grab the app's relative directory name @@ -70,6 +109,8 @@ IonicTask.prototype.run = function run(ionic, argv) { log.error(error); throw error; }); -}; +} -exports.IonicTask = IonicTask; +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/starter-templates.json b/lib/ionic/starter-templates.json deleted file mode 100644 index db0a21623f..0000000000 --- a/lib/ionic/starter-templates.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "total_count": "7", - "incomplete_results": false, - "items": [ - { - "name": "ionic-starter-maps", - "description": "An Ionic starter project using Google Maps and a side menu", - "created_at": "2014-04-28T20:16:01Z", - "updated_at": "2015-01-14T19:14:08Z", - "pushed_at": "2014-09-03T07:20:28Z", - "git_url": "git://github.com/driftyco/ionic-starter-maps.git" - }, - { - "name": "ionic-starter-tabs", - "description": "A starting project for Ionic using a simple tabbed interface", - "created_at": "2014-03-21T17:56:04Z", - "updated_at": "2015-01-11T16:54:23Z", - "pushed_at": "2014-12-29T21:09:20Z", - "git_url": "git://github.com/driftyco/ionic-starter-tabs.git" - }, - { - "name": "ionic-starter-sidemenu", - "description": "A starting project for Ionic using a side menu with navigation in the content area", - "created_at": "2014-03-24T22:31:35Z", - "updated_at": "2015-01-12T06:33:31Z", - "pushed_at": "2014-12-15T20:47:21Z", - "git_url": "git://github.com/driftyco/ionic-starter-sidemenu.git" - }, - { - "id": 17983811, - "name": "ionic-starter-blank", - "description": "A blank starter project for Ionic", - "created_at": "2014-03-21T15:14:45Z", - "updated_at": "2014-12-10T23:36:01Z", - "pushed_at": "2014-05-14T15:26:21Z", - "git_url": "git://github.com/driftyco/ionic-starter-blank.git" - }, - { - "id": 24473944, - "name": "ionic-starter-salesforce", - "description": "A starter project for Ionic and Salesforce", - "created_at": "2014-09-25T20:22:13Z", - "updated_at": "2015-01-06T10:08:01Z", - "pushed_at": "2014-11-06T18:57:36Z", - "git_url": "git://github.com/driftyco/ionic-starter-salesforce.git" - }, - { - "id": 24610601, - "name": "ionic-starter-tests", - "description": "A test of different kinds of page navigation", - "created_at": "2014-09-29T19:57:25Z", - "updated_at": "2015-01-17T17:12:18Z", - "pushed_at": "2015-01-17T17:12:17Z", - "git_url": "git://github.com/driftyco/ionic-starter-tests.git" - }, - { - "id": 27608851, - "name": "ionic-starter-complex-list", - "description": "A complex list starter template", - "created_at": "2014-12-05T20:29:34Z", - "updated_at": "2014-12-06T05:12:52Z", - "pushed_at": "2014-12-05T20:29:34Z", - "git_url": "git://github.com/driftyco/ionic-starter-complex-list.git" - } - ] -} diff --git a/lib/ionic/state.js b/lib/ionic/state.js index 809bb467ba..c5bbf2072e 100644 --- a/lib/ionic/state.js +++ b/lib/ionic/state.js @@ -1,27 +1,47 @@ +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle var shelljs = require('shelljs'); -var Task = require('./task').Task; var IonicAppLib = require('ionic-app-lib'); var IonicProject = IonicAppLib.project; var State = IonicAppLib.state; var log = IonicAppLib.logging.logger; - -// var State = module.exports; +var appLibUtils = IonicAppLib.utils; shelljs.config.silent = true; -function IonicTask() {} - -IonicTask.prototype = new Task(); +var settings = { + title: 'state', + name: 'state', + summary: 'Saves or restores state of your Ionic Application using the package.json file', + args: { + '': '[ save | restore | clear | reset ]' + }, + options: { + save: 'Save the platforms and plugins into package.json', + restore: 'Restore the platforms and plugins from package.json', + clear: 'Clear the package.json of cordovaPlugins and cordovaPlatforms, ' + + 'as well as clear out the platforms and plugins folders', + reset: 'Clear out the platforms and plugins directories, and reinstall plugins and platforms', + '--plugins': { + title: 'Only do operations with plugins', + boolean: true + }, + '--platforms': { + title: 'Only do operations with platforms', + boolean: true + } + }, + isProjectTask: true +}; -IonicTask.prototype.run = function run(ionic, argv) { +function run(ionic, argv) { var options = { platforms: true, plugins: true }; - this.ionic = ionic; - try { IonicProject.load(); } catch (ex) { - this.ionic.fail(ex.message); + appLibUtils.fail(ex.message); return; } @@ -46,6 +66,8 @@ IonicTask.prototype.run = function run(ionic, argv) { default: log.error('Please specify a command [ save | restore | reset | clear ] for the state command.'); } -}; +} -exports.IonicTask = IonicTask; +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/stats.js b/lib/ionic/stats.js deleted file mode 100644 index f5cf1b2884..0000000000 --- a/lib/ionic/stats.js +++ /dev/null @@ -1,657 +0,0 @@ -/* eslint-disable camelcase, no-underscore-dangle */ - -var path = require('path'); -var http = require('http'); -var _ = require('underscore'); -var querystring = require('querystring'); -var Buffer = require('buffer').Buffer; -var util = require('util'); -var IonicConfig = require('ionic-app-lib').config; -var IonicInfo = require('ionic-app-lib').info; - -exports.getVersion = function getVersion() { - var packageJson = require('../../package.json'); - return packageJson; -}; - -/** - * Heavily inspired by the original js library copyright Mixpanel, Inc. - * (http://mixpanel.com/) - * - * Copyright (c) 2012 Carl Sverre - * - * Released under the MIT license. - */ - -function create_client(token, config) { - var metrics = {}; - - if (!token) { - throw new Error('The Mixpanel Client needs a Mixpanel token: `init(token)`'); - } - - metrics.config = { - test: false, - debug: false, - verbose: false - }; - - metrics.token = token; - - /** - * send_request(data) - * --- - * this function sends an async GET request to mixpanel - * - * data:object the data to send in the request - * callback:function(err:Error) callback is called when the request is - * finished or an error occurs - */ - metrics.send_request = function(endpoint, data, callback) { - callback = callback || function() {}; - var event_data = new Buffer(JSON.stringify(data)); - var request_data = { - data: event_data.toString('base64'), - ip: 0, - verbose: metrics.config.verbose ? 1 : 0 - }; - - if (endpoint === '/import') { - var key = metrics.config.key; - if (!key) { - throw new Error('The Mixpanel Client needs a Mixpanel api key when importing old events: ' + - ' `init(token, { key: ... })`'); - } - request_data.api_key = key; - } - - var request_options = { - host: 'api.mixpanel.com', - port: 80, - headers: {} - }; - - if (metrics.config.test) { request_data.test = 1; } - - var query = querystring.stringify(request_data); - - request_options.path = [endpoint, '?', query].join(''); - - http.get(request_options, function(res) { - var data = ''; - res.on('data', function(chunk) { - data += chunk; - }); - - res.on('end', function() { - var e; - - if (metrics.config.verbose) { - try { - var result = JSON.parse(data); - if (result.status !== 1) { - e = new Error('Mixpanel Server Error: ' + result.error); - } - } catch (ex) { - e = new Error('Could not parse response from Mixpanel'); - } - } else { - e = (data !== '1') ? new Error('Mixpanel Server Error: ' + data) : undefined; - } - - callback(e); - }); - }).on('error', function(e) { - if (metrics.config.debug) { - console.log('Got Error: ' + e.message); - } - callback(e); - }); - }; - - /** - * track(event, properties, callback) - * --- - * this function sends an event to mixpanel. - * - * event:string the event name - * properties:object additional event properties to send - * callback:function(err:Error) callback is called when the request is - * finished or an error occurs - */ - metrics.track = function(event, properties, callback) { - if (typeof(properties) === 'function' || !properties) { - callback = properties; - properties = {}; - } - - // if properties.time exists, use import endpoint - var endpoint = (typeof(properties.time) === 'number') ? '/import' : '/track'; - - properties.token = metrics.token; - properties.mp_lib = 'node'; - - var data = { - event: event, - properties: properties - }; - - if (metrics.config.debug) { - console.log('Sending the following event to Mixpanel:'); - console.log(data); - } - - metrics.send_request(endpoint, data, callback); - }; - - /** - * import(event, properties, callback) - * --- - * This function sends an event to mixpanel using the import - * endpoint. The time argument should be either a Date or Number, - * and should signify the time the event occurred. - * - * It is highly recommended that you specify the distinct_id - * property for each event you import, otherwise the events will be - * tied to the IP address of the sending machine. - * - * For more information look at: - * https://mixpanel.com/docs/api-documentation/importing-events-older-than-31-days - * - * event:string the event name - * time:date|number the time of the event - * properties:object additional event properties to send - * callback:function(err:Error) callback is called when the request is - * finished or an error occurs - */ - metrics.import = function(event, time, properties, callback) { - if (typeof(properties) === 'function' || !properties) { - callback = properties; - properties = {}; - } - - if (time === void 0) { - throw new Error('The import method requires you to specify the time of the event'); - } else if (Object.prototype.toString.call(time) === '[object Date]') { - time = Math.floor(time.getTime() / 1000); - } - - properties.time = time; - - metrics.track(event, properties, callback); - }; - - /** - * alias(distinct_id, alias) - * --- - * This function creates an alias for distinct_id - * - * For more information look at: - * https://mixpanel.com/docs/integration-libraries/using-mixpanel-alias - * - * distinct_id:string the current identifier - * alias:string the future alias - */ - metrics.alias = function(distinct_id, alias, callback) { - var properties = { - distinct_id: distinct_id, - alias: alias - }; - - metrics.track('$create_alias', properties, callback); - }; - - metrics.people = { - - /** people.set_once(distinct_id, prop, to, callback) - * --- - * The same as people.set but in the words of mixpanel: - * mixpanel.people.set_once - * - * " This method allows you to set a user attribute, only if - * it is not currently set. It can be called multiple times - * safely, so is perfect for storing things like the first date - * you saw a user, or the referrer that brought them to your - * website for the first time. " - * - */ - set_once: function(distinct_id, prop, to, callback) { - var $set = {}; - - if (typeof(prop) === 'object') { - callback = to; - $set = prop; - } else { - $set[prop] = to; - } - - this._set(distinct_id, $set, callback, { set_once: true }); - }, - - /** - * people.set(distinct_id, prop, to, callback) - * --- - * set properties on an user record in engage - * - * usage: - * - * mixpanel.people.set('bob', 'gender', 'm'); - * - * mixpanel.people.set('joe', { - * 'company': 'acme', - * 'plan': 'premium' - * }); - */ - set: function(distinct_id, prop, to, callback) { - var $set = {}; - - if (typeof(prop) === 'object') { - callback = to; - $set = prop; - } else { - $set[prop] = to; - } - - this._set(distinct_id, $set, callback); - }, - - // used internally by set and set_once - _set: function(distinct_id, $set, callback, options) { - var set_key = (options && options.set_once) ? '$set_once' : '$set'; - - var data = { - $token: metrics.token, - $distinct_id: distinct_id - }; - data[set_key] = $set; - - if ('ip' in $set) { - data.$ip = $set.ip; - delete $set.ip; - } - - if ($set.$ignore_time) { - data.$ignore_time = $set.$ignore_time; - delete $set.$ignore_time; - } - - if (metrics.config.debug) { - console.log('Sending the following data to Mixpanel (Engage):'); - console.log(data); - } - - metrics.send_request('/engage', data, callback); - }, - - /** - * people.increment(distinct_id, prop, to, callback) - * --- - * increment/decrement properties on an user record in engage - * - * usage: - * - * mixpanel.people.increment('bob', 'page_views', 1); - * - * // or, for convenience, if you're just incrementing a counter by 1, you can - * // simply do - * mixpanel.people.increment('bob', 'page_views'); - * - * // to decrement a counter, pass a negative number - * mixpanel.people.increment('bob', 'credits_left', -1); - * - * // like mixpanel.people.set(), you can increment multiple properties at once: - * mixpanel.people.increment('bob', { - * counter1: 1, - * counter2: 3, - * counter3: -2 - * }); - */ - increment: function(distinct_id, prop, by, callback) { - var $add = {}; - - if (typeof(prop) === 'object') { - callback = by; - Object.keys(prop).forEach(function(key) { - var val = prop[key]; - - if (isNaN(parseFloat(val))) { - if (metrics.config.debug) { - console.error('Invalid increment value passed to mixpanel.people.increment - must be a number'); - console.error('Passed ' + key + ':' + val); - } - return; - } else { - $add[key] = val; - } - }); - } else { - if (!by) { by = 1; } - $add[prop] = by; - } - - var data = { - $add: $add, - $token: metrics.token, - $distinct_id: distinct_id - }; - - if (metrics.config.debug) { - console.log('Sending the following data to Mixpanel (Engage):'); - console.log(data); - } - - metrics.send_request('/engage', data, callback); - }, - - /** - * people.track_charge(distinct_id, amount, properties, callback) - * --- - * Record that you have charged the current user a certain - * amount of money. - * - * usage: - * - * // charge a user $29.99 - * mixpanel.people.track_charge('bob', 29.99); - * - * // charge a user $19 on the 1st of february - * mixpanel.people.track_charge('bob', 19, { '$time': new Date('feb 1 2012') }); - */ - track_charge: function(distinct_id, amount, properties, callback) { - - if (!properties) { properties = {}; } - - if (typeof(amount) !== 'number') { - amount = parseFloat(amount); - if (isNaN(amount)) { - console.error('Invalid value passed to mixpanel.people.track_charge - must be a number'); - return; - } - } - - properties.$amount = amount; - - if (properties.hasOwnProperty('$time')) { - var time = properties.$time; - if (Object.prototype.toString.call(time) === '[object Date]') { - properties.$time = time.toISOString(); - } - } - - var data = { - $append: { - $transactions: properties - }, - $token: metrics.token, - $distinct_id: distinct_id - }; - - if (metrics.config.debug) { - console.log('Sending the following data to Mixpanel (Engage):'); - console.log(data); - } - - metrics.send_request('/engage', data, callback); - }, - - /** - * people.clear_charges(distinct_id, callback) - * --- - * Clear all the current user's transactions. - * - * usage: - * - * mixpanel.people.clear_charges('bob'); - */ - clear_charges: function(distinct_id, callback) { - var data = { - $set: { - $transactions: [] - }, - $token: metrics.token, - $distinct_id: distinct_id - }; - - if (metrics.config.debug) { - console.log("Clearing this user's charges:", distinct_id); - } - - metrics.send_request('/engage', data, callback); - }, - - /** - * people.delete_user(distinct_id, callback) - * --- - * delete an user record in engage - * - * usage: - * - * mixpanel.people.delete_user('bob'); - */ - delete_user: function(distinct_id, callback) { - var data = { - $delete: distinct_id, - $token: metrics.token, - $distinct_id: distinct_id - }; - - if (metrics.config.debug) { - console.log('Deleting the user from engage:', distinct_id); - } - - metrics.send_request('/engage', data, callback); - }, - - /** - * people.unset(distinct_id, prop, callback) - * --- - * delete a property on an user record in engage - * - * usage: - * - * mixpanel.people.unset('bob', 'page_views'); - * - * mixpanel.people.unset('bob', ['page_views', 'last_login']); - */ - unset: function(distinct_id, prop, callback) { - var $unset = []; - - if (util.isArray(prop)) { - $unset = prop; - } else if (typeof(prop) === 'string') { - $unset = [prop]; - } else { - if (metrics.config.debug) { - console.error('Invalid argument passed to mixpanel.people.unset - must be a string or array'); - console.error('Passed: ' + prop); - } - return; - } - - var data = { - $unset: $unset, - $token: metrics.token, - $distinct_id: distinct_id - }; - - if (metrics.config.debug) { - console.log('Sending the following data to Mixpanel (Engage):'); - console.log(data); - } - - metrics.send_request('/engage', data, callback); - } - }; - - /** - * set_config(config) - * --- - * Modifies the mixpanel config - * - * config:object an object with properties to override in the - * mixpanel client config - */ - metrics.set_config = function(config) { - for (var c in config) { - if (config.hasOwnProperty(c)) { - metrics.config[c] = config[c]; - } - } - }; - - if (config) { - metrics.set_config(config); - } - - return metrics; -} - -// module exporting -/* -module.exports = { - Client: function(token) { - console.warn("The function `Client(token)` is deprecated. It is now called `init(token)`."); - return create_client(token); - }, - init: create_client -}; -*/ - -var mixpanel = create_client('69f7271aa8f3d43f2e1b6baf698159b7'); - -var ionicConfig = IonicConfig.load(); - - -exports.IonicStats = { - t: function(additionalData) { - try { - - var packageJson = exports.getVersion(); - - if (process.argv.length < 3) return; - - if (ionicConfig.get('statsOptOut') === true) { - return; - } - - var cmdName = process.argv[2].toLowerCase(); - var cmdArgs = (process.argv.length > 3 ? process.argv.slice(3) : []); // skip the cmdName - - var statsData = additionalData || {}; - var platforms = []; - var x; - var y; - var cmd; - - // update any aliases with the full cmd so there's a common property - // TODO: This probably does not work correctly - var aliasMap = { - rm: 'remove', - ls: 'list', - up: 'update', - '-w': '--no-cordova', - '-b': '--nobrowser', - '-r': '--nolivereload', - '-x': '--noproxy', - '-l': '--livereload', - '-c': '--consolelogs', - '-s': '--serverlogs', - '-n': '--no-email' - }; - for (x = 0; x < cmdArgs.length; x += 1) { - for (y in aliasMap) { - if (cmdArgs[x].toLowerCase() === y) { - cmdArgs[x] = aliasMap[y]; - } - } - } - - var platformWhitelist = 'android ios firefoxos wp7 wp8 amazon-fireos blackberry10 tizen'.split(' '); - var argsWhitelist = 'add remove list update check debug release search --livereload ' + - '--consolelogs --serverlogs --no-cordova --nobrowser --nolivereload --noproxy --no-email ' + - '--debug --release --device --emulator --sass --splash --icon --v2 --ts'.split(' '); - - // collect only certain args, skip over everything else - for (x = 0; x < cmdArgs.length; x += 1) { - cmd = cmdArgs[x].toLowerCase(); - - // gather what platforms this is targeting - for (y = 0; y < platformWhitelist.length; y += 1) { - if (cmd === platformWhitelist[y]) { - platforms.push(cmd); // group them together - statsData[cmd] = true; // also give them their own property - break; - } - } - - // gather only args that are in our list of valid stat args - for (y = 0; y < argsWhitelist.length; y += 1) { - if (cmd === argsWhitelist[y]) { - statsData[cmd] = true; - break; - } - } - } - - // create a platform property only when there is 1 or more - if (platforms.length) { - statsData.platform = platforms.sort().join(','); - } - - // add which ionic lib version they're using - try { - statsData.ionic_version = require(path.resolve('www/lib/ionic/version.json')).version; - } catch (e2) {} // eslint-disable-line no-empty - - // add which cli version is being used - try { - statsData.cli_version = packageJson.version; - } catch (e2) {} // eslint-disable-line no-empty - - try { - statsData.email = ionicConfig.get('email'); - } catch (e2) {} // eslint-disable-line no-empty - - try { - statsData.account_id = ionicConfig.get('id'); - } catch (e2) {} // eslint-disable-line no-empty - - var info = {}; - IonicInfo.getNodeVersion(info); - IonicInfo.getOsEnvironment(info); - IonicInfo.gatherGulpInfo(info); - - this.mp(cmdName, _.extend({}, statsData, { - os: info.os, - gulp: info.gulp, - node: info.node - })); - - } catch (e) { - console.log(('Error stats: ' + e)); - } - }, - mp: function(e, d) { - var unique_id = ionicConfig.get('ank'); - if (!unique_id) { - this.createId(); - unique_id = ionicConfig.get('ank'); - } - d.distinct_id = unique_id; - mixpanel.track(e, d, function(err) { - if (err) { - console.error(err); - } - }); - }, - createId: function() { - var d = new Date().getTime(); - var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - var r = (d + Math.random() * 16) % 16 | 0; - d = Math.floor(d / 16); - return (c === 'x' ? r : (r & 0x7 | 0x8)).toString(16); - }); - - ionicConfig.set('ank', uuid); - } -}; diff --git a/lib/ionic/task.js b/lib/ionic/task.js deleted file mode 100644 index 7f6ef25c85..0000000000 --- a/lib/ionic/task.js +++ /dev/null @@ -1,7 +0,0 @@ -function Task() {} - -Task.prototype = { - run: function() {} -}; - -exports.Task = Task; diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index d1a2ed03ef..5ac346937d 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -1,16 +1,27 @@ +'use strict'; + +var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle var IonicAppLib = require('ionic-app-lib'); var Login = IonicAppLib.login; var LoginTask = require('./login'); -var Task = require('./task').Task; var Upload = IonicAppLib.upload; var log = IonicAppLib.logging.logger; -var Utils = IonicAppLib.utils; - -function IonicTask() {} +var appLibUtils = IonicAppLib.utils; -IonicTask.prototype = new Task(); +var settings = { + title: 'upload', + name: 'upload', + summary: 'Upload an app to your Ionic account', + options: { + '--email|-e': 'Ionic account email', + '--password|-p': 'Ionic account password', + '--note': 'The note to signify the upload', + '--deploy ': 'Deploys the upload to the given channel. Defaults to the Dev channel' + }, + isProjectTask: true +}; -IonicTask.prototype.run = function run(ionic, argv) { +function run(ionic, argv) { var note = argv.note; var deploy = argv.deploy || false; @@ -26,10 +37,10 @@ IonicTask.prototype.run = function run(ionic, argv) { return Upload.doUpload(process.cwd(), jar, note, deploy); }) .catch(function(ex) { - - // log.info('Error', ex, ex.stack); - Utils.fail(ex); + appLibUtils.fail(ex); }); -}; +} -exports.IonicTask = IonicTask; +module.exports = extend(settings, { + run: run +}); diff --git a/lib/ionic/utils.js b/lib/ionic/utils.js deleted file mode 100644 index 8baec0f1df..0000000000 --- a/lib/ionic/utils.js +++ /dev/null @@ -1,46 +0,0 @@ -function transformCookies(jar) { - return jar.map(function(c) { - return c.key + '=' + encodeURIComponent(c.value); - }).join('; '); -} - -function retrieveCsrfToken(jar) { - - // console.log('retrieveCsrfToken', jar) - if (!jar || typeof jar == 'undefined' || jar.length === 0) { - - // console.log('no jar folks') - return ''; - } - var csrftoken = ''; - for (var i = 0; i < jar.length; i += 1) { - if (jar[i].key === 'csrftoken') { - csrftoken = jar[i].value; - break; - } - } - return csrftoken; -} - -function deleteFolderRecursive(removePath) { - var fs = require('fs'); - var path = require('path'); - - if (fs.existsSync(removePath)) { - fs.readdirSync(removePath).forEach(function(file) { - var curPath = path.join(removePath, file); - if (fs.lstatSync(curPath).isDirectory()) { // recurse - deleteFolderRecursive(curPath); - } else { // delete file - fs.unlinkSync(curPath); - } - }); - fs.rmdirSync(removePath); - } -} - -module.exports = { - deleteFolderRecursive: deleteFolderRecursive, - retrieveCsrfToken: retrieveCsrfToken, - transformCookies: transformCookies -}; diff --git a/lib/tasks/cliTasks.js b/lib/tasks/cliTasks.js deleted file mode 100644 index bc6c5c0aa2..0000000000 --- a/lib/tasks/cliTasks.js +++ /dev/null @@ -1,528 +0,0 @@ -var resourcesSummary = [ - 'Automatically create icon and splash screen resources' + ' (beta)'.yellow, - 'Put your images in the ./resources directory, named splash or icon.', - 'Accepted file types are .png, .ai, and .psd.', - 'Icons should be 192x192 px without rounded corners.', - 'Splashscreens should be 2208x2208 px, with the image centered in the middle.\n' -].join('\n\t\t '); - -var cordovaRunEmulateOptions = { - '--livereload|-l': 'Live reload app dev files from the device' + ' (beta)'.yellow, - '--address': 'Use specific address (livereload req.)', - '--port|-p': 'Dev server HTTP port (8100 default, livereload req.)', - '--livereload-port|-r': 'Live Reload port (35729 default, livereload req.)', - '--consolelogs|-c': { - title: 'Print app console logs to Ionic CLI (livereload req.)', - boolean: true - }, - '--serverlogs|-s': { - title: 'Print dev server logs to Ionic CLI (livereload req.)', - boolean: true - }, - '--debug|--release': { - title: '', - boolean: true - }, - '--device|--emulator|--target=FOO': '' -}; - -var TASKS = [ - { - title: 'start', - name: 'start', - summary: 'Starts a new Ionic project in the specified PATH', - args: { - '[options]': 'any flags for the command', - '': 'directory for the new project', - '[template]': 'Starter templates can either come from a named template, \n' + - '(ex: tabs, sidemenu, blank),\n' + - 'a Github repo, a Codepen url, or a local directory.\n' + - 'Codepen url, ex: http://codepen.io/ionic/pen/odqCz\n' + - 'Defaults to Ionic "tabs" starter template' - }, - options: { - '--appname|-a': 'Human readable name for the app (Use quotes around the name)', - '--id|-i': 'Package name for config, ex: com.mycompany.myapp', - '--skip-npm': { - title: 'Skip npm package installation', - boolean: true - }, - '--no-cordova|-w': { - title: 'Create a basic structure without Cordova requirements', - boolean: true - }, - '--sass|-s': { - title: 'Setup the project to use Sass CSS precompiling', - boolean: true - }, - '--list|-l': { - title: 'List starter templates available', - boolean: true - }, - '--io-app-id': 'The Ionic.io app ID to use', - '--template|-t': 'Project starter template', - '--v2|-v': { - boolean: true, - title: 'Start a Ionic v2 project' - }, - '--typescript|--ts': { - boolean: true, - title: '(with --v2 only) Use TypeScript in starter' - }, - '--zip-file|-z': 'URL to download zipfile for starter template' - }, - module: './ionic/start', - disableChangePwd: true - }, - { - title: 'serve', - name: 'serve', - summary: 'Start a local development server for app dev/testing', - args: { - '[options]': '' - }, - options: { - '--consolelogs|-c': { - title: 'Print app console logs to Ionic CLI', - boolean: true - }, - '--serverlogs|-s': { - title: 'Print dev server logs to Ionic CLI', - boolean: true - }, - '--port|-p': 'Dev server HTTP port (8100 default)', - '--livereload-port|-r': 'Live Reload port (35729 default)', - '--nobrowser|-b': { - title: 'Disable launching a browser', - boolean: true - }, - '--nolivereload|-d': { - title: 'Do not start live reload', - boolean: true - }, - '--noproxy|-x': { - title: 'Do not add proxies', - boolean: true - }, - '--address': 'Use specific address or return with failure', - '--all|-a': { - title: 'Have the server listen on all addresses (0.0.0.0)', - boolean: true - }, - '--browser|-w': 'Specifies the browser to use (safari, firefox, chrome)', - '--browseroption|-o': 'Specifies a path to open to (/#/tab/dash)', - '--lab|-l': { - title: 'Test your apps on multiple screen sizes and platform types', - boolean: true - }, - '--nogulp': { - title: 'Disable running gulp during serve', - boolean: true - }, - '--platform|-t': 'Start serve with a specific platform (ios/android)' - }, - module: './ionic/serve' - }, - { - title: 'platform', - name: 'platform', - summary: 'Add platform target for building an Ionic app', - args: { - '[options]': '', - '': '' - }, - options: { - '--noresources|-r': { - title: 'Do not add default Ionic icons and splash screen resources', - boolean: true - }, - '--nosave|-e': { - title: 'Do not save the platform to the package.json file', - boolean: true - } - }, - module: './ionic/cordova', - alt: ['platforms'] - }, - { - title: 'run', - name: 'run', - summary: 'Run an Ionic project on a connected device', - args: { - '[options]': '', - '': '' - }, - options: cordovaRunEmulateOptions, - module: './ionic/cordova' - }, - { - title: 'emulate', - name: 'emulate', - summary: 'Emulate an Ionic project on a simulator or emulator', - args: { - '[options]': '', - '': '' - }, - options: cordovaRunEmulateOptions, - module: './ionic/cordova' - }, - { - title: 'build', - name: 'build', - summary: 'Build (prepare + compile) an Ionic project for a given platform.\n', - args: { - '[options]': '', - '': '' - }, - options: { - '--nohooks|-n': { - title: 'Do not add default Ionic hooks for Cordova', - boolean: true - } - }, - module: './ionic/cordova' - }, - { - title: 'plugin add', - name: 'plugin', - summary: 'Add a Cordova plugin', - args: { - '[options]': '', - '': 'Can be a plugin ID, a local path, or a git URL.' - }, - options: { - '--searchpath ': 'When looking up plugins by ID, look in this directory\n' + - 'and subdirectories first for the plugin before\n' + - 'looking it up in the registry.', - '--nosave|-e': { - title: 'Do not save the plugin to the package.json file', - boolean: true - } - }, - alt: ['plugins'], - module: './ionic/cordova' - }, - { - title: 'prepare', - name: 'prepare', - module: './ionic/cordova' - }, - { - title: 'compile', - name: 'compile', - module: './ionic/cordova' - }, - { - title: 'resources', - name: 'resources', - summary: resourcesSummary, - options: { - '--icon|-i': { - title: 'Generate icon resources', - boolean: true - }, - '--splash|-s': { - title: 'Generate splash screen resources', - boolean: true - } - }, - module: './ionic/resources' - }, - { - title: 'upload', - name: 'upload', - summary: 'Upload an app to your Ionic account', - options: { - '--email|-e': 'Ionic account email', - '--password|-p': 'Ionic account password', - '--note': 'The note to signify the upload', - '--deploy ': 'Deploys the upload to the given channel. Defaults to the Dev channel' - }, - alt: ['up'], - module: './ionic/upload' - }, - { - title: 'share', - name: 'share', - summary: 'Share an app with a client, co-worker, friend, or customer', - args: { - '': 'The email to share the app with' - }, - module: './ionic/share' - }, - { - title: 'lib', - name: 'lib', - summary: 'Gets Ionic library version or updates the Ionic library', - args: { - '[options]': '', - '[update]': 'Updates the Ionic Framework in www/lib/ionic' - }, - options: { - '--version|-v': 'Specific Ionic version\nOtherwise it defaults to the latest version' - }, - module: './ionic/lib' - }, - { - title: 'setup', - name: 'setup', - summary: 'Configure the project with a build tool ' + '(beta)'.yellow, - args: { - '[sass]': 'Setup the project to use Sass CSS precompiling' - }, - module: './ionic/setup' - }, - { - title: 'login', - name: 'login', - module: './ionic/login' - }, - { - title: 'address', - name: 'address', - module: './ionic/serve' - }, - { - title: 'io', - name: 'io', - summary: 'Integrate your app with the ionic.io platform services ' + '(alpha)'.red, - args: { - '': 'init'.yellow - }, - module: './ionic/io-init' - }, - { - title: 'security', - name: 'security', - summary: 'Store your app\'s credentials for the Ionic Platform ' + '(alpha)'.red, - args: { - '': 'profiles list'.yellow + ', ' + 'profiles add ""'.yellow + ', ' + - 'credentials android'.yellow + ', or ' + 'credentials ios'.yellow, - '[options]': '' - }, - options: { - '--profile ': '(' + 'credentials '.yellow + - ') Specify the profile on which these credentials are saved', - '--keystore|-s ': '(' + 'credentials android'.yellow + - ') Specify the location of your keystore file', - '--keystore-password|-p ': '(' + 'credentials android'.yellow + - ') Specify your keystore password (exclude for prompt)', - '--key-alias|-k ': '(' + 'credentials android'.yellow + - ') Specify your key alias for this app', - '--key-password|-w ': '(' + 'credentials android'.yellow + - ') Specify your key password for this app (exclude for prompt)', - '--cert|-c ': '(' + 'credentials ios'.yellow + - ') Specify the location of your .p12 file', - '--cert-password|-p ': '(' + 'credentials ios'.yellow + - ') Specify your certificate password (exclude for prompt)', - '--provisioning-profile|-r ': '(' + 'credentials ios'.yellow + - ') Specify the location of your .mobileprovision file' - }, - module: './ionic/security' - }, - { - title: 'push', - name: 'push', - summary: 'Upload APNS and GCM credentials to Ionic Push ' + '(alpha)'.red, - options: { - '--ios-dev-cert': 'Upload your development .p12 file to Ionic Push', - '--ios-prod-cert': 'Upload your production .p12 file to Ionic Push', - '--production-mode=y,n': 'Tell Ionic Push to use production (y) or sandbox (n) APNS servers', - '--google-api-key ': "Set your app's GCM API key on Ionic Push" - }, - module: './ionic/push' - }, - { - title: 'package', - name: 'package', - summary: 'Use Ionic Package to build your app ' + '(alpha)'.red, - args: { - '': 'build android'.yellow + ', ' + 'build ios'.yellow + - ', ' + 'list'.yellow + ', ' + 'info'.yellow + ', or ' + 'download'.yellow, - '[options]': '' - }, - options: { - '--release': '(' + 'build '.yellow + - ') Mark this build as a release', - '--profile|-p ': '(' + 'build '.yellow + - ') Specify the Security Profile to use with this build', - '--noresources': '(' + 'build '.yellow + - ') Do not generate icon and splash screen resources during this build', - '--destination|-d ': '(' + 'download'.yellow + - ') Specify the destination directory to download your packaged app.' - }, - module: './ionic/package' - }, - { - title: 'config', - name: 'config', - summary: 'Set configuration variables for your ionic app ' + '(alpha)'.red, - args: { - '': 'set'.yellow + ', ' + 'unset'.yellow + ', ' + 'build'.yellow + ', or ' + 'info'.yellow, - '[key]': 'The key to set', - '[value]': 'The value to set' - }, - module: './ionic/io-config' - }, - { - title: 'browser', - name: 'browser', - summary: 'Add another browser for a platform ' + '(beta)'.yellow, - args: { - '': '"add remove rm info versions upgrade list ls revert"', - '[browser]': 'The browser you wish to add or remove (Crosswalk)' - }, - options: { - '--nosave|-n': { - title: 'Do not save the platforms and plugins to the package.json file', - boolean: true - } - }, - module: './ionic/browser' - }, - { - title: 'service add', - name: 'service', - summary: 'Add an Ionic service package and install any required plugins', - args: { - '[options]': '', - '': 'Can be a service name or a git url' - }, - module: './ionic/service' - }, - { - title: 'add', - name: 'add', - summary: 'Add an Ion, bower component, or addon to the project', - args: { - '[name]': 'The name of the ion, bower component, or addon you wish to install' - }, - module: './ionic/add' - }, - { - title: 'remove', - name: 'remove', - summary: 'Remove an Ion, bower component, or addon from the project', - args: { - '[name]': 'The name of the Ion, bower component, or addon you wish to remove' - }, - module: './ionic/add', - alt: ['rm'] - }, - { - title: 'list', - name: 'list', - summary: 'List Ions, bower components, or addons in the project', - module: './ionic/add', - alt: ['ls'] - }, - - /* - { - title: 'ions', - name: 'ions', - summary: 'List available ions to add to your project', - module: './ionic/ions' - }, - { - title: 'templates', - name: 'templates', - summary: 'List available Ionic starter templates', - module: './ionic/templates' - }, - */ - { - title: 'info', - name: 'info', - summary: 'List information about the users runtime environment', - module: './ionic/info' - }, - { - title: 'help', - name: 'help', - summary: 'Provides help for a certain command', - args: { - '[command]': 'The command you desire help with' - }, - module: './ionic/help', - disableChangePwd: true - }, - { - title: 'link', - name: 'link', - summary: 'Sets your Ionic App ID for your project', - args: { - '[appId]': 'The app ID you wish to set for this project' - }, - options: { - '--reset|-r': { - title: 'This will reset the Ionic App ID', - boolean: true - } - }, - module: './ionic/link' - }, - { - title: 'hooks', - name: 'hooks', - summary: 'Manage your Ionic Cordova hooks', - args: { - '[add|remove|permissions|perm]': 'Add, remove, or modify permissions on the default Ionic Cordova hooks' - }, - module: './ionic/hooks' - }, - { - title: 'state', - name: 'state', - summary: 'Saves or restores state of your Ionic Application using the package.json file', - args: { - '': '[ save | restore | clear | reset ]' - }, - options: { - save: 'Save the platforms and plugins into package.json', - restore: 'Restore the platforms and plugins from package.json', - clear: 'Clear the package.json of cordovaPlugins and cordovaPlatforms, ' + - 'as well as clear out the platforms and plugins folders', - reset: 'Clear out the platforms and plugins directories, and reinstall plugins and platforms', - '--plugins': { - title: 'Only do operations with plugins', - boolean: true - }, - '--platforms': { - title: 'Only do operations with platforms', - boolean: true - } - }, - module: './ionic/state' - }, - { - title: 'docs', - name: 'docs', - summary: 'Opens up the documentation for Ionic', - args: { - '': 'the topic to view help documentation for. Use "ls" to view all topics' - }, - module: './ionic/docs', - disableChangePwd: true - }, - { - title: 'generate', - alt: ['g'], - name: 'generate', - summary: 'Generate pages and components', - module: './ionic/generate', - options: { - '--list': { - title: 'List available generators', - boolean: true - }, - '--typescript|--ts': { - boolean: true, - title: '(with --v2 only) Use TypeScript in generation' - } - } - } -]; - - -module.exports = TASKS; diff --git a/lib/utils/bower.js b/lib/utils/bower.js new file mode 100644 index 0000000000..66c88e967d --- /dev/null +++ b/lib/utils/bower.js @@ -0,0 +1,67 @@ +'use strict'; + +require('colors'); + +var fs = require('fs'); +var path = require('path'); +var childProcess = require('child_process'); +var IonicAppLib = require('ionic-app-lib'); +var log = IonicAppLib.logging.logger; + +function setIonicVersion(newVersion) { + var bowerData = this.getData(); + if (!bowerData.devDependencies) bowerData.devDependencies = {}; + bowerData.devDependencies.ionic = 'driftyco/ionic-bower#' + newVersion; + this.saveData(bowerData); +} + +function setAppName(newAppName) { + var bowerData = this.getData(); + bowerData.name = newAppName; + this.saveData(bowerData); +} + +function getData() { + var bowerFilePath = path.resolve('bower.json'); + + try { + if (fs.existsSync(bowerFilePath)) { + return JSON.parse(fs.readFileSync(bowerFilePath)); + } + } catch (e) { + throw e; + } + + return { + name: 'HelloIonic', + private: 'true' + }; +} + +function saveData(bowerData) { + try { + var bowerFilePath = path.resolve('bower.json'); + fs.writeFileSync(bowerFilePath, JSON.stringify(bowerData, null, 2)); + } catch (e) { + log.error(('Error saving ' + bowerFilePath + ': ' + e).red.bold); + } +} + +function checkForBower() { + var result = childProcess.exec('bower -v', { silent: true }); + if (result.output.indexOf('command not found') === -1 && result.output.indexOf('not recognized') === -1) { + return true; + } + return false; +} + +var installMessage = 'You must have bower installed to continue. \nType `npm install -g bower`'; + +module.exports = { + setIonicVersion: setIonicVersion, + setAppName: setAppName, + getData: getData, + saveData: saveData, + checkForBower: checkForBower, + installMessage: installMessage +}; diff --git a/lib/utils/cordova.js b/lib/utils/cordova.js new file mode 100644 index 0000000000..3e1b02378e --- /dev/null +++ b/lib/utils/cordova.js @@ -0,0 +1,278 @@ +'use strict'; + +require('colors'); + +var Q = require('q'); +var _ = require('underscore'); +var path = require('path'); +var fs = require('fs'); +var IonicAppLib = require('ionic-app-lib'); +var Serve = IonicAppLib.serve; +var Project = IonicAppLib.project; +var log = IonicAppLib.logging.logger; +var ConfigXml = IonicAppLib.configXml; +var childProcess = require('child_process'); +var childExec = childProcess.exec; +var promiseExec = Q.denodeify(childExec); + +/** + * Returns true or false after checking if the platform exists + * Synchronous + * + * @param {String} platform The platform to check for + * @param {String} baseDir The projects base directory + * @return {Boolean} True if platform is installed + */ +function isPlatformInstalled(platform, baseDir) { + var platformPath = path.join(baseDir, 'platforms', platform); + + try { + fs.statSync(platformPath); + return true; + } catch (ex) { + return false; + } +} + +/** + * Returns true or false after checking if any plugin is installed + * Synchronous + * + * @param {String} baseDir The projects base directory + * @return {Boolean} True if any plugin is installed + */ +function arePluginsInstalled(baseDir) { + var pluginPath = path.join(baseDir, 'plugins'); + + try { + fs.statSync(pluginPath); + return true; + } catch (ex) { + return false; + } +} + +/** + * Install the platform specified using cordova + * + * @param {String} platform The platform to install (ios, android, etc.) + * @return {Promise} Promise upon completion + */ +function installPlatform(platform) { + log.info(('• You\'re trying to build for ' + platform + 'but don\'t have the platform installed yet.').yellow); + log.info('∆ Installing ' + platform + ' for you.'); + + return promiseExec('cordova platform add ' + platform).then(function() { + log.info('√ Installed platform ' + platform); + }); +} + +function execCordovaCommand(optionList, isLiveReload, serveOptions) { + var deferred = Q.defer(); + isLiveReload = !!isLiveReload; + + log.debug('Executing cordova cli: ' + optionList.join(' ')); + var cordovaProcess = childProcess.exec('cordova ' + optionList.join(' ')); + + cordovaProcess.stdout.on('data', function(data) { + log.info(data); + }); + + cordovaProcess.stderr.on('data', function(data) { + if (data) { + log.error(data.toString().bold); + } + }); + + cordovaProcess.on('close', function(code) { + if (code > 0) { + return deferred.reject(code); + } + return deferred.resolve(); + }); + + if (isLiveReload) { + cordovaProcess.on('exit', function() { + + Serve.printCommandTips(serveOptions); + setTimeout(function() { + + // set it back to the original src after a few seconds + ConfigXml.setConfigXml(process.cwd(), { + resetContent: true, + errorWhenNotFound: true + }); + + // deferred.resolve(); + }, 5000); + }); + + process.on('exit', function() { + + // verify it was set back + ConfigXml.setConfigXml(process.cwd(), { + resetContent: true, + errorWhenNotFound: false + }); + }); + + var readLine = require('readline'); + if (process.platform === 'win32') { + var rl = readLine.createInterface({ + input: process.stdin, + output: process.stdout + }); + + rl.on('SIGINT', function() { + process.emit('SIGINT'); + }); + } + + process.on('SIGINT', function() { + process.exit(); + }); + } + + return deferred.promise; +} + +/** + * Install ionic required plugins + * + * @return {Promise} Promise upon completion + */ +function installPlugins() { + var plugins = [ + 'cordova-plugin-device', + 'cordova-plugin-console', + 'cordova-plugin-whitelist', + 'cordova-plugin-splashscreen', + 'cordova-plugin-statusbar', + 'ionic-plugin-keyboard' + ]; + + return Q.all(plugins.map(function(plugin) { + log.info(['Installing ', plugin].join('')); + return promiseExec('cordova plugin add --save ' + plugin); + })); +} + +/** + * Filter and gather arguments from command line to be passed to Cordova + * + * @param {String} cmdName The command that is being executed (ie run, build, etc) + * @param {Object} argv An optimist object + * @return {Array} Returns a list of commands to use with cordova + */ +function filterArgumentsForCordova(cmdName, argv, rawCliArguments) { + + // clean out any cmds that may confuse cordova + var port = argv.port || argv.p || ''; + var liveReloadPort = argv.livereloadport || argv['livereload-port'] || argv.r || ''; + var ignoreCmds = [ + '--livereload', '-l', + '--consolelogs', '-c', + '--serverlogs', '-s', + '--port', '-p', + '--livereload-port', + '-i', '-r' + ]; + + return rawCliArguments.filter(function(arg, index, fullList) { + + // Remove address parameter and the param that follows it + if (arg === '--address' || fullList[index - 1] === '--address') { return false; } + + // If arg is equal to what we identifed as the port reject it + if (port && parseInt(arg, 10) === parseInt(port, 10)) { return false; } + + // If arg is equal to what we identifed as the liveReloadPort reject it + if (liveReloadPort && parseInt(arg, 10) === parseInt(liveReloadPort, 10)) { return false; } + + // Only return true if the arg is not found in the ignore Cmds list + return ignoreCmds.indexOf(arg) === -1; + }).map(function(arg) { + + // If the arg is the target command and it does not contain double quotes add them + // because process.argv removes them + if (arg.indexOf('--target=') === 0 && arg.indexOf('"') === -1) { + return arg.replace('--target=', '--target="') + '"'; + } + return arg; + }); +} + +/** + * Setup the live reload server for ionic + * + * @param {Array} argv List of arguments + * @param {String} baseDir The projects base directory + * @return {Promise} Promise upon completion + */ +function setupLiveReload(argv, baseDir) { + log.info(('Setup Live Reload').green.bold); + + var project = Project.load(baseDir); + var options = _.extend(Serve.loadSettings(argv, project), { + appDirectory: baseDir, + runLivereload: true, + launchBrowser: false, + launchLab: false, + isPlatformServe: true + }); + + + // First ask user for the IP selection + // Check ports not used + // Set up config.xml src url + // run the cordova command + + var promises = []; + + if (argv.all) { + log.info('Defaulting address to 0.0.0.0'); + options.address = '0.0.0.0'; + } else if (argv.address) { + options.address = argv.address; + } else { + promises = promises.concat(Serve.getAddress(options)); + } + + return Q.all(promises) + .then(function() { + options.devServer = Serve.host(options.address, options.port); + return Serve.checkPorts(true, options.port, options.address, options); + }) + .then(function() { + if (options.runLivereload) { + return Serve.checkPorts(false, options.liveReloadPort, options.address, options); + } + }) + .then(function() { + return ConfigXml.setConfigXml(process.cwd(), { + devServer: Serve.host(options.address, options.port) + }); + }) + .then(function() { + return Serve.start(options); + }) + .then(function() { + Serve.showFinishedServeMessage(options); + return options; + }) + .catch(function(error) { + log.info('There was an error serving your Ionic application for run', error); + log.info(error.stack); + throw error; + }); +} + +module.exports = { + isPlatformInstalled: isPlatformInstalled, + arePluginsInstalled: arePluginsInstalled, + installPlatform: installPlatform, + installPlugins: installPlugins, + execCordovaCommand: execCordovaCommand, + filterArgumentsForCordova: filterArgumentsForCordova, + setupLiveReload: setupLiveReload +}; diff --git a/lib/utils/help.js b/lib/utils/help.js new file mode 100644 index 0000000000..144adbdbc2 --- /dev/null +++ b/lib/utils/help.js @@ -0,0 +1,243 @@ +'use strict'; + +var IonicAppLib = require('ionic-app-lib'); +var log = IonicAppLib.logging.logger; +require('colors'); + +/** + * @method printIonic + * @return {Array} Returns array of lines to print for ionic name + */ +function printIonic(ionicVersion) { + return [ + ' _ _ ', + ' (_) (_) ', + ' _ ___ _ __ _ ___ ', + ' | |/ _ \\| \'_ \\| |/ __| ', + ' | | (_) | | | | | (__ ', + ' |_|\\___/|_| |_|_|\\___| CLI v' + ionicVersion + ]; +} + + +/** + * @method printTemplate + * @param {Array} lineArray list of lines to print + * @return {Null} no return value + */ +function printTemplate(lineArray) { + log.info(lineArray.reduce(function(all, line) { + all += (line !== null) ? line + '\n' : ''; + return all; + }, '')); +} + + +/** + * @method printAvailableTasks + * @param {String} taskName task name supplied to cli + * @return {Null} no return value + */ +function printTaskListShortUsage(taskList, taskName, ionicVersion) { + var taskLines = taskList + .filter(function(task) { + return task.summary; + }) + .map(function(task) { + var name = ' ' + task.name + ' '; + var dots = ''; + while ((name + dots).length < 20) { + dots += '.'; + } + return name.green.bold + dots.grey + ' ' + task.summary.bold; + }); + + var lines = [].concat( + printIonic(ionicVersion), + [ + '', + 'Usage: ionic task args', + '', + '', + '=======================', + (taskName ? + (taskName + ' is not a valid task\n\n').bold.red + : null), + 'Available tasks: '.bold, + '(use --help or -h for more info)', + '', + '' + ], + taskLines + ); + + printTemplate(lines); +} + + +/** + * @method printTaskUsage + * @return {Null} no return value + */ +function printTaskUsage(task, ionicVersion) { + var lines = [].concat( + printIonic(ionicVersion), + [ + '', + '=======================', + '' + ] + ); + printTemplate(lines); + + printTaskDetails(task); + + printTemplate([ + '' + ]); +} + + +/** + * @method printAllTaskUsage + * @return {Null} no return value + */ +function printTaskListUsage(taskList, ionicVersion) { + var lines = [].concat( + printIonic(ionicVersion), + [ + '', + '=======================', + '' + ] + ); + printTemplate(lines); + + taskList + .filter(function(task) { + return task.summary; + }).forEach(function(task) { + return printTaskDetails(task); + }); + + printTemplate([ + '' + ]); +} + + +/** + * @method printTaskDetails + * @return {Null} no return value + */ +function printTaskDetails(d) { + function w(s) { + process.stdout.write(s); + } + + w('\n'); + + var rightColumn = 45; + var dots = ''; + var indent = ''; + var x; + var arg; + + var taskArgs = d.title; + + for (arg in d.args) { + if ({}.hasOwnProperty.call(d.args, arg)) { + taskArgs += ' ' + arg; + } + } + + w(taskArgs.green.bold); + + while ((taskArgs + dots).length < rightColumn + 1) { + dots += '.'; + } + + w(' ' + dots.grey + ' '); + + if (d.summary) { + w(d.summary.bold); + } + + for (arg in d.args) { + if (!d.args[arg]) continue; + + indent = ''; + w('\n'); + while (indent.length < rightColumn) { + indent += ' '; + } + w((indent + ' ' + arg + ' ').bold); + + var argDescs = d.args[arg].split('\n'); + var argIndent = indent + ' '; + + for (x = 0; x < arg.length + 1; x += 1) { + argIndent += ' '; + } + + for (x = 0; x < argDescs.length; x += 1) { + if (x === 0) { + w(argDescs[x].bold); + } else { + w('\n' + argIndent + argDescs[x].bold); + } + } + } + + indent = ''; + while (indent.length < d.name.length + 1) { + indent += ' '; + } + + var optIndent = indent; + while (optIndent.length < rightColumn + 4) { + optIndent += ' '; + } + + for (var opt in d.options) { + if ({}.hasOwnProperty.call(d.options, opt)) { + w('\n'); + dots = ''; + + var optLine = indent + '[' + opt + '] '; + + w(optLine.yellow.bold); + + if (d.options[opt]) { + while ((dots.length + optLine.length - 2) < rightColumn) { + dots += '.'; + } + w(dots.grey + ' '); + + var taskOpt = d.options[opt]; + var optDescs; + + if (typeof taskOpt == 'string') { + optDescs = taskOpt.split('\n'); + } else { + optDescs = taskOpt.title.split('\n'); + } + for (x = 0; x < optDescs.length; x += 1) { + if (x === 0) { + w(optDescs[x].bold); + } else { + w('\n' + optIndent + optDescs[x].bold); + } + } + } + } + + w('\n'); + } +} + +module.exports = { + printTaskListShortUsage: printTaskListShortUsage, + printTaskListUsage: printTaskListUsage, + printTaskUsage: printTaskUsage +}; diff --git a/lib/ionic/ionitron.js b/lib/utils/ionitron.js similarity index 88% rename from lib/ionic/ionitron.js rename to lib/utils/ionitron.js index 3befddc537..fd630f04e8 100644 --- a/lib/ionic/ionitron.js +++ b/lib/utils/ionitron.js @@ -1,7 +1,10 @@ -require('colors'); +'use strict'; +var colors = require('colors'); var path = require('path'); var fs = require('fs'); +var IonicAppLib = require('ionic-app-lib'); +var log = IonicAppLib.logging.logger; var ionitronStatements = [ 'Hello human, what shall we build today?', @@ -31,11 +34,11 @@ var ionitronStatementsEs = [ 'Bif\u00Farcate! Oh, Lo siento, rama equivocada. ' ]; -var ionictronAsciiFile = 'ionitron.txt'; +var ASCII_ART_FILE = 'ionitron.txt'; -module.exports.print = function print(lang) { +function print(lang) { - var ionitronPath = path.join(__dirname, 'assets', ionictronAsciiFile); + var ionitronPath = path.join(__dirname, ASCII_ART_FILE); var contents = fs.readFileSync(ionitronPath, 'utf8'); var messageContent; @@ -54,19 +57,21 @@ module.exports.print = function print(lang) { var replaceString = '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'; - // console.log(messageContent.length, replaceString.length) if (messageContent.length < replaceString.length) { var diff = (replaceString.length) - messageContent.length; var i = 0; - // console.log(diff, i) while (i < diff) { messageContent = messageContent + ' '; i += 1; } } contents = contents.replace(replaceString, messageContent); - console.log(contents.cyan); + log.info(colors.cyan(contents)); return; +} + +module.exports = { + print: print }; diff --git a/lib/ionic/assets/ionitron.txt b/lib/utils/ionitron.txt similarity index 100% rename from lib/ionic/assets/ionitron.txt rename to lib/utils/ionitron.txt diff --git a/lib/ionic/prompt.js b/lib/utils/prompt.js similarity index 100% rename from lib/ionic/prompt.js rename to lib/utils/prompt.js diff --git a/lib/utils/stats.js b/lib/utils/stats.js new file mode 100644 index 0000000000..2f3f42aeca --- /dev/null +++ b/lib/utils/stats.js @@ -0,0 +1,164 @@ +/* eslint-disable camelcase, no-underscore-dangle */ +'use strict'; + +var request = require('request'); +var IonicConfig = require('ionic-app-lib').config; +var IonicInfo = require('ionic-app-lib').info; +var log = require('ionic-app-lib').logging.logger; +var path = require('path'); +var fs = require('fs'); + +var proxy = process.env.PROXY || process.env.http_proxy || null; + +function track(event, uuid, data, callback) { + data = { + _event : event, + _uuid: uuid, + data: data + }; + + request({ + url: 'https://t.ionic.io/event/cli', + method: 'POST', + json: data, + proxy: proxy + }, function(err, res, body) { + callback(err, res, body); + }); +} + +var ionicConfig = IonicConfig.load(); + +function createId() { + var d = new Date().getTime(); + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = (d + Math.random() * 16) % 16 | 0; + d = Math.floor(d / 16); + return (c === 'x' ? r : (r & 0x7 | 0x8)).toString(16); + }); +} + +// update any aliases with the full cmd so there's a common property +function mapAliases(args) { + var aliasMap = { + rm: 'remove', + ls: 'list', + up: 'update', + '-w': '--no-cordova', + '-b': '--nobrowser', + '-r': '--nolivereload', + '-x': '--noproxy', + '-l': '--livereload', + '-c': '--consolelogs', + '-s': '--serverlogs', + '-n': '--no-email' + }; + + return args.map(function(arg) { + var lowerCaseArg = arg.toLowerCase(); + return (aliasMap[lowerCaseArg]) ? aliasMap[lowerCaseArg] : arg; + }); +} + +function mp(e, d) { + var uniqueId = ionicConfig.get('ank'); + if (!uniqueId) { + uniqueId = createId(); + ionicConfig.set('ank', uniqueId); + } + track(e, uniqueId, d, function(err, data) { // eslint-disable-line no-unused-vars,handle-callback-err + }); +} + +function t(additionalData) { + try { + + if (process.argv.length < 3) return; + + if (ionicConfig.get('statsOptOut') === true) { + return; + } + + var cmdName = process.argv[2].toLowerCase(); + var cmdArgs = (process.argv.length > 3 ? process.argv.slice(3) : []); // skip the cmdName + + var statsData = additionalData || {}; + var platforms = []; + var filePath; + var releaseTag; + + // update any aliases with the full cmd so there's a common property + cmdArgs = mapAliases(cmdArgs); + + var platformWhitelist = 'android ios firefoxos wp7 wp8 amazon-fireos blackberry10 tizen'.split(' '); + var argsWhitelist = ('add remove list update check debug release search --livereload --consolelogs --serverlogs ' + + '--no-cordova --nobrowser --nolivereload --noproxy --no-email --debug --release --device --emulator --sass ' + + '--splash --icon').split(' '); + + platforms = cmdArgs.filter(function(cmd) { + return platformWhitelist.indexOf(cmd) !== -1; + }); + + // create a platform property only when there is 1 or more + if (platforms.length) { + statsData.platform = platforms.sort().join(','); + } + + statsData = cmdArgs + .filter(function(cmd) { + return argsWhitelist.indexOf(cmd) !== -1; + }) + .concat(platforms) + .reduce(function(argObj, cmd) { + argObj[cmd] = true; + return argObj; + }, statsData); + + // add which ionic lib version they're using + try { + filePath = path.resolve('www/lib/ionic/version.json'); + statsData.ionic_version = JSON.parse(fs.readFileSync(filePath)).version; + } catch (e2) {} // eslint-disable-line no-empty + + // add which cli version is being used + try { + filePath = '../../package.json'; + statsData.cli_version = JSON.parse(fs.readFileSync(filePath)).version; + } catch (e2) {} // eslint-disable-line no-empty + + try { + statsData.email = ionicConfig.get('email'); + } catch (e2) {} // eslint-disable-line no-empty + + try { + statsData.account_id = ionicConfig.get('id'); + } catch (e2) {} // eslint-disable-line no-empty + + if (statsData.cli_version) { + releaseTag = statsData.cli_version.split('-')[1]; + } + if (releaseTag) { + statsData.cli_release_tag = releaseTag.split('.')[0]; + } + + var info = {}; + IonicInfo.getNodeVersion(info); + IonicInfo.getOsEnvironment(info); + IonicInfo.gatherGulpInfo(info); + + mp(cmdName, ({ + os: info.os, + gulp: info.gulp, + node: info.node + }, statsData)); + + // console.log(cmdName, statsData); + + } catch (e) { + log.error(('Error stats: ' + e)); + } +} + +module.exports = { + t: t +}; diff --git a/lib/ionic/store.js b/lib/utils/store.js similarity index 97% rename from lib/ionic/store.js rename to lib/utils/store.js index 92c7eacc5f..f027f044cb 100644 --- a/lib/ionic/store.js +++ b/lib/utils/store.js @@ -53,4 +53,4 @@ IonicStore.prototype.save = function() { } }; -exports.IonicStore = IonicStore; +module.exports = IonicStore; diff --git a/lib/ionic/table.js b/lib/utils/table.js similarity index 100% rename from lib/ionic/table.js rename to lib/utils/table.js diff --git a/lib/ionic/templates.js b/lib/utils/templates.js similarity index 62% rename from lib/ionic/templates.js rename to lib/utils/templates.js index e7c30dc564..1f58639602 100644 --- a/lib/ionic/templates.js +++ b/lib/utils/templates.js @@ -2,16 +2,11 @@ require('colors'); var _ = require('underscore'); var Q = require('q'); -var Task = require('./task').Task; var request = require('request'); -var log = require('ionic-app-lib').logging.logger; +var IonicAppLib = require('ionic-app-lib'); +var log = IonicAppLib.logging.logger; -function IonicTask() {} - -IonicTask.prototype = new Task(); - -IonicTask.prototype.fetchStarterTemplates = function() { - var self = this; +function fetchStarterTemplates() { // log.info('About to fetch template'); var downloadUrl = 'http://code.ionicframework.com/content/starter-templates.json'; @@ -29,20 +24,18 @@ IonicTask.prototype.fetchStarterTemplates = function() { templatesJson = JSON.parse(html); } catch (ex) { log.error('ex', ex); - q.reject('Error occured in download templates:', ex); - self.ionic.fail(ex); - return; + return q.reject('Error occured in download templates:', ex); } - q.resolve(templatesJson); + return q.resolve(templatesJson); } else { log.error('Unable to fetch the starter templates. Please check your internet connection'); - q.reject(res); + return q.reject(res); } }); return q.promise; -}; +} -IonicTask.prototype.list = function list(templates) { +function list(templates) { // Should have array of [{ name: 'name', description: 'desc' }] log.info('\n'); @@ -56,18 +49,18 @@ IonicTask.prototype.list = function list(templates) { } log.info(shortName.green, dots, template.description); }); -}; +} -IonicTask.prototype.run = function() { - var self = this; +function listTemplates() { - self.fetchStarterTemplates() - .then(function(starterTemplates) { - var templates = _.sortBy(starterTemplates.items, function(template) { return template.name; }); - log.info('Ionic Starter templates'.green); - self.list(templates); - }); + return fetchStarterTemplates() + .then(function(starterTemplates) { + var templates = _.sortBy(starterTemplates.items, function(template) { return template.name; }); + log.info('Ionic Starter templates'.green); + return list(templates); + }); +} +module.exports = { + listTemplates: listTemplates }; - -exports.IonicTask = IonicTask; diff --git a/package.json b/package.json index 9fe637f100..7c5af1585c 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,11 @@ "scripts": { "changelog": "conventional-changelog -i CHANGELOG.md -s -p angular", "gh-release": "node scripts/release", + "coveralls": "istanbul cover node_modules/jasmine-node/bin/jasmine-node --captureExceptions spec/ && cat coverage/lcov.info | node_modules/coveralls/bin/coveralls.js && rm -rf coverage", "e2e": "jasmine-node --captureExceptions ./e2e", "jasmine": "jasmine-node --captureExceptions ./spec", "lint": "eslint .", - "test": "npm run jasmine" + "test": "istanbul cover node_modules/jasmine-node/bin/jasmine-node --captureExceptions spec/" }, "keywords": [ "ionic", @@ -61,7 +62,6 @@ "dependencies": { "cli-table": "0.3.1", "colors": "0.6.2", - "detective": "4.3.1", "expand-tilde": "1.2.0", "form-data": "0.1.4", "gulp": "3.8.8", @@ -84,15 +84,17 @@ }, "devDependencies": { "conventional-changelog-cli": "1.1.1", + "coveralls": "^2.2.0", "eslint": "^2.8.0", "github": "0.2.4", + "istanbul": "^0.4.3", "jasmine-node": "1.14.5", + "mock-require": "1.3.0", "rewire": "2.5.1" }, "bundledDependencies": [ "cli-table", "colors", - "detective", "expand-tilde", "form-data", "gulp", diff --git a/spec/cli.spec.js b/spec/cli.spec.js index 8faeffe051..14e249227d 100644 --- a/spec/cli.spec.js +++ b/spec/cli.spec.js @@ -1,20 +1,30 @@ +'use strict'; + var IonicAppLib = require('ionic-app-lib'); -var Ionitron = require('../lib/ionic/ionitron'); -var IonicCli = require('../lib/cli'); +var semver = require('semver'); +var Ionitron = require('../lib/utils/ionitron'); var Q = require('q'); -var IonicStats = require('../lib/ionic/stats').IonicStats; -var Task = require('../lib/ionic/task').Task; +var helpUtils = require('../lib/utils/help'); +var IonicStore = require('../lib/utils/store'); +var IonicStats = require('../lib/utils/stats'); var Info = IonicAppLib.info; var Utils = IonicAppLib.utils; var Project = IonicAppLib.project; +var Logging = IonicAppLib.logging; +var log = Logging.logger; +var fs = require('fs'); +var path = require('path'); var rewire = require('rewire'); +var IonicCli = rewire('../lib/cli'); +var gulp = require('gulp'); describe('Cli', function() { beforeEach(function() { + spyOn(IonicStats, 't'); spyOn(IonicCli, 'processExit'); - spyOn(IonicCli, 'printAvailableTasks'); - spyOn(IonicCli, 'doRuntimeCheck'); + spyOn(helpUtils, 'printTaskListShortUsage'); + spyOn(helpUtils, 'printTaskListUsage'); spyOn(IonicAppLib.events, 'on'); spyOn(process, 'on'); spyOn(Info, 'checkRuntime'); @@ -24,7 +34,6 @@ describe('Cli', function() { spyOn(Utils, 'fail').andCallFake(function(err) { console.log(err); console.log(err.stack); - throw err; }); }); @@ -33,132 +42,291 @@ describe('Cli', function() { }); it('should have cli tasks defined', function() { - expect(IonicCli.Tasks).toBeDefined(); + expect(IonicCli.ALL_TASKS).toBeDefined(); }); describe('#run', function() { beforeEach(function() { - function fakeTask() {} - fakeTask.prototype = new Task(); - fakeTask.prototype.run = function() {}; - - spyOn(IonicCli, 'lookupTask').andReturn(fakeTask); + spyOn(IonicCli, 'doRuntimeCheck'); }); describe('#Cli methods', function() { - it('should run checkLatestVersion on run', function() { - var deferred = Q.defer(); - deferred.resolve(); - spyOn(IonicCli, 'checkLatestVersion').andReturn(deferred); - IonicCli.run(['node', 'bin/ionic', 'run', 'ios']); - expect(IonicCli.checkLatestVersion).toHaveBeenCalled(); + it('should run checkLatestVersion on run', function(done) { + spyOn(IonicCli, 'checkLatestVersion').andReturn(Q(true)); + + IonicCli.run(['node', 'bin/ionic', '--h']) + .then(function() { + expect(IonicCli.checkLatestVersion).toHaveBeenCalled(); + done(); + }); }); - xit('should run info doRuntimeCheck on run', function() { - spyOn(IonicCli, 'printHelpLines'); - IonicCli.run(['node', 'bin/ionic', '--h']); - expect(IonicCli.doRuntimeCheck).toHaveBeenCalled(); + it('should run info doRuntimeCheck on run', function(done) { + IonicCli.run(['node', 'bin/ionic', '--h']) + .then(function() { + expect(IonicCli.doRuntimeCheck).toHaveBeenCalled(); + done(); + }); }); - it('should run ionitron when argument is passed', function() { + it('should run ionitron when argument is passed', function(done) { spyOn(Ionitron, 'print'); - IonicCli.run(['node', 'bin/ionic', '--ionitron']); - expect(Ionitron.print).toHaveBeenCalled(); + + IonicCli.run(['node', 'bin/ionic', '--ionitron']) + .then(function() { + expect(Ionitron.print).toHaveBeenCalled(); + done(); + }); }); - it('should change log level to debug when verbose arg is passed', function() { - spyOn(IonicCli, 'tryBuildingTask').andReturn(false); + it('should change log level to debug when verbose arg is passed', function(done) { expect(IonicAppLib.logging.logger.level).toBe('info'); - IonicCli.run(['node', 'bin/ionic', '--verbose']); - expect(IonicAppLib.logging.logger.level).toBe('debug'); + + IonicCli.run(['node', 'bin/ionic', '--verbose']) + .then(function() { + expect(IonicAppLib.logging.logger.level).toBe('debug'); + done(); + }); }); - it('should get version when version flag passed', function() { + it('should get version when version flag passed', function(done) { spyOn(IonicCli, 'version'); - IonicCli.run(['node', 'bin/ionic', '--version']); - expect(IonicCli.version).toHaveBeenCalled(); + + IonicCli.run(['node', 'bin/ionic', '--version']) + .then(function() { + expect(IonicCli.version).toHaveBeenCalled(); + done(); + }); }); - it('should call help when help argument passed', function() { - spyOn(IonicCli, 'printHelpLines'); - IonicCli.run(['node', 'bin/ionic', '--help']); - expect(IonicCli.printHelpLines).toHaveBeenCalled(); + it('should call help when help argument passed', function(done) { + + IonicCli.run(['node', 'bin/ionic', '--help']) + .then(function() { + expect(helpUtils.printTaskListUsage).toHaveBeenCalled(); + done(); + }); }); - it('should call help when help shorthand argument passed', function() { - spyOn(IonicCli, 'printHelpLines'); - IonicCli.run(['node', 'bin/ionic', '--h']); - expect(IonicCli.printHelpLines).toHaveBeenCalled(); + it('should call help when help shorthand argument passed', function(done) { + + IonicCli.run(['node', 'bin/ionic', '--h']) + .then(function() { + expect(helpUtils.printTaskListUsage).toHaveBeenCalled(); + done(); + }); }); - it('should print available tasks if no valid command is passed', function() { - spyOn(IonicCli, 'tryBuildingTask').andReturn(false); - IonicCli.run(['node', 'bin/ionic']); - expect(IonicCli.printAvailableTasks).toHaveBeenCalled(); + it('should print available tasks if no valid command is passed', function(done) { + + IonicCli.run(['node', 'bin/ionic']) + .then(function() { + expect(helpUtils.printTaskListShortUsage).toHaveBeenCalled(); + done(); + }); }); it('should get the correct task by name', function() { - var task = IonicCli.getTaskWithName('start'); + var task = IonicCli.getTaskSettingsByName('start'); expect(task).toBeDefined(); expect(task.name).toBe('start'); expect(task.args).toBeDefined(); }); - it('should call attachErrorHandling', function() { + it('should call attachErrorHandling', function(done) { spyOn(IonicCli, 'attachErrorHandling'); - IonicCli.run(['node', 'bin/ionic']); - expect(IonicCli.attachErrorHandling).toHaveBeenCalled(); + + IonicCli.run(['node', 'bin/ionic']) + .then(function() { + expect(IonicCli.attachErrorHandling).toHaveBeenCalled(); + done(); + }); }); it('should get boolean options from start task', function() { - var task = IonicCli.getTaskWithName('start'); - var booleanOptions = IonicCli.getBooleanOptionsForTask(task); + var task = IonicCli.getTaskSettingsByName('start'); + var booleanOptions = IonicCli.getListOfBooleanOptions(task.options); // We expect 6 total = 3 options, each with short hand notation. expect(booleanOptions.length).toBe(11); }); - it('should track stats for cli', function() { - spyOn(IonicStats, 't'); - IonicCli.run(['node', 'bin/ionic', 'run', 'ios']); - expect(IonicStats.t).toHaveBeenCalled(); + it('should track stats for cli', function(done) { + + IonicCli.run(['node', 'bin/ionic', 'help']) + .then(function() { + expect(IonicStats.t).toHaveBeenCalled(); + done(); + }); + }); + + it('should change cwd to project root for project tasks', function(done) { + var FakeTask = { + name: 'fake', + title: 'fake', + run: function() { + return Q(true); + }, + isProjectTask: true + }; + spyOn(IonicCli, 'getTaskSettingsByName').andReturn(FakeTask); + + IonicCli.run(['node', 'bin/ionic', 'fake']) + .then(function() { + expect(Utils.cdIonicRoot).toHaveBeenCalled(); + done(); + }); + }); + + it('should not change cwd to project root for non project tasks', function(done) { + var FakeTask = { + name: 'fake', + title: 'fake', + run: function() { + return Q(true); + }, + isProjectTask: false + }; + spyOn(IonicCli, 'getTaskSettingsByName').andReturn(FakeTask); + + IonicCli.run(['node', 'bin/ionic', 'fake']) + .then(function() { + expect(Utils.cdIonicRoot).not.toHaveBeenCalled(); + done(); + }); + }); + + it('should print a warning if node_modules doesn\'t exist and using v2', function(done) { + spyOn(fs, 'existsSync').andReturn(false); + Project.load = function() { + return { + get: function() { return true; } // return v2 === true + }; + }; + + var FakeTask = { + name: 'fake', + title: 'fake', + run: function() { + return Q(true); + }, + isProjectTask: true + }; + spyOn(IonicCli, 'getTaskSettingsByName').andReturn(FakeTask); + spyOn(IonicCli, 'runWithGulp'); + spyOn(log, 'warn'); + IonicCli.run(['node', 'bin/ionic', 'fake']) + .then(function() { + expect(log.warn.calls[0].args[0]).toMatch( + 'WARN: No node_modules directory found, do you need to run npm install?' + ); + expect(IonicCli.runWithGulp).not.toHaveBeenCalled(); + done(); + }); + }); + + it('should warn if cmd requires build step and gulpfile doesn\'t exist and using v2', function(done) { + var FakeTask = { + name: 'build', + title: 'build', + run: function() {}, + isProjectTask: true + }; + Project.load = function() { + return { + get: function() { return true; } // return v2 === true + }; + }; + spyOn(IonicCli, 'getTaskSettingsByName').andReturn(FakeTask); + spyOn(FakeTask, 'run').andReturn(Q(true)); + spyOn(fs, 'existsSync').andReturn(true); + spyOn(IonicCli, 'loadGulpfile').andReturn(false); + spyOn(log, 'warn'); + + IonicCli.run(['node', 'bin/ionic', 'build']) + .then(function() { + expect(log.warn).toHaveBeenCalledWith('WARN: No gulpfile found!'); + done(); + }); + }); + + it('should not runWithGulp if a gulpfile doesn\'t exist', function(done) { + var FakeTask = { + name: 'fake', + title: 'fake', + run: function() {}, + isProjectTask: true + }; + spyOn(IonicCli, 'getTaskSettingsByName').andReturn(FakeTask); + spyOn(IonicCli, 'runWithGulp'); + spyOn(FakeTask, 'run').andReturn(Q(true)); + spyOn(fs, 'existsSync').andReturn(true); + spyOn(IonicCli, 'loadGulpfile').andReturn(false); + + IonicCli.run(['node', 'bin/ionic', 'fake']) + .then(function() { + expect(IonicCli.loadGulpfile).toHaveBeenCalled(); + expect(FakeTask.run).toHaveBeenCalled(); + expect(IonicCli.runWithGulp).not.toHaveBeenCalled(); + done(); + }); }); - it('should change pwd for commands', function() { - IonicCli.run(['node', 'bin/ionic', 'serve']); - expect(Utils.cdIonicRoot).toHaveBeenCalled(); + it('should runWithGulp', function(done) { + var FakeTask = { + name: 'fake', + isProjectTask: true + }; + spyOn(IonicCli, 'getTaskSettingsByName').andReturn(FakeTask); + spyOn(IonicCli, 'runWithGulp').andReturn(Q(true)); + spyOn(fs, 'existsSync').andReturn(true); + spyOn(IonicCli, 'loadGulpfile').andReturn(true); + + IonicCli.run(['node', 'bin/ionic', 'fake']) + .then(function() { + expect(IonicCli.runWithGulp).toHaveBeenCalled(); + done(); + }); }); - it('should not change pwd for commands', function() { - IonicCli.run(['node', 'bin/ionic', 'start']); - expect(Utils.cdIonicRoot).not.toHaveBeenCalled(); + it('should call Utils.fail if an exception occurrs within run', function() { + var error = new Error('error happened'); + spyOn(IonicCli, 'checkLatestVersion').andCallFake(function() { + throw error; + }); + + IonicCli.run(['node', 'bin/ionic', '--stats-opt-out']); + expect(Utils.fail).toHaveBeenCalledWith(error); + }); + + it('should save to the config if stats-opt-out is passed', function(done) { + spyOn(IonicStore.prototype, 'set'); + spyOn(IonicStore.prototype, 'save'); + + IonicCli.run(['node', 'bin/ionic', '--stats-opt-out']) + .then(function() { + expect(IonicStore.prototype.set).toHaveBeenCalledWith('statsOptOut', true); + expect(IonicStore.prototype.save).toHaveBeenCalled(); + done(); + }); }); }); }); describe('#commands options', function() { - var fakeTask; - beforeEach(function() { - fakeTask = function() {}; - fakeTask.prototype = new Task(); - fakeTask.prototype.run = function() {}; - - spyOn(IonicCli, 'lookupTask').andReturn(fakeTask); - spyOn(fakeTask.prototype, 'run').andCallThrough(); - + spyOn(IonicCli, 'doRuntimeCheck'); }); it('should parse start options correctly', function(done) { - var processArgs = ['node', '/usr/local/bin/ionic', 'start', 's1', '-w', '--appname', 'asdf']; + var Start = require('../lib/ionic/start'); + spyOn(Start, 'run').andReturn(Q(true)); - var promise = IonicCli.run(processArgs); - var fakeTaskRef = fakeTask; + var processArgs = ['node', '/usr/local/bin/ionic', 'start', 's1', '-w', '--appname', 'asdf']; - promise.then(function() { - expect(fakeTaskRef.prototype.run).toHaveBeenCalled(); - var taskArgs = fakeTaskRef.prototype.run.mostRecentCall.args; + IonicCli.run(processArgs).then(function() { + var taskArgs = Start.run.mostRecentCall.args; var taskArgv = taskArgs[1]; expect(taskArgv._.length).toBe(2); @@ -178,14 +346,13 @@ describe('Cli', function() { }); it('should parse serve options correctly', function(done) { - var processArgs = ['node', '/usr/local/bin/ionic', 'serve', '--nogulp', '--all', '--browser', 'firefox']; + var Serve = require('../lib/ionic/serve'); + spyOn(Serve, 'run').andReturn(Q(true)); - var promise = IonicCli.run(processArgs); - var fakeTaskRef = fakeTask; + var processArgs = ['node', '/usr/local/bin/ionic', 'serve', '--nogulp', '--all', '--browser', 'firefox']; - promise.then(function() { - expect(fakeTaskRef.prototype.run).toHaveBeenCalled(); - var taskArgs = fakeTaskRef.prototype.run.mostRecentCall.args; + IonicCli.run(processArgs).then(function() { + var taskArgs = Serve.run.mostRecentCall.args; var taskArgv = taskArgs[1]; @@ -200,18 +367,16 @@ describe('Cli', function() { }); }); - it('should parse upload options correctly', function(done) { + var Upload = require('../lib/ionic/upload'); + spyOn(Upload, 'run').andReturn(Q(true)); + var note = 'A note for notes'; var processArgs = ['node', '/usr/local/bin/ionic', 'upload', '--email', 'user@ionic.io', '--password', 'pass', '--note', note]; - var promise = IonicCli.run(processArgs); - var fakeTaskRef = fakeTask; - - promise.then(function() { - expect(fakeTaskRef.prototype.run).toHaveBeenCalled(); - var taskArgs = fakeTaskRef.prototype.run.mostRecentCall.args; + IonicCli.run(processArgs).then(function() { + var taskArgs = Upload.run.mostRecentCall.args; var taskArgv = taskArgs[1]; @@ -225,14 +390,13 @@ describe('Cli', function() { }); it('should parse login options correctly', function(done) { - var processArgs = ['node', '/usr/local/bin/ionic', 'login', '--email', 'user@ionic.io', '--password', 'pass']; + var Login = require('../lib/ionic/login'); + spyOn(Login, 'run').andReturn(Q(true)); - var promise = IonicCli.run(processArgs); - var fakeTaskRef = fakeTask; + var processArgs = ['node', '/usr/local/bin/ionic', 'login', '--email', 'user@ionic.io', '--password', 'pass']; - promise.then(function() { - expect(fakeTaskRef.prototype.run).toHaveBeenCalled(); - var taskArgs = fakeTaskRef.prototype.run.mostRecentCall.args; + IonicCli.run(processArgs).then(function() { + var taskArgs = Login.run.mostRecentCall.args; var taskArgv = taskArgs[1]; @@ -241,19 +405,18 @@ describe('Cli', function() { expect(taskArgv.email).toBe('user@ionic.io'); expect(taskArgv.password).toBe('pass'); done(); - }); + }).catch(done); }); it('should parse run options correctly', function(done) { + var Run = require('../lib/ionic/run'); + spyOn(Run, 'run').andReturn(Q(true)); + var processArgs = ['node', '/usr/local/bin/ionic', 'run', 'ios', '--livereload', '--port', '5000', '-r', '35730', '--consolelogs', '--serverlogs', '--device']; - var promise = IonicCli.run(processArgs); - var fakeTaskRef = fakeTask; - - promise.then(function() { - expect(fakeTaskRef.prototype.run).toHaveBeenCalled(); - var taskArgs = fakeTaskRef.prototype.run.mostRecentCall.args; + IonicCli.run(processArgs).then(function() { + var taskArgs = Run.run.mostRecentCall.args; var taskArgv = taskArgs[1]; @@ -270,15 +433,14 @@ describe('Cli', function() { }); it('should parse emulate options correctly', function(done) { + var Emulate = require('../lib/ionic/emulate'); + spyOn(Emulate, 'run').andReturn(Q(true)); + var processArgs = ['node', '/usr/local/bin/ionic', 'emulate', 'android', '--livereload', '--address', 'localhost', '--port', '5000', '-r', '35730', '--consolelogs', '--serverlogs']; - var promise = IonicCli.run(processArgs); - var fakeTaskRef = fakeTask; - - promise.then(function() { - expect(fakeTaskRef.prototype.run).toHaveBeenCalled(); - var taskArgs = fakeTaskRef.prototype.run.mostRecentCall.args; + IonicCli.run(processArgs).then(function() { + var taskArgs = Emulate.run.mostRecentCall.args; var taskArgv = taskArgs[1]; @@ -296,14 +458,13 @@ describe('Cli', function() { }); it('should parse state options correctly', function(done) { - var processArgs = ['node', '/usr/local/bin/ionic', 'state', 'save', '--plugins']; + var State = require('../lib/ionic/state'); + spyOn(State, 'run').andReturn(Q(true)); - var promise = IonicCli.run(processArgs); - var fakeTaskRef = fakeTask; + var processArgs = ['node', '/usr/local/bin/ionic', 'state', 'save', '--plugins']; - promise.then(function() { - expect(fakeTaskRef.prototype.run).toHaveBeenCalled(); - var taskArgs = fakeTaskRef.prototype.run.mostRecentCall.args; + IonicCli.run(processArgs).then(function() { + var taskArgs = State.run.mostRecentCall.args; var taskArgv = taskArgs[1]; @@ -317,15 +478,14 @@ describe('Cli', function() { }); it('should parse plugin options correctly', function(done) { + var Plugin = require('../lib/ionic/plugin'); + spyOn(Plugin, 'run').andReturn(Q(true)); + var processArgs = ['node', '/usr/local/bin/ionic', 'plugin', 'add', 'org.apache.cordova.splashscreen', '--nosave', '--searchpath', '../']; - var promise = IonicCli.run(processArgs); - var fakeTaskRef = fakeTask; - - promise.then(function() { - expect(fakeTaskRef.prototype.run).toHaveBeenCalled(); - var taskArgs = fakeTaskRef.prototype.run.mostRecentCall.args; + IonicCli.run(processArgs).then(function() { + var taskArgs = Plugin.run.mostRecentCall.args; var taskArgv = taskArgs[1]; @@ -341,14 +501,13 @@ describe('Cli', function() { }); it('should parse build options correctly', function(done) { - var processArgs = ['node', '/usr/local/bin/ionic', 'build', 'ios', '--nohooks']; + var Build = require('../lib/ionic/build'); + spyOn(Build, 'run').andReturn(Q(true)); - var promise = IonicCli.run(processArgs); - var fakeTaskRef = fakeTask; + var processArgs = ['node', '/usr/local/bin/ionic', 'build', 'ios', '--nohooks']; - promise.then(function() { - expect(fakeTaskRef.prototype.run).toHaveBeenCalled(); - var taskArgs = fakeTaskRef.prototype.run.mostRecentCall.args; + IonicCli.run(processArgs).then(function() { + var taskArgs = Build.run.mostRecentCall.args; var taskArgv = taskArgs[1]; @@ -363,6 +522,7 @@ describe('Cli', function() { describe('version checking for checkRuntime', function() { var IonicCli; + beforeEach(function() { IonicCli = rewire('../lib/cli'); }); @@ -370,23 +530,355 @@ describe('Cli', function() { xit('should do runtime check when version is not checked', function() { var IonicConfigSpy = jasmine.createSpyObj('IonicConfig', ['get', 'set', 'save']); IonicConfigSpy.get.andReturn('1.6.4'); - IonicCli.__set__('IonicConfig', IonicConfigSpy); + var revertConfig = IonicCli.__set__('IonicConfig', IonicConfigSpy); IonicCli.doRuntimeCheck('1.6.4'); + expect(Info.checkRuntime).not.toHaveBeenCalled(); expect(IonicConfigSpy.set).not.toHaveBeenCalled(); expect(IonicConfigSpy.save).not.toHaveBeenCalled(); + revertConfig(); }); xit('should do runtime check when version is not checked', function() { var IonicConfigSpy = jasmine.createSpyObj('IonicConfig', ['get', 'set', 'save']); IonicConfigSpy.get.andReturn('1.6.4'); - IonicCli.__set__('IonicConfig', IonicConfigSpy); + var revertConfig = IonicCli.__set__('IonicConfig', IonicConfigSpy); IonicCli.doRuntimeCheck('1.6.5'); + expect(Info.checkRuntime).toHaveBeenCalled(); expect(IonicConfigSpy.get).toHaveBeenCalled(); expect(IonicConfigSpy.set).toHaveBeenCalledWith('lastVersionChecked', '1.6.5'); expect(IonicConfigSpy.save).toHaveBeenCalled(); + revertConfig(); + }); + }); + }); + + describe('runWithGulp function', function() { + var fakeTask; + var argv; + var qCallbacks; + + beforeEach(function() { + fakeTask = { + name: 'fake', + title: 'fake', + run: function() { + return Q(true); + }, + isProjectTask: true + }; + argv = { + _: ['fake'], + v2: true + }; + gulp.tasks = { + 'fake:before': function() {}, + 'fake:after': function() {} + }; + qCallbacks = []; + + // gulp.start gets called with nfcall because it's async with a callback + // so we stub our own function with a callback instead of creating a spy + // (which is sync). We also track the callbacks because they are created + // at runtime by Q.nfcall so we can tell jasmine what to expect. + gulp.start = function(taskName, cb) { + qCallbacks.push(cb); + cb(); + }; + spyOn(gulp, 'start').andCallThrough(); + spyOn(IonicCli, 'loadGulpfile').andReturn(true); + spyOn(fakeTask, 'run').andCallThrough(); + spyOn(process, 'exit'); + spyOn(IonicCli, 'logEvents'); + spyOn(log, 'error'); + spyOn(fs, 'existsSync'); + }); + + it('should try to load gulp, exit if it fails', function() { + spyOn(path, 'resolve').andReturn('./wrong_path'); + + IonicCli.runWithGulp(argv, fakeTask); + + expect(log.error).toHaveBeenCalledWith('\nGulpfile detected, but gulp is not installed'.red); + expect(log.error).toHaveBeenCalledWith('Do you need to run `npm install`?\n'.red); + expect(process.exit).toHaveBeenCalled(); + + expect(IonicCli.logEvents).not.toHaveBeenCalled(); + expect(fakeTask.run).not.toHaveBeenCalled(); + expect(gulp.start).not.toHaveBeenCalled(); + }); + + + it('should run logEvents, the command and the gulp hooks', function(done) { + IonicCli.runWithGulp(argv, fakeTask).then(function() { + + expect(IonicCli.logEvents).toHaveBeenCalled(); + expect(gulp.start).toHaveBeenCalledWith('fake:before', qCallbacks[0]); + expect(gulp.start).toHaveBeenCalledWith('fake:after', qCallbacks[1]); + expect(fakeTask.run).toHaveBeenCalledWith(IonicCli, argv); + + expect(log.error).not.toHaveBeenCalled(); + expect(process.exit).not.toHaveBeenCalled(); + expect(fs.existsSync).not.toHaveBeenCalled(); + done(); + }).catch(done); + }); + + it('should warn if no gulp task and using v2 and cmd requires build', function(done) { + argv._[0] = 'build'; + spyOn(log, 'warn'); + gulp.start = function(taskName, cb) { + cb({ + missingTask: true + }); + }; + IonicCli.runWithGulp(argv, fakeTask).then(function() { + expect(log.warn).toHaveBeenCalledWith(('WARN: No \'build:before\' gulp task found!').yellow); + done(); + }); + }); + }); + + describe('loadGulpfile function', function() { + it('should return true if gulpfile found', function() { + var mock = require('mock-require'); + var gulpfilePath = path.resolve(process.cwd() + '/gulpfile.js'); + mock(gulpfilePath, {}); + + var result = IonicCli.loadGulpfile(); + expect(result).toBe(true); + + mock.stop(gulpfilePath); + }); + + it('should return false if gulpfile not found', function() { + var result = IonicCli.loadGulpfile(); + expect(result).toBe(false); + }); + }); + + describe('processExit method', function() { + }); + + describe('gatherInfo method', function() { + it('should return an object and gather info from Info function from app-lib', function() { + spyOn(Info, 'gatherInfo').andReturn({}); + spyOn(Info, 'getIonicVersion'); + spyOn(Info, 'getIonicCliVersion'); + + var info = IonicCli.gatherInfo(); + + expect(info).toEqual(jasmine.any(Object)); + }); + }); + + describe('printVersionWarning method', function() { + it('should write out a warning if the version is not equal to version specified by the cli', function() { + spyOn(log, 'warn'); + + IonicCli.printVersionWarning('2.0.1', '2.0.2'); + expect(log.warn).toHaveBeenCalled(); + }); + it('should not write out a warning if the version is equal to version specified by the cli', function() { + spyOn(log, 'warn'); + + IonicCli.printVersionWarning('2.0.1', '2.0.1'); + expect(log.warn).not.toHaveBeenCalled(); + }); + + it('should not write out a warning if the version is in beta', function() { + spyOn(log, 'warn'); + + IonicCli.printVersionWarning('2.0.1-beta', '2.0.1'); + expect(log.warn).not.toHaveBeenCalled(); + }); + + it('should not write out a warning if the version is in alpha', function() { + spyOn(log, 'warn'); + + IonicCli.printVersionWarning('2.0.1-alpha', '2.0.1'); + expect(log.warn).not.toHaveBeenCalled(); + }); + }); + + describe('formatGulpError function', function() { + it('should return e.message if e.err is null', function() { + var error = new Error('gulp broke'); + error.err = null; + + var results = IonicCli.formatGulpError(error); + expect(results).toEqual(error.message); + }); + + it('should return a string if the error is in a plugin', function() { + var error = { + err: { + showStack: 'boolean' + } + }; + + var results = IonicCli.formatGulpError(error); + expect(results).toEqual(jasmine.any(String)); + }); + + it('should return a stack if it exists', function() { + var testError = new Error('gulp broke'); + var error = { + err: testError + }; + + var results = IonicCli.formatGulpError(error); + expect(results).toEqual(testError.stack); + }); + + it('should return a new error if it does not understand the error', function() { + var error = { + err: 'Something broke somewhere' + }; + var results = IonicCli.formatGulpError(error); + expect(results).toContain(error.err); + }); + }); + + describe('checkLatestVersion method', function() { + it('should not check npm if current version is a beta', function() { + spyOn(IonicStore.prototype, 'get'); + spyOn(IonicStore.prototype, 'set'); + spyOn(IonicStore.prototype, 'save'); + var npmVersion = IonicCli.npmVersion; + + var result = IonicCli.checkLatestVersion('2.0.1-beta'); + expect(result).toEqual(null); + expect(npmVersion).toEqual(IonicCli.npmVersion); + expect(IonicStore.prototype.get).not.toHaveBeenCalled(); + expect(IonicStore.prototype.set).not.toHaveBeenCalled(); + expect(IonicStore.prototype.save).not.toHaveBeenCalled(); + }); + + it('should not check npm if timestamp is recent', function() { + spyOn(IonicStore.prototype, 'get').andReturn(new Date().getTime()); + spyOn(IonicStore.prototype, 'set'); + spyOn(IonicStore.prototype, 'save'); + var npmVersion = IonicCli.npmVersion; + + var result = IonicCli.checkLatestVersion('2.0.1'); + expect(result).toEqual(null); + expect(npmVersion).toEqual(IonicCli.npmVersion); + expect(IonicStore.prototype.set).not.toHaveBeenCalled(); + expect(IonicStore.prototype.save).not.toHaveBeenCalled(); + }); + + it('should check npm if timestamp is recent', function(done) { + spyOn(IonicStore.prototype, 'get').andReturn(new Date(2016, 1, 1).getTime()); + spyOn(IonicStore.prototype, 'set'); + spyOn(IonicStore.prototype, 'save'); + var revertRequest = IonicCli.__set__('request', function(options, callback) { + callback(null, null, '{ "version": "1.0.1" }'); + }); + + IonicCli.checkLatestVersion('2.0.1').then(function() { + expect(IonicStore.prototype.set).toHaveBeenCalledWith('versionCheck', jasmine.any(Number)); + expect(IonicStore.prototype.save).toHaveBeenCalled(); + revertRequest(); + done(); + }); + }); + }); + + describe('printNewsUpdates method', function() { + it('should log request info if a valid response is returned', function(done) { + spyOn(log, 'info'); + var revertRequest = IonicCli.__set__('request', function(options, callback) { + callback(null, { statusCode: '200' }, '{ "version": "1.0.1" }'); + }); + IonicCli.printNewsUpdates().then(function() { + expect(log.info).toHaveBeenCalled(); + revertRequest(); + done(); + }); + }); + }); + + describe('doRuntimeCheck method', function() { + it('should update IonicConfig if semver is not met', function() { + var version = '0.2.0'; + var error = new Error('semver failure'); + spyOn(IonicStore.prototype, 'get').andReturn('0.1.0'); + spyOn(IonicStore.prototype, 'set'); + spyOn(IonicStore.prototype, 'save'); + spyOn(semver, 'satisfies').andCallFake(function() { + throw error; + }); + + IonicCli.doRuntimeCheck(version); + expect(IonicStore.prototype.set).toHaveBeenCalledWith('lastVersionChecked', version); + expect(IonicStore.prototype.save).toHaveBeenCalled(); + }); + + it('should update IonicConfig if lastVersionChecked from IonicConfig is not available', function() { + var version = '0.2.0'; + spyOn(IonicStore.prototype, 'get').andReturn(null); + spyOn(IonicStore.prototype, 'set'); + spyOn(IonicStore.prototype, 'save'); + + IonicCli.doRuntimeCheck(version); + expect(IonicStore.prototype.set).toHaveBeenCalledWith('lastVersionChecked', version); + expect(IonicStore.prototype.save).toHaveBeenCalled(); + }); + + it('should not update IonicConfig if lastVersionChecked is available and semver is met', function() { + var version = '0.2.0'; + spyOn(IonicStore.prototype, 'get').andReturn('0.2.0'); + spyOn(IonicStore.prototype, 'set'); + spyOn(IonicStore.prototype, 'save'); + spyOn(semver, 'satisfies').andReturn(true); + + IonicCli.doRuntimeCheck(version); + expect(IonicStore.prototype.set).not.toHaveBeenCalled(); + expect(IonicStore.prototype.save).not.toHaveBeenCalled(); + }); + }); + + describe('getContentSrc method', function() { + it('should call getContentSrc util for process cwd.', function() { + spyOn(process, 'cwd').andReturn('/some/dir'); + spyOn(Utils, 'getContentSrc').andCallFake(function(param) { + return param; }); + var result = IonicCli.getContentSrc(); + expect(process.cwd).toHaveBeenCalled(); + expect(Utils.getContentSrc).toHaveBeenCalledWith('/some/dir'); + expect(result).toEqual('/some/dir'); + }); + }); + + describe('fail method', function() { + it('should call fail util.', function() { + IonicCli.fail('some error', 'task help text'); + expect(Utils.fail).toHaveBeenCalledWith('some error', 'task help text'); + }); + }); + + describe('handleUncaughtExceptions method', function() { + it('log an error and then exit if param is a string', function() { + spyOn(log, 'error'); + spyOn(process, 'exit'); + spyOn(Utils, 'errorHandler'); + + IonicCli.handleUncaughtExceptions('error message'); + expect(Utils.errorHandler).toHaveBeenCalledWith('error message'); + expect(process.exit).toHaveBeenCalledWith(1); + }); + + it('log an error and then exit if param is an object', function() { + spyOn(log, 'error'); + spyOn(process, 'exit'); + spyOn(Utils, 'errorHandler'); + + IonicCli.handleUncaughtExceptions({ message: 'error message' }); + expect(Utils.errorHandler).toHaveBeenCalledWith('error message'); + expect(process.exit).toHaveBeenCalledWith(1); }); }); }); diff --git a/spec/helpers/ionic.js b/spec/helpers/ionic.js new file mode 100644 index 0000000000..84044e3fc9 --- /dev/null +++ b/spec/helpers/ionic.js @@ -0,0 +1,19 @@ +'use strict'; + +var cli = require('../../lib/cli'); + +/* + * args + * [0] = node + * [1] = ionic + * [2] = command + * + */ +module.exports = function ionic(ionicArgs) { + var cliArgs = [ + 'node', + 'bin/ionic' + ]; + + return cli.run(cliArgs.concat(ionicArgs)); +}; diff --git a/spec/serve.spec.js b/spec/serve.spec.js deleted file mode 100644 index 9f2c1bd7dc..0000000000 --- a/spec/serve.spec.js +++ /dev/null @@ -1,84 +0,0 @@ -var IonicAppLib = require('ionic-app-lib'); -var IonicProject = IonicAppLib.project; -var Serve = IonicAppLib.serve; -var Q = require('q'); -var rewire = require('rewire'); - -var argv = { - _: ['--livereload'], - nogulp: false -}; - -describe('Serve', function() { - var serveTask; - - beforeEach(function() { - serveTask = rewire('../lib/ionic/serve'); - }); - - it('should have serve defined', function() { - expect(serveTask).toBeDefined(); - }); - - it('should use connect live reload port from env var', function(done) { - spyOn(IonicProject, 'load').andReturn({}); - spyOn(Serve, 'loadSettings').andReturn({}); - spyOn(Serve, 'getAddress').andReturn(Q()); - - spyOn(Serve, 'checkPorts'); - spyOn(Serve, 'start'); - spyOn(Serve, 'showFinishedServeMessage'); - spyOn(process, 'cwd').andReturn('/ionic/app/path'); - - process.env = { CONNECT_LIVE_RELOAD_PORT: 5000 }; - - var serve = new serveTask.IonicTask(); - var options = { - appDirectory: '/ionic/app/path', - liveReloadPort: 5000, - nogulp: false - }; - - Q() - .then(function() { - return serve.run({}, argv); - }) - .then(function() { - expect(Serve.start).toHaveBeenCalledWith(options); - }) - .catch(function(ex) { - expect('this').toBe(ex.stack); - }) - .fin(done); - }); - - it('should use connect live reload port from env var', function(done) { - spyOn(IonicProject, 'load').andReturn({ - get: function() { - return false; - } - }); - process.env = {}; - spyOn(Serve, 'getAddress').andReturn(Q()); - - spyOn(Serve, 'checkPorts'); - spyOn(Serve, 'start'); - spyOn(Serve, 'showFinishedServeMessage'); - spyOn(process, 'cwd').andReturn('/ionic/app/path'); - - var serve = new serveTask.IonicTask(); - - Q() - .then(function() { - return serve.run({}, argv); - }) - .then(function() { - var calls = Serve.start.calls[0].args[0]; - expect(calls.liveReloadPort).toBe(35729); - }) - .catch(function(ex) { - expect('this').toBe(ex.stack); - }) - .fin(done); - }); -}); diff --git a/spec/start.spec.js b/spec/start.spec.js deleted file mode 100644 index 60f26969e0..0000000000 --- a/spec/start.spec.js +++ /dev/null @@ -1,13 +0,0 @@ -var rewire = require('rewire'); - -describe('Start', function() { - var startTask; - - beforeEach(function() { - startTask = rewire('../lib/ionic/serve'); - }); - - it('should have serve defined', function() { - expect(startTask).toBeDefined(); - }); -}); diff --git a/spec/stats.spec.js b/spec/stats.spec.js deleted file mode 100644 index cad77d7e97..0000000000 --- a/spec/stats.spec.js +++ /dev/null @@ -1,79 +0,0 @@ -var IonicStatsModule; -var IonicInfoModule; -var IonicStats; -var rewire = require('rewire'); - -describe('Stats', function() { - beforeEach(function() { - IonicStatsModule = rewire('../lib/ionic/stats'); - IonicInfoModule = rewire('ionic-app-lib').info; - IonicStats = IonicStatsModule.IonicStats; - }); - - it('should have stats defined', function() { - expect(IonicStats).toBeDefined(); - }); - - describe('#t', function() { - it('should not track if process.argv is less than 3', function() { - var oldprocessargv = process.argv; - process.argv = ['node', 'bin/ionic']; - - spyOn(IonicStats, 'mp'); - - var configSpy = jasmine.createSpyObj('ionicConfig', ['get']); - configSpy.get.andReturn(true); - - IonicStatsModule.__set__('ionicConfig', configSpy); - - IonicStats.t(); - - expect(configSpy.get).not.toHaveBeenCalled(); - expect(IonicStats.mp).not.toHaveBeenCalled(); - process.argv = oldprocessargv; - }); - - it('should not track stats if opted out', function() { - var configSpy = jasmine.createSpyObj('ionicConfig', ['get']); - configSpy.get.andReturn(true); - spyOn(IonicStatsModule, 'getVersion').andReturn({ version: '1.6.4' }); - - IonicStatsModule.__set__('ionicConfig', configSpy); - - IonicStats.t(); - - expect(configSpy.get).toHaveBeenCalled(); - }); - - it('should track the correct command', function() { - var oldprocessargv = process.argv; - process.argv = ['node', 'bin/ionic', 'start', 'foldername']; - var packageJson = { version: '1.6.4' }; - - spyOn(IonicStats, 'mp'); - spyOn(IonicStatsModule, 'getVersion').andReturn(packageJson); - spyOn(IonicInfoModule, 'gatherGulpInfo').andCallFake(function(info) { - info.os = 'Mac OS X El Capitan'; - info.node = 'v5.10.1'; - info.gulp = 'v3.0.0'; - }); - - var configSpy = jasmine.createSpyObj('ionicConfig', ['get']); - configSpy.get.andReturn(false); - - IonicStatsModule.__set__('ionicConfig', configSpy); - - IonicStats.t(); - - expect(IonicStats.mp).toHaveBeenCalledWith('start', { - cli_version: packageJson.version, // eslint-disable-line camelcase - email: false, - account_id: false, // eslint-disable-line camelcase - os: 'Mac OS X El Capitan', - gulp: 'v3.0.0', - node: 'v5.10.1' - }); - process.argv = oldprocessargv; - }); - }); -}); diff --git a/spec/tasks/add.spec.js b/spec/tasks/add.spec.js new file mode 100644 index 0000000000..4cf2811309 --- /dev/null +++ b/spec/tasks/add.spec.js @@ -0,0 +1,134 @@ +'use strict'; + +var optimist = require('optimist'); +var childProcess = require('child_process'); +var rewire = require('rewire'); +var add = rewire('../../lib/ionic/add'); +var IonicAppLib = require('ionic-app-lib'); +var appLibUtils = IonicAppLib.utils; +var ioLib = IonicAppLib.ioConfig; +var log = IonicAppLib.logging.logger; +var bower = require('../../lib/utils/bower'); + +describe('add command', function() { + describe('command settings', function() { + it('should have a title', function() { + expect(add.title).toBeDefined(); + expect(add.title).not.toBeNull(); + expect(add.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(add.summary).toBeDefined(); + expect(add.summary).not.toBeNull(); + expect(add.summary.length).toBeGreaterThan(0); + }); + + it('should have a boolean option of --nohooks or -n that defaults to true', function() { + expect(add.args).toEqual(jasmine.any(Object)); + expect(add.args['[name]']).toEqual(jasmine.any(String)); + }); + }); + + describe('run function', function() { + var processArguments = ['node', 'ionic', 'add', 'thing']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + beforeEach(function() { + spyOn(childProcess, 'exec'); + }); + + it('should fail if bower is not installed', function() { + spyOn(appLibUtils, 'fail'); + spyOn(bower, 'checkForBower').andReturn(false); + + // Expect failure + add.run(null, argv, rawCliArguments); + expect(appLibUtils.fail).toHaveBeenCalledWith(bower.installMessage, 'add'); + }); + + it('should fail if installBowerComponent throws an error obj message if the error has a message value', function() { + spyOn(ioLib, 'injectIoComponent'); + spyOn(ioLib, 'warnMissingData'); + spyOn(appLibUtils, 'fail'); + spyOn(bower, 'checkForBower').andReturn(true); + var installBowerSpy = jasmine.createSpy('installBowerSpy'); + installBowerSpy.andCallFake(function() { + throw new Error('something failed'); + }); + var revertInstallBowerSpy = add.__set__('installBowerComponent', installBowerSpy); + + // Expect failure + add.run(null, argv, rawCliArguments); + expect(installBowerSpy).toHaveBeenCalledWith('thing'); + expect(appLibUtils.fail).toHaveBeenCalledWith('something failed', 'service'); + + // TODO: things seems incorrect + expect(ioLib.injectIoComponent).toHaveBeenCalledWith(true, 'thing'); + expect(ioLib.warnMissingData).toHaveBeenCalled(); + revertInstallBowerSpy(); + }); + + it('should fail if installBowerComponent throws an error if the error has a message value', function() { + spyOn(ioLib, 'injectIoComponent'); + spyOn(ioLib, 'warnMissingData'); + spyOn(appLibUtils, 'fail'); + spyOn(bower, 'checkForBower').andReturn(true); + var installBowerSpy = jasmine.createSpy('installBowerSpy'); + installBowerSpy.andCallFake(function() { + throw 'something'; + }); + var revertInstallBowerSpy = add.__set__('installBowerComponent', installBowerSpy); + + // Expect failure + add.run(null, argv, rawCliArguments); + expect(installBowerSpy).toHaveBeenCalledWith('thing'); + expect(appLibUtils.fail).toHaveBeenCalledWith('something', 'service'); + + // TODO: things seems incorrect + expect(ioLib.injectIoComponent).toHaveBeenCalledWith(true, 'thing'); + expect(ioLib.warnMissingData).toHaveBeenCalled(); + revertInstallBowerSpy(); + }); + + it('should call injectIoComponent and warnMissingData on install success.', function() { + spyOn(ioLib, 'injectIoComponent'); + spyOn(ioLib, 'warnMissingData'); + spyOn(bower, 'checkForBower').andReturn(true); + + var installBowerSpy = jasmine.createSpy('installBowerSpy'); + var revertInstallBowerSpy = add.__set__('installBowerComponent', installBowerSpy); + + add.run(null, argv, rawCliArguments); + expect(bower.checkForBower).toHaveBeenCalled(); + expect(installBowerSpy).toHaveBeenCalledWith('thing'); + expect(ioLib.injectIoComponent).toHaveBeenCalledWith(true, 'thing'); + expect(ioLib.warnMissingData).toHaveBeenCalled(); + revertInstallBowerSpy(); + }); + }); + + describe('installBowerComponent function', function() { + + it('should call childProcess exec to install the bower component', function() { + spyOn(log, 'info'); + spyOn(childProcess, 'exec').andReturn({ code: 0 }); + + var installBowerComponent = add.__get__('installBowerComponent'); + installBowerComponent('thing'); + expect(childProcess.exec).toHaveBeenCalledWith('bower install --save-dev thing'); + expect(log.info).toHaveBeenCalledWith('Bower component installed - thing'); + }); + + it('should call childProcess exec and call util fail if result.code is not equal to 0', function() { + spyOn(appLibUtils, 'fail'); + spyOn(childProcess, 'exec').andReturn({ code: 1 }); + + var installBowerComponent = add.__get__('installBowerComponent'); + installBowerComponent('thing'); + expect(childProcess.exec).toHaveBeenCalledWith('bower install --save-dev thing'); + expect(appLibUtils.fail).toHaveBeenCalledWith(jasmine.any(String), 'add'); + }); + }); +}); diff --git a/spec/tasks/address.spec.js b/spec/tasks/address.spec.js new file mode 100644 index 0000000000..fc0ce0b9a9 --- /dev/null +++ b/spec/tasks/address.spec.js @@ -0,0 +1,45 @@ +'use strict'; + +var optimist = require('optimist'); +var rewire = require('rewire'); +var Q = require('q'); +var addressTask = rewire('../../lib/ionic/address'); +var IonicAppLib = require('ionic-app-lib'); +var Serve = IonicAppLib.serve; +var log = IonicAppLib.logging.logger; + +describe('Serve', function() { + + beforeEach(function() { + spyOn(log, 'info'); + }); + + describe('command settings', function() { + it('should have a title', function() { + expect(addressTask.title).toBeDefined(); + expect(addressTask.title).not.toBeNull(); + expect(addressTask.title.length).toBeGreaterThan(0); + }); + }); + + describe('run command', function() { + it('should update config and gather the current address', function(done) { + var processArguments = ['node', 'ionic', 'address']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var configSpy = jasmine.createSpyObj('config', ['set']); + + spyOn(IonicAppLib.config, 'load').andReturn(configSpy); + spyOn(Serve, 'getAddress').andReturn(Q(true)); + + addressTask.run({}, argv).then(function() { + expect(IonicAppLib.config.load).toHaveBeenCalled(); + expect(configSpy.set.calls[0].args).toEqual(['ionicServeAddress', null]); + expect(configSpy.set.calls[1].args).toEqual(['platformServeAddress', null]); + expect(Serve.getAddress).toHaveBeenCalledWith({ isAddressCmd: true }); + done(); + }).catch(done); + }); + }); +}); diff --git a/spec/tasks/build.spec.js b/spec/tasks/build.spec.js new file mode 100644 index 0000000000..4fdf6a5d9c --- /dev/null +++ b/spec/tasks/build.spec.js @@ -0,0 +1,138 @@ +'use strict'; + +var Q = require('q'); +var optimist = require('optimist'); +var cordovaUtils = require('../../lib/utils/cordova'); +var os = require('os'); +var IonicAppLib = require('ionic-app-lib'); +var ConfigXml = IonicAppLib.configXml; +var log = IonicAppLib.logging.logger; +var build = require('../../lib/ionic/build'); + +describe('build command', function() { + beforeEach(function() { + spyOn(log, 'error'); + spyOn(ConfigXml, 'setConfigXml').andReturn(Q(true)); + }); + + describe('command settings', function() { + + it('should have a title', function() { + expect(build.title).toBeDefined(); + expect(build.title).not.toBeNull(); + expect(build.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(build.summary).toBeDefined(); + expect(build.summary).not.toBeNull(); + expect(build.summary.length).toBeGreaterThan(0); + }); + + it('should have args', function() { + expect(build.args).toEqual(jasmine.any(Object)); + expect(build.args['[options]']).toEqual(jasmine.any(String)); + expect(build.args['']).toEqual(jasmine.any(String)); + }); + + it('should have options', function() { + expect(build.options).toEqual(jasmine.any(Object)); + expect(build.options['--nohooks|-n']).toEqual(jasmine.any(Object)); + }); + }); + + + describe('cordova platform checks', function() { + + var appDirectory = '/ionic/app/path'; + var processArguments = ['node', 'ionic', 'build', '-n']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + beforeEach(function() { + spyOn(process, 'cwd').andReturn(appDirectory); + + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); + }); + + it('should default to iOS for the platform', function(done) { + spyOn(os, 'platform').andReturn('darwin'); + + // Expect failure + build.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.isPlatformInstalled).toHaveBeenCalledWith('ios', appDirectory); + done(); + }); + }); + + it('should fail if the system is not Mac and the platform is iOS', function(done) { + spyOn(os, 'platform').andReturn('windows'); + + build.run(null, argv, rawCliArguments).catch(function(error) { + expect(error).toEqual('✗ You cannot run iOS unless you are on Mac OSX.'); + done(); + }); + }); + }); + + + describe('cordova platform and plugin checks', function() { + + var appDirectory = '/ionic/app/path'; + var processArguments = ['node', 'ionic', 'build', 'ios', '-n']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + beforeEach(function() { + spyOn(os, 'platform').andReturn('darwin'); + spyOn(process, 'cwd').andReturn(appDirectory); + + spyOn(cordovaUtils, 'installPlatform').andReturn(Q(true)); + spyOn(cordovaUtils, 'installPlugins').andReturn(Q(true)); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(0)); + }); + + it('should try to install the cordova platform if it is not installed', function(done) { + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(false); + + build.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.installPlatform).toHaveBeenCalledWith('ios'); + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['build', 'ios', '-n']); + done(); + }); + }); + + it('should not try to install the cordova platform if it is installed', function(done) { + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + + build.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.installPlatform).not.toHaveBeenCalledWith(); + done(); + }); + }); + + it('should install plugins if they are not installed', function(done) { + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(false); + + build.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.arePluginsInstalled).toHaveBeenCalledWith(appDirectory); + expect(cordovaUtils.installPlugins).toHaveBeenCalledWith(); + done(); + }); + }); + + it('should not install plugins if they are installed', function(done) { + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + + build.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.arePluginsInstalled).toHaveBeenCalledWith(appDirectory); + expect(cordovaUtils.installPlugins).not.toHaveBeenCalledWith(); + done(); + }); + }); + }); +}); diff --git a/spec/tasks/compile.spec.js b/spec/tasks/compile.spec.js new file mode 100644 index 0000000000..eaeae971e1 --- /dev/null +++ b/spec/tasks/compile.spec.js @@ -0,0 +1,56 @@ +'use strict'; + +var Q = require('q'); +var optimist = require('optimist'); +var cordovaUtils = require('../../lib/utils/cordova'); +var IonicAppLib = require('ionic-app-lib'); +var ConfigXml = IonicAppLib.configXml; +var log = IonicAppLib.logging.logger; +var compile = require('../../lib/ionic/compile'); + +describe('compile command', function() { + beforeEach(function() { + spyOn(log, 'error'); + spyOn(process, 'cwd').andReturn('/some/directory'); + }); + + describe('command settings', function() { + it('should have a title', function() { + expect(compile.title).toBeDefined(); + expect(compile.title).not.toBeNull(); + expect(compile.title.length).toBeGreaterThan(0); + }); + }); + + describe('run function', function() { + var processArguments = ['node', 'ionic', 'compile', 'set']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + it('should fail if any functions throw', function(done) { + var error = new Error('error occurred'); + spyOn(ConfigXml, 'setConfigXml').andReturn(Q.reject(error)); + + compile.run({}, argv, rawCliArguments).catch(function(ex) { + expect(ex).toEqual(error); + done(); + }); + }); + + it('should call execCordovaCommand and return a promise', function(done) { + spyOn(ConfigXml, 'setConfigXml').andReturn(Q(true)); + spyOn(cordovaUtils, 'filterArgumentsForCordova').andReturn(['compile', 'set']); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); + + compile.run({}, argv, rawCliArguments).then(function(results) { + expect(ConfigXml.setConfigXml).toHaveBeenCalledWith('/some/directory', { + resetContent: true, + errorWhenNotFound: false + }); + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['compile', 'set']); + expect(results).toEqual(true); + done(); + }); + }); + }); +}); diff --git a/spec/tasks/config.spec.js b/spec/tasks/config.spec.js new file mode 100644 index 0000000000..dd4026105d --- /dev/null +++ b/spec/tasks/config.spec.js @@ -0,0 +1,135 @@ +'use strict'; + +var optimist = require('optimist'); +var configTask = require('../../lib/ionic/config'); +var IonicAppLib = require('ionic-app-lib'); +var ioLib = IonicAppLib.ioConfig; +var IonicProject = IonicAppLib.project; +var appLibUtils = IonicAppLib.utils; + +describe('config command', function() { + + describe('command settings', function() { + + it('should have a title', function() { + expect(configTask.title).toBeDefined(); + expect(configTask.title).not.toBeNull(); + expect(configTask.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(configTask.summary).toBeDefined(); + expect(configTask.summary).not.toBeNull(); + expect(configTask.summary.length).toBeGreaterThan(0); + }); + + it('should have a boolean option of --nohooks or -n that defaults to true', function() { + expect(configTask.args).toEqual(jasmine.any(Object)); + expect(configTask.args['']).toEqual(jasmine.any(String)); + expect(configTask.args['[key]']).toEqual(jasmine.any(String)); + expect(configTask.args['[value]']).toEqual(jasmine.any(String)); + }); + }); + + describe('run function', function() { + it('should fail if the project cannot be loaded', function() { + var processArguments = ['node', 'ionic', 'config', 'set']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicProject, 'load').andCallFake(function() { + throw new Error('error happened'); + }); + spyOn(appLibUtils, 'fail'); + + configTask.run({}, argv); + expect(appLibUtils.fail).toHaveBeenCalledWith('error happened'); + }); + + it('should fail if the command passed is not valid', function() { + var processArguments = ['node', 'ionic', 'config', 'stuff']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicProject, 'load'); + spyOn(appLibUtils, 'fail'); + + configTask.run({}, argv); + expect(appLibUtils.fail).toHaveBeenCalledWith(jasmine.any(String)); + }); + + it('should call ioLib.writeIoConfig if build command is passed', function() { + var processArguments = ['node', 'ionic', 'config', 'build']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicProject, 'load'); + spyOn(ioLib, 'writeIoConfig'); + + configTask.run({}, argv); + expect(ioLib.writeIoConfig).toHaveBeenCalledWith(false, false, true); + }); + + it('should call ioLib.listConfig if info command is passed', function() { + var processArguments = ['node', 'ionic', 'config', 'info']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicProject, 'load'); + spyOn(ioLib, 'listConfig'); + + configTask.run({}, argv); + expect(ioLib.listConfig).toHaveBeenCalled(); + }); + + it('should fail if set is called without a key', function() { + var processArguments = ['node', 'ionic', 'config', 'set']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicProject, 'load'); + spyOn(ioLib, 'writeIoConfig'); + spyOn(appLibUtils, 'fail'); + + configTask.run({}, argv); + expect(ioLib.writeIoConfig).not.toHaveBeenCalled(); + expect(appLibUtils.fail).toHaveBeenCalledWith(jasmine.any(String)); + }); + + it('should call ioLib.writeIoConfig if unset command is used', function() { + var processArguments = ['node', 'ionic', 'config', 'unset', 'red']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicProject, 'load'); + spyOn(ioLib, 'writeIoConfig'); + + configTask.run({}, argv); + expect(ioLib.writeIoConfig).toHaveBeenCalledWith('red', '', false); + }); + + it('should call ioLib.writeIoConfig if set command and pass empty string if no value is provided', function() { + var processArguments = ['node', 'ionic', 'config', 'set', 'red']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicProject, 'load'); + spyOn(ioLib, 'writeIoConfig'); + + configTask.run({}, argv); + expect(ioLib.writeIoConfig).toHaveBeenCalledWith('red', '', true); + }); + + it('should call ioLib.writeIoConfig if set command', function() { + var processArguments = ['node', 'ionic', 'config', 'set', 'red', 'yes']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicProject, 'load'); + spyOn(ioLib, 'writeIoConfig'); + + configTask.run({}, argv); + expect(ioLib.writeIoConfig).toHaveBeenCalledWith('red', 'yes', true); + }); + }); +}); diff --git a/spec/tasks/docs.spec.js b/spec/tasks/docs.spec.js new file mode 100644 index 0000000000..bcd997dd22 --- /dev/null +++ b/spec/tasks/docs.spec.js @@ -0,0 +1,80 @@ +'use strict'; + +var rewire = require('rewire'); +var docs = rewire('../../lib/ionic/docs'); + +describe('docs command', function() { + describe('command settings', function() { + + it('should have a title', function() { + expect(docs.title).toBeDefined(); + expect(docs.title).not.toBeNull(); + expect(docs.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(docs.summary).toBeDefined(); + expect(docs.summary).not.toBeNull(); + expect(docs.summary.length).toBeGreaterThan(0); + }); + + it('should have a set of options with boolean defaults to true', function() { + expect(docs.options).not.toEqual(jasmine.any(Object)); + }); + }); + + describe('sanitizeUrl function', function() { + it('should replace $ with %24', function() { + var sanitizeUrl = docs.__get__('sanitizeUrl'); + var results = sanitizeUrl('http://ionic.io/$yo'); + expect(results).toEqual('http://ionic.io/%24yo'); + }); + }); + + describe('list function', function() { + + }); + + describe('openDefault function', function() { + + }); + + describe('lookupCommand function', function() { + + }); + + describe('run', function() { + it('should call list if command is ls', function() { + var listSpy = jasmine.createSpy('listSpy'); + var revert = docs.__set__('list', listSpy); + var argv = { + _: ['docs', 'ls'] + }; + docs.run({}, argv); + expect(listSpy).toHaveBeenCalled(); + revert(); + }); + + it('should call openDefault if command is missing', function() { + var openDefaultSpy = jasmine.createSpy('openDefaultSpy'); + var revert = docs.__set__('openDefault', openDefaultSpy); + var argv = { + _: ['docs'] + }; + docs.run({}, argv); + expect(openDefaultSpy).toHaveBeenCalled(); + revert(); + }); + + it('should call list if command is not ls', function() { + var lookUpCommandSpy = jasmine.createSpy('lookUpCommandSpy'); + var revert = docs.__set__('lookUpCommand', lookUpCommandSpy); + var argv = { + _: ['docs', 'other'] + }; + docs.run({}, argv); + expect(lookUpCommandSpy).toHaveBeenCalledWith('other'); + revert(); + }); + }); +}); diff --git a/spec/tasks/emulate.spec.js b/spec/tasks/emulate.spec.js new file mode 100644 index 0000000000..b0dc4bee9d --- /dev/null +++ b/spec/tasks/emulate.spec.js @@ -0,0 +1,193 @@ +'use strict'; + +var Q = require('q'); +var optimist = require('optimist'); +var cordovaUtils = require('../../lib/utils/cordova'); +var os = require('os'); +var IonicAppLib = require('ionic-app-lib'); +var ConfigXml = IonicAppLib.configXml; +var log = IonicAppLib.logging.logger; +var emulate = require('../../lib/ionic/emulate'); + +describe('emulate command', function() { + beforeEach(function() { + spyOn(log, 'error'); + spyOn(ConfigXml, 'setConfigXml').andReturn(Q(true)); + }); + + describe('command settings', function() { + + it('should have a title', function() { + expect(emulate.title).toBeDefined(); + expect(emulate.title).not.toBeNull(); + expect(emulate.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(emulate.summary).toBeDefined(); + expect(emulate.summary).not.toBeNull(); + expect(emulate.summary.length).toBeGreaterThan(0); + }); + + it('should have a set of options with boolean defaults to true', function() { + expect(emulate.options).toEqual(jasmine.any(Object)); + expect(emulate.options['--consolelogs|-c']).toEqual(jasmine.any(Object)); + expect(emulate.options['--consolelogs|-c'].boolean).toEqual(true); + expect(emulate.options['--serverlogs|-s']).toEqual(jasmine.any(Object)); + expect(emulate.options['--serverlogs|-s'].boolean).toEqual(true); + expect(emulate.options['--debug|--release']).toEqual(jasmine.any(Object)); + expect(emulate.options['--debug|--release'].boolean).toEqual(true); + }); + }); + + + describe('cordova platform checks', function() { + + var appDirectory = '/ionic/app/path'; + var processArguments = ['node', 'ionic', 'emulate', '-n']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + beforeEach(function() { + spyOn(process, 'cwd').andReturn(appDirectory); + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + }); + + it('should default to iOS for the platform', function(done) { + spyOn(os, 'platform').andReturn('darwin'); + + emulate.run(null, argv, rawCliArguments).catch(function() { + expect(cordovaUtils.isPlatformInstalled).toHaveBeenCalledWith('ios', appDirectory); + done(); + }); + }); + + it('should fail if the system is not Mac and the platform is iOS', function(done) { + spyOn(os, 'platform').andReturn('windows'); + + emulate.run(null, argv, rawCliArguments).catch(function(error) { + expect(error).toEqual('✗ You cannot run iOS unless you are on Mac OSX.'); + done(); + }); + }); + }); + + + describe('cordova platform and plugin checks', function() { + + var appDirectory = '/ionic/app/path'; + var processArguments = ['node', 'ionic', 'emulate', 'ios', '-n']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + beforeEach(function() { + spyOn(os, 'platform').andReturn('darwin'); + spyOn(process, 'cwd').andReturn(appDirectory); + spyOn(cordovaUtils, 'installPlatform').andReturn(Q(true)); + spyOn(cordovaUtils, 'installPlugins').andReturn(Q(true)); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); + }); + + it('should try to install the cordova platform if it is not installed', function(done) { + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(false); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + + emulate.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.installPlatform).toHaveBeenCalledWith('ios'); + done(); + }); + }); + + it('should not try to install the cordova platform if it is installed', function(done) { + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + + emulate.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.installPlatform).not.toHaveBeenCalledWith(); + done(); + }); + }); + + it('should install plugins if they are not installed', function(done) { + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(false); + + emulate.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.arePluginsInstalled).toHaveBeenCalledWith(appDirectory); + expect(cordovaUtils.installPlugins).toHaveBeenCalledWith(); + done(); + }); + }); + + it('should not install plugins if they are installed', function(done) { + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + + emulate.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.arePluginsInstalled).toHaveBeenCalledWith(appDirectory); + expect(cordovaUtils.installPlugins).not.toHaveBeenCalledWith(); + done(); + }); + }); + }); + + describe('execute cordova command', function() { + var appDirectory = '/ionic/app/path'; + + beforeEach(function() { + spyOn(process, 'cwd').andReturn(appDirectory); + spyOn(os, 'platform').andReturn('darwin'); + }); + + it('should execute the command against the cordova util', function(done) { + var processArguments = ['node', 'ionic', 'emulate', '-n']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); + + emulate.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['emulate', '-n', 'ios'], false, true); + done(); + }); + }); + + it('should execute the command against the cordova util using the platform provided', function(done) { + var processArguments = ['node', 'ionic', 'emulate', 'android']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); + + emulate.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['emulate', 'android'], false, true); + done(); + }); + }); + + it('should execute the command against the cordova util using the platform provided', function(done) { + var processArguments = ['node', 'ionic', 'emulate', 'android', '--livereload']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + spyOn(cordovaUtils, 'setupLiveReload').andReturn(Q({ + blah: 'blah' + })); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); + + emulate.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.setupLiveReload).toHaveBeenCalledWith(argv); + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith( + ['emulate', 'android'], true, { blah: 'blah' }); + done(); + }); + }); + }); +}); diff --git a/spec/tasks/generate.spec.js b/spec/tasks/generate.spec.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/tasks/help.spec.js b/spec/tasks/help.spec.js new file mode 100644 index 0000000000..e38aa65601 --- /dev/null +++ b/spec/tasks/help.spec.js @@ -0,0 +1,91 @@ +'use strict'; + +var optimist = require('optimist'); +var help = require('../../lib/ionic/help'); +var helpUtils = require('../../lib/utils/help'); +var ionic = require('../../lib/cli'); + +describe('help command', function() { + describe('command settings', function() { + it('should have a title', function() { + expect(help.title).toBeDefined(); + expect(help.title).not.toBeNull(); + expect(help.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(help.summary).toBeDefined(); + expect(help.summary).not.toBeNull(); + expect(help.summary.length).toBeGreaterThan(0); + }); + + it('should have args', function() { + expect(help.args).toEqual(jasmine.any(Object)); + expect(help.args['[command]']).toEqual(jasmine.any(String)); + }); + }); + + describe('run function', function() { + it('should print all task help if a command is not provided', function() { + var processArguments = ['node', 'ionic', 'help']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(ionic, 'getAllTaskSettings').andReturn([ + { name: '1' }, + { name: '2' }, + { name: '3' } + ]); + spyOn(ionic, 'getTaskSettingsByName'); + spyOn(helpUtils, 'printTaskListUsage'); + + // Expect failure + help.run(ionic, argv, rawCliArguments); + expect(ionic.getAllTaskSettings).toHaveBeenCalled(); + expect(helpUtils.printTaskListUsage).toHaveBeenCalledWith([ + { name: '1' }, + { name: '2' }, + { name: '3' } + ], ionic.VERSION); + }); + + it('should print a help for a command if provided', function() { + var processArguments = ['node', 'ionic', 'help', 'red']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(ionic, 'getAllTaskSettings'); + spyOn(ionic, 'getTaskSettingsByName').andReturn({ + name: 'red', + summary: 'some summary' + }); + spyOn(helpUtils, 'printTaskListUsage'); + spyOn(helpUtils, 'printTaskUsage'); + + // Expect failure + help.run(ionic, argv, rawCliArguments); + expect(ionic.getAllTaskSettings).not.toHaveBeenCalled(); + expect(ionic.getTaskSettingsByName).toHaveBeenCalledWith('red'); + expect(helpUtils.printTaskUsage).toHaveBeenCalledWith({ + name: 'red', + summary: 'some summary' + }, ionic.VERSION); + }); + + it('should print an error if the command is not found', function() { + var processArguments = ['node', 'ionic', 'help', 'black']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(ionic, 'getTaskSettingsByName').andReturn(null); + spyOn(helpUtils, 'printTaskListUsage'); + spyOn(helpUtils, 'printTaskUsage'); + spyOn(console, 'log'); + + // Expect failure + help.run(ionic, argv, rawCliArguments); + expect(ionic.getTaskSettingsByName).toHaveBeenCalledWith('black'); + expect(console.log).toHaveBeenCalledWith(jasmine.any(String)); + }); + }); +}); diff --git a/spec/tasks/hooks.spec.js b/spec/tasks/hooks.spec.js new file mode 100644 index 0000000000..a8826de881 --- /dev/null +++ b/spec/tasks/hooks.spec.js @@ -0,0 +1,99 @@ +'use strict'; + +var optimist = require('optimist'); +var IonicAppLib = require('ionic-app-lib'); +var Hooks = IonicAppLib.hooks; +var log = IonicAppLib.logging.logger; +var appHooks = require('../../lib/ionic/hooks'); + +describe('hooks command', function() { + var appDirectory = '/ionic/app/path'; + + beforeEach(function() { + spyOn(log, 'error'); + spyOn(process, 'cwd').andReturn(appDirectory); + }); + + it('should log an error by default', function() { + var processArguments = ['node', 'ionic', 'hooks']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + // Expect failure + appHooks.run(null, argv, rawCliArguments); + expect(log.error).toHaveBeenCalledWith('Please supply a command - either add or remove'); + }); + + it('should log an error if the hooks command is unkown', function() { + var processArguments = ['node', 'ionic', 'hooks', 'other']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + // Expect failure + appHooks.run(null, argv, rawCliArguments); + expect(log.error).toHaveBeenCalledWith('Please supply a command - either add or remove'); + }); + + it('should log an error if an error is thrown', function() { + var processArguments = ['node', 'ionic', 'hooks', 'permissions']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + var errorText = 'oh no an error occurred.'; + + spyOn(Hooks, 'setHooksPermission').andCallFake(function() { + throw new Error(errorText); + }); + + // Expect failure + appHooks.run(null, argv, rawCliArguments); + expect(log.error).toHaveBeenCalledWith('There was an error running the hooks command: ', jasmine.any(String)); + }); + + it('should call ionic-app-lib when command is add', function() { + var processArguments = ['node', 'ionic', 'hooks', 'add']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(Hooks, 'add'); + + // Expect failure + appHooks.run(null, argv, rawCliArguments); + expect(Hooks.add).toHaveBeenCalledWith(appDirectory); + }); + + it('should call ionic-app-lib when command is remove', function() { + var processArguments = ['node', 'ionic', 'hooks', 'remove']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(Hooks, 'remove'); + + // Expect failure + appHooks.run(null, argv, rawCliArguments); + expect(Hooks.remove).toHaveBeenCalledWith(appDirectory); + }); + + it('should call ionic-app-lib when command is perm', function() { + var processArguments = ['node', 'ionic', 'hooks', 'perm']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(Hooks, 'setHooksPermission'); + + // Expect failure + appHooks.run(null, argv, rawCliArguments); + expect(Hooks.setHooksPermission).toHaveBeenCalledWith(appDirectory); + }); + + it('should call ionic-app-lib when command is permissions', function() { + var processArguments = ['node', 'ionic', 'hooks', 'permissions']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(Hooks, 'setHooksPermission'); + + // Expect failure + appHooks.run(null, argv, rawCliArguments); + expect(Hooks.setHooksPermission).toHaveBeenCalledWith(appDirectory); + }); +}); diff --git a/spec/tasks/info.spec.js b/spec/tasks/info.spec.js new file mode 100644 index 0000000000..3d532fd2c7 --- /dev/null +++ b/spec/tasks/info.spec.js @@ -0,0 +1,72 @@ +'use strict'; + +var optimist = require('optimist'); +var info = require('../../lib/ionic/info'); +var IonicAppLib = require('ionic-app-lib'); +var appLibUtils = IonicAppLib.utils; +var appLibInfo = IonicAppLib.info; +var log = IonicAppLib.logging.logger; + +describe('info command', function() { + describe('command settings', function() { + it('should have a title', function() { + expect(info.title).toBeDefined(); + expect(info.title).not.toBeNull(); + expect(info.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(info.summary).toBeDefined(); + expect(info.summary).not.toBeNull(); + expect(info.summary.length).toBeGreaterThan(0); + }); + }); + + describe('run function', function() { + var processArguments = ['node', 'ionic', 'info']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + it('should fail if any Info task throws', function() { + spyOn(appLibUtils, 'fail'); + spyOn(appLibInfo, 'gatherInfo').andCallFake(function() { + throw new Error('error stuff'); + }); + spyOn(appLibInfo, 'getIonicVersion'); + spyOn(appLibInfo, 'getIonicCliVersion'); + spyOn(appLibInfo, 'printInfo'); + spyOn(appLibInfo, 'checkRuntime'); + + // Expect failure + info.run(null, argv, rawCliArguments); + expect(appLibInfo.gatherInfo).toHaveBeenCalled(); + expect(appLibUtils.fail).toHaveBeenCalled(); + + expect(appLibInfo.getIonicVersion).not.toHaveBeenCalled(); + expect(appLibInfo.getIonicCliVersion).not.toHaveBeenCalled(); + expect(appLibInfo.printInfo).not.toHaveBeenCalled(); + expect(appLibInfo.checkRuntime).not.toHaveBeenCalled(); + }); + + it('should fail if any Info task throws', function() { + var gatheredInfo = { info: 'hi' }; + spyOn(appLibUtils, 'fail'); + spyOn(process, 'cwd').andReturn('/hello/how/areyou'); + spyOn(appLibInfo, 'gatherInfo').andReturn(gatheredInfo); + spyOn(appLibInfo, 'getIonicVersion'); + spyOn(appLibInfo, 'getIonicCliVersion'); + spyOn(appLibInfo, 'printInfo'); + spyOn(appLibInfo, 'checkRuntime'); + + // Expect failure + info.run(null, argv, rawCliArguments); + expect(appLibInfo.gatherInfo).toHaveBeenCalled(); + expect(appLibInfo.getIonicVersion).toHaveBeenCalledWith(gatheredInfo, '/hello/how/areyou'); + expect(appLibInfo.getIonicCliVersion).toHaveBeenCalledWith(gatheredInfo, jasmine.any(String)); + expect(appLibInfo.printInfo).toHaveBeenCalledWith(gatheredInfo); + expect(appLibInfo.checkRuntime).toHaveBeenCalled(); + + expect(appLibUtils.fail).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/spec/tasks/io.spec.js b/spec/tasks/io.spec.js new file mode 100644 index 0000000000..266af0bc8c --- /dev/null +++ b/spec/tasks/io.spec.js @@ -0,0 +1,115 @@ +'use strict'; + +var Q = require('q'); +var optimist = require('optimist'); +var rewire = require('rewire'); +var io = rewire('../../lib/ionic/io'); +var IonicAppLib = require('ionic-app-lib'); +var Login = IonicAppLib.login; +var IonicProject = IonicAppLib.project; +var appLibUtils = IonicAppLib.utils; +var ioLib = IonicAppLib.ioConfig; +var log = IonicAppLib.logging.logger; + +// TODO: lets not do this +var LoginTask = require('../../lib/ionic/login'); + +describe('io command', function() { + beforeEach(function() { + spyOn(log, 'info'); + }); + + describe('command settings', function() { + it('should have a title', function() { + expect(io.title).toBeDefined(); + expect(io.title).not.toBeNull(); + expect(io.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(io.summary).toBeDefined(); + expect(io.summary).not.toBeNull(); + expect(io.summary.length).toBeGreaterThan(0); + }); + + it('should have args', function() { + expect(io.args).toEqual(jasmine.any(Object)); + expect(io.args['']).toEqual(jasmine.any(String)); + }); + }); + + describe('run function', function() { + var processArguments = ['node', 'ionic', 'io', 'init']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + it('should fail if ionic project fails to laod', function() { + spyOn(appLibUtils, 'fail'); + spyOn(IonicProject, 'load').andCallFake(function() { + throw new Error('oh broken'); + }); + io.run({}, argv); + expect(appLibUtils.fail).toHaveBeenCalledWith('oh broken'); + }); + + it('should fail if retrieveLogin returns a rejected promise', function(done) { + var error = new Error('blah failed'); + spyOn(appLibUtils, 'fail'); + spyOn(IonicProject, 'load'); + spyOn(Login, 'retrieveLogin').andReturn(Q.reject(error)); + + io.run({}, argv).then(function() { + expect(Login.retrieveLogin).toHaveBeenCalled(); + expect(appLibUtils.fail).toHaveBeenCalledWith(error); + done(); + }); + }); + + it('should not call LoginTask if retrieveLogin returns a cookie jar', function(done) { + spyOn(IonicProject, 'load'); + spyOn(Login, 'retrieveLogin').andReturn(Q(true)); + spyOn(LoginTask, 'login'); + spyOn(process, 'cwd').andReturn('/blah/dir/'); + spyOn(ioLib, 'initIoPlatform'); + + io.run({}, argv).then(function() { + expect(Login.retrieveLogin).toHaveBeenCalled(); + expect(LoginTask.login).not.toHaveBeenCalled(); + expect(ioLib.initIoPlatform).toHaveBeenCalledWith('/blah/dir/', true); + done(); + }); + }); + + it('should call LoginTask if retrieveLogin returns no cookie jar', function(done) { + spyOn(IonicProject, 'load'); + spyOn(Login, 'retrieveLogin').andReturn(Q(null)); + spyOn(LoginTask, 'login').andReturn(Q(true)); + spyOn(process, 'cwd').andReturn('/blah/dir/'); + spyOn(ioLib, 'initIoPlatform'); + + io.run({}, argv).then(function() { + expect(Login.retrieveLogin).toHaveBeenCalled(); + expect(LoginTask.login).toHaveBeenCalledWith(argv); + expect(ioLib.initIoPlatform).toHaveBeenCalledWith('/blah/dir/', true); + done(); + }); + }); + + it('should fail if command is not init', function(done) { + processArguments = ['node', 'ionic', 'io', 'ini']; + rawCliArguments = processArguments.slice(2); + argv = optimist(rawCliArguments).argv; + + spyOn(IonicProject, 'load'); + spyOn(Login, 'retrieveLogin').andReturn(Q(true)); + spyOn(ioLib, 'initIoPlatform'); + spyOn(appLibUtils, 'fail'); + + io.run({}, argv).then(function() { + expect(Login.retrieveLogin).toHaveBeenCalled(); + expect(appLibUtils.fail).toHaveBeenCalledWith('Invalid command'); + done(); + }); + }); + }); +}); diff --git a/spec/tasks/lib.spec.js b/spec/tasks/lib.spec.js new file mode 100644 index 0000000000..7923c7f69b --- /dev/null +++ b/spec/tasks/lib.spec.js @@ -0,0 +1,124 @@ +'use strict'; + +var rewire = require('rewire'); +var path = require('path'); +var fs = require('fs'); +var optimist = require('optimist'); +var IonicAppLib = require('ionic-app-lib'); +var log = IonicAppLib.logging.logger; +var utils = IonicAppLib.utils; +var lib = rewire('../../lib/ionic/lib'); + +describe('lib command', function() { + beforeEach(function() { + spyOn(log, 'error'); + spyOn(utils, 'fail'); + }); + + describe('command settings', function() { + + it('should have a title', function() { + expect(lib.title).toBeDefined(); + expect(lib.title).not.toBeNull(); + expect(lib.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(lib.summary).toBeDefined(); + expect(lib.summary).not.toBeNull(); + expect(lib.summary.length).toBeGreaterThan(0); + }); + + it('should have a set of options with boolean defaults to true', function() { + expect(lib.options).toEqual(jasmine.any(Object)); + expect(lib.options['--version|-v']).toEqual(jasmine.any(String)); + }); + }); + + describe('lib commands', function() { + + it('should throw an error if the www directory is not found', function() { + var processArguments = ['node', 'ionic', 'lib', 'update']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(fs, 'existsSync').andReturn(false); + + // Expect failure + lib.run(null, argv, rawCliArguments); + expect(utils.fail).toHaveBeenCalledWith(jasmine.any(String), 'lib'); + }); + }); + + describe('loadVersionData method testing', function() { + var loadVersionData = lib.__get__('loadVersionData'); + + it('should load bower.json file if version.json does not exist', function() { + spyOn(fs, 'existsSync').andCallFake(function(path) { + return path === 'www/lib/ionic/bower.json'; + }); + spyOn(path, 'resolve').andCallFake(function(path) { + return path; + }); + spyOn(fs, 'readFileSync').andReturn('{ "stuff": "things" }'); + + var result = loadVersionData(); + expect(result.usesBower).toEqual(true); + expect(result.versionFilePath).toEqual('www/lib/ionic/bower.json'); + expect(result.local).toEqual({ stuff: 'things' }); + }); + + it('should load version.json if it does exist', function() { + spyOn(fs, 'existsSync').andCallFake(function(path) { + return path === 'www/lib/ionic/version.json'; + }); + spyOn(path, 'resolve').andCallFake(function(path) { + return path; + }); + spyOn(fs, 'readFileSync').andReturn('{ "stuff": "things" }'); + + var result = loadVersionData(); + expect(result.usesBower).toEqual(false); + expect(result.versionFilePath).toEqual('www/lib/ionic/version.json'); + expect(result.local).toEqual({ stuff: 'things' }); + }); + + it('should log error if version.json is malformed', function() { + spyOn(fs, 'existsSync').andCallFake(function(path) { + return path === 'www/lib/ionic/version.json'; + }); + spyOn(path, 'resolve').andCallFake(function(path) { + return path; + }); + spyOn(fs, 'readFileSync').andReturn('{ "stuff": "things }'); + + loadVersionData(); + expect(log.error).toHaveBeenCalledWith('Unable to load ionic lib version information'); + }); + }); + + /* + describe('updateLibVersion method testing', function() { + var updateLibVersion = lib.__get__('updateLibVersion'); + it('', function() { + updateLibVersion(); + }); + }); + + describe('printLibVersions method testing', function() { + var printLibVersions = lib.__get__('printLibVersions'); + + it('', function() { + printLibVersions(); + }); + }); + + describe('getVersionData method testing', function() { + var getVersionData = lib.__get__('getVersionData'); + + it('', function() { + getVersionData(); + }); + }); + */ +}); diff --git a/spec/tasks/link.spec.js b/spec/tasks/link.spec.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/tasks/list.spec.js b/spec/tasks/list.spec.js new file mode 100644 index 0000000000..50cdaa698e --- /dev/null +++ b/spec/tasks/list.spec.js @@ -0,0 +1,95 @@ +'use strict'; + +var optimist = require('optimist'); +var childProcess = require('child_process'); +var rewire = require('rewire'); +var list = rewire('../../lib/ionic/list'); +var IonicAppLib = require('ionic-app-lib'); +var appLibUtils = IonicAppLib.utils; +var fs = require('fs'); +var log = IonicAppLib.logging.logger; + +describe('list command', function() { + describe('command settings', function() { + it('should have a title', function() { + expect(list.title).toBeDefined(); + expect(list.title).not.toBeNull(); + expect(list.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(list.summary).toBeDefined(); + expect(list.summary).not.toBeNull(); + expect(list.summary.length).toBeGreaterThan(0); + }); + }); + + describe('run function', function() { + var processArguments = ['node', 'ionic', 'list', 'thing']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + beforeEach(function() { + spyOn(childProcess, 'exec'); + }); + + it('should fail if list Components throws', function() { + spyOn(appLibUtils, 'fail'); + var listComponentsSpy = jasmine.createSpy('listComponentsSpy'); + listComponentsSpy.andCallFake(function() { + throw new Error('something failed'); + }); + var revertListComponentsSpy = list.__set__('listComponents', listComponentsSpy); + + // Expect failure + list.run(null, argv, rawCliArguments); + expect(listComponentsSpy).toHaveBeenCalled(); + expect(appLibUtils.fail).toHaveBeenCalledWith('something failed', 'list'); + revertListComponentsSpy(); + }); + + it('should fail if list Components throws an error string', function() { + spyOn(appLibUtils, 'fail'); + var listComponentsSpy = jasmine.createSpy('listComponentsSpy'); + listComponentsSpy.andCallFake(function() { + throw 'something failed'; + }); + var revertListComponentsSpy = list.__set__('listComponents', listComponentsSpy); + + // Expect failure + list.run(null, argv, rawCliArguments); + expect(listComponentsSpy).toHaveBeenCalled(); + expect(appLibUtils.fail).toHaveBeenCalledWith('something failed', 'list'); + revertListComponentsSpy(); + }); + }); + + describe('listComponents function', function() { + + it('should call log.info for every component in devDependencies', function() { + spyOn(log, 'info'); + spyOn(fs, 'readFileSync'); + spyOn(JSON, 'parse').andReturn({ + devDependencies: { + stuff: '1', + things: '1' + } + }); + + var listComponents = list.__get__('listComponents'); + listComponents('thing'); + expect(log.info.calls[0].args[0]).toEqual('Ions, bower components, or addons installed:'); + expect(log.info.calls[1].args[0]).toMatch('stuff'); + expect(log.info.calls[2].args[0]).toMatch('things'); + }); + + it('should call childProcess exec and call util fail if result.code is not equal to 0', function() { + spyOn(log, 'error'); + spyOn(childProcess, 'exec').andReturn({ code: 1 }); + + var listComponents = list.__get__('listComponents'); + listComponents('thing'); + expect(log.error).toHaveBeenCalledWith(jasmine.any(String)); + }); + }); +}); diff --git a/spec/tasks/login.spec.js b/spec/tasks/login.spec.js new file mode 100644 index 0000000000..1bad90f05c --- /dev/null +++ b/spec/tasks/login.spec.js @@ -0,0 +1,183 @@ +'use strict'; + +var Q = require('q'); +var optimist = require('optimist'); +var prompt = require('prompt'); +var rewire = require('rewire'); +var login = rewire('../../lib/ionic/login'); +var IonicAppLib = require('ionic-app-lib'); +var log = IonicAppLib.logging.logger; +var appLibLogin = IonicAppLib.login; + +describe('login command', function() { + describe('command settings', function() { + it('should have a title', function() { + expect(login.title).toBeDefined(); + expect(login.title).not.toBeNull(); + expect(login.title.length).toBeGreaterThan(0); + }); + }); + + describe('run function', function() { + it('should call the login function', function() { + var processArguments = ['node', 'ionic', 'login']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var loginSpy = jasmine.createSpy('loginSpy'); + var revertLoginSpy = login.__set__('login', loginSpy); + + // Expect failure + login.run(null, argv); + expect(loginSpy).toHaveBeenCalledWith(argv); + revertLoginSpy(); + }); + }); + + describe('login function', function() { + it('should use argv email and password first.', function(done) { + var processArguments = ['node', 'ionic', 'login', '--email', 'josh@ionic.io', '--password', 'asdf1234']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var jar = { jar: 'contents' }; + spyOn(log, 'info'); + spyOn(login, 'promptForLogin'); + spyOn(appLibLogin, 'requestLogIn').andReturn(Q(jar)); + + login.login(argv).then(function(results) { + expect(results).toEqual(jar); + expect(login.promptForLogin).not.toHaveBeenCalled(); + expect(appLibLogin.requestLogIn).toHaveBeenCalledWith('josh@ionic.io', 'asdf1234', true); + done(); + }); + }); + + it('should use argv e and p second.', function(done) { + var processArguments = ['node', 'ionic', 'login', '--e', 'josh@ionic.io', '--p', 'asdf1234']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var jar = { jar: 'contents' }; + spyOn(log, 'info'); + spyOn(login, 'promptForLogin'); + spyOn(appLibLogin, 'requestLogIn').andReturn(Q(jar)); + + login.login(argv).then(function(results) { + expect(results).toEqual(jar); + expect(login.promptForLogin).not.toHaveBeenCalled(); + expect(appLibLogin.requestLogIn).toHaveBeenCalledWith('josh@ionic.io', 'asdf1234', true); + done(); + }); + }); + + it('should use environment variables last.', function(done) { + var processArguments = ['node', 'ionic', 'login']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + process.env.IONIC_EMAIL = 'josh@ionic.io'; + process.env.IONIC_PASSWORD = 'asdf1234'; + var jar = { jar: 'contents' }; + spyOn(log, 'info'); + spyOn(login, 'promptForLogin'); + spyOn(appLibLogin, 'requestLogIn').andReturn(Q(jar)); + + login.login(argv).then(function(results) { + expect(results).toEqual(jar); + expect(login.promptForLogin).not.toHaveBeenCalled(); + expect(appLibLogin.requestLogIn).toHaveBeenCalledWith('josh@ionic.io', 'asdf1234', true); + done(); + }); + delete process.env.IONIC_EMAIL; + delete process.env.IONIC_PASSWORD; + }); + + it('should prompt to ask for email and password when not available', function(done) { + var processArguments = ['node', 'ionic', 'login']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var jar = { jar: 'contents' }; + spyOn(log, 'info'); + + var promptForLoginSpy = jasmine.createSpy('promptForLoginSpy'); + promptForLoginSpy.andReturn(Q({ + email: 'josh@ionic.io', + password: 'asdf1234' + })); + var revertPromptForLoginSpy = login.__set__('promptForLogin', promptForLoginSpy); + spyOn(appLibLogin, 'requestLogIn').andReturn(Q(jar)); + + login.login(argv).then(function(results) { + expect(results).toEqual(jar); + expect(promptForLoginSpy).toHaveBeenCalledWith(argv); + expect(appLibLogin.requestLogIn).toHaveBeenCalledWith('josh@ionic.io', 'asdf1234', true); + revertPromptForLoginSpy(); + done(); + }); + }); + + it('should throw an error on a failed log in', function(done) { + var processArguments = ['node', 'ionic', 'login', '--email', 'josh@ionic.io', '--password', 'asdf1234']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var error = new Error('error occurred'); + spyOn(log, 'error'); + spyOn(login, 'promptForLogin'); + spyOn(appLibLogin, 'requestLogIn').andCallFake(function() { + throw error; + }); + + login.login(argv).catch(function(err) { + expect(log.error).toHaveBeenCalledWith('Error logging in'); + expect(err).toEqual(error); + done(); + }); + }); + }); + + describe('promptForLogin function', function() { + it('should ask for email and password and return a promise', function(done) { + var processArguments = ['node', 'ionic', 'login']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(log, 'info'); + spyOn(prompt, 'start'); + spyOn(prompt, 'get').andCallFake(function(schema, callback) { + callback(null, { email: 'josh@ionic.io', password: 'asdf1234' }); + }); + + var promptForLogin = login.__get__('promptForLogin'); + + promptForLogin(argv).then(function(results) { + expect(prompt.get).toHaveBeenCalled(); + expect(results.email).toEqual('josh@ionic.io'); + expect(results.password).toEqual('asdf1234'); + done(); + }); + }); + + it('should return a rejected promise on failure', function(done) { + var processArguments = ['node', 'ionic', 'login']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(log, 'info'); + spyOn(prompt, 'start'); + spyOn(prompt, 'get').andCallFake(function(schema, callback) { + callback('an error occurred', null); + }); + + var promptForLogin = login.__get__('promptForLogin'); + + promptForLogin(argv).catch(function(results) { + expect(prompt.get).toHaveBeenCalled(); + expect(results).toEqual('Error logging in: an error occurred'); + done(); + }); + }); + }); +}); diff --git a/spec/tasks/package.spec.js b/spec/tasks/package.spec.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/tasks/platform.spec.js b/spec/tasks/platform.spec.js new file mode 100644 index 0000000000..0a2540dd10 --- /dev/null +++ b/spec/tasks/platform.spec.js @@ -0,0 +1,105 @@ +'use strict'; + +var Q = require('q'); +var optimist = require('optimist'); +var cordovaUtils = require('../../lib/utils/cordova'); +var IonicAppLib = require('ionic-app-lib'); +var ConfigXml = IonicAppLib.configXml; +var IonicResources = IonicAppLib.resources; +var State = IonicAppLib.state; +var log = IonicAppLib.logging.logger; +var platform = require('../../lib/ionic/platform'); + +describe('platform command', function() { + beforeEach(function() { + spyOn(log, 'error'); + spyOn(ConfigXml, 'setConfigXml').andReturn(Q(true)); + }); + + describe('command settings', function() { + + it('should have a title', function() { + expect(platform.title).toBeDefined(); + expect(platform.title).not.toBeNull(); + expect(platform.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(platform.summary).toBeDefined(); + expect(platform.summary).not.toBeNull(); + expect(platform.summary.length).toBeGreaterThan(0); + }); + + it('should have args', function() { + expect(platform.args).toEqual(jasmine.any(Object)); + expect(platform.args['[options]']).toEqual(jasmine.any(String)); + expect(platform.args['']).toEqual(jasmine.any(String)); + }); + + it('should have options', function() { + expect(platform.options).toEqual(jasmine.any(Object)); + expect(platform.options['--noresources|-r']).toEqual(jasmine.any(Object)); + expect(platform.options['--nosave|-e']).toEqual(jasmine.any(Object)); + }); + }); + + describe('cordova command', function() { + var appDirectory = '/ionic/app/path'; + + beforeEach(function() { + spyOn(process, 'cwd').andReturn(appDirectory); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(0)); + }); + + it('should use commands provided', function(done) { + var processArguments = ['node', 'ionic', 'platform', '-n']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + platform.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['platform', '-n']); + done(); + }); + }); + + it('should execute cordova using commands provided', function(done) { + var processArguments = ['node', 'ionic', 'platform', 'add', 'ios']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicResources, 'copyIconFilesIntoResources').andReturn(Q(true)); + spyOn(IonicResources, 'addIonicIcons').andReturn(Q(true)); + spyOn(State, 'savePlatform'); + spyOn(State, 'removePlatform'); + + platform.run(null, argv, rawCliArguments).then(function() { + expect(IonicResources.copyIconFilesIntoResources).toHaveBeenCalledWith(appDirectory); + expect(IonicResources.addIonicIcons).toHaveBeenCalledWith(appDirectory, 'ios'); + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['platform', 'add', 'ios']); + expect(State.savePlatform).toHaveBeenCalledWith(appDirectory, 'ios'); + expect(State.removePlatform).not.toHaveBeenCalled(); + done(); + }); + }); + + it('should execute cordova using commands provided', function(done) { + var processArguments = ['node', 'ionic', 'platform', 'remove', 'ios']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicResources, 'copyIconFilesIntoResources').andReturn(Q(true)); + spyOn(IonicResources, 'addIonicIcons').andReturn(Q(true)); + spyOn(State, 'savePlatform'); + spyOn(State, 'removePlatform'); + + platform.run(null, argv, rawCliArguments).then(function() { + expect(IonicResources.copyIconFilesIntoResources).not.toHaveBeenCalled(); + expect(IonicResources.addIonicIcons).not.toHaveBeenCalled(); + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['platform', 'remove', 'ios']); + expect(State.savePlatform).not.toHaveBeenCalled(); + expect(State.removePlatform).toHaveBeenCalledWith(appDirectory, 'ios'); + done(); + }); + }); + }); +}); diff --git a/spec/tasks/plugin.spec.js b/spec/tasks/plugin.spec.js new file mode 100644 index 0000000000..89c034f738 --- /dev/null +++ b/spec/tasks/plugin.spec.js @@ -0,0 +1,102 @@ +'use strict'; + +var Q = require('q'); +var optimist = require('optimist'); +var cordovaUtils = require('../../lib/utils/cordova'); +var IonicAppLib = require('ionic-app-lib'); +var State = IonicAppLib.state; +var ConfigXml = IonicAppLib.configXml; +var log = IonicAppLib.logging.logger; +var plugin = require('../../lib/ionic/plugin'); + +describe('plugin command', function() { + beforeEach(function() { + spyOn(log, 'error'); + spyOn(ConfigXml, 'setConfigXml').andReturn(Q(true)); + }); + + describe('command settings', function() { + + it('should have a title', function() { + expect(plugin.title).toBeDefined(); + expect(plugin.title).not.toBeNull(); + expect(plugin.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(plugin.summary).toBeDefined(); + expect(plugin.summary).not.toBeNull(); + expect(plugin.summary.length).toBeGreaterThan(0); + }); + + it('should have args', function() { + expect(plugin.args).toEqual(jasmine.any(Object)); + expect(plugin.args['[options]']).toEqual(jasmine.any(String)); + expect(plugin.args['']).toEqual(jasmine.any(String)); + }); + + it('should have a boolean option of --nohooks or -n that defaults to true', function() { + expect(plugin.options).toEqual(jasmine.any(Object)); + expect(plugin.options['--searchpath ']).toEqual(jasmine.any(String)); + expect(plugin.options['--nosave|-e']).toEqual(jasmine.any(Object)); + }); + }); + + + describe('cordova platform checks', function() { + + var appDirectory = '/ionic/app/path'; + + beforeEach(function() { + spyOn(process, 'cwd').andReturn(appDirectory); + spyOn(log, 'info'); + }); + + it('should pass args on to cordovaUtils', function(done) { + var processArguments = ['node', 'ionic', 'plugin', '-n']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(0)); + + plugin.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['plugin', '-n']); + done(); + }); + }); + + it('should add plugins', function(done) { + var processArguments = ['node', 'ionic', 'plugin', 'add', 'thing', '--variable', 'hello']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(State, 'savePlugin'); + spyOn(State, 'removePlugin'); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(0)); + + plugin.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['plugin', 'add', 'thing', '--variable', 'hello']); + expect(State.savePlugin).toHaveBeenCalledWith(appDirectory, 'thing', ['hello']); + expect(State.removePlugin).not.toHaveBeenCalled(); + done(); + }); + }); + + it('should remove plugins', function(done) { + var processArguments = ['node', 'ionic', 'plugin', 'remove', 'thing']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(State, 'savePlugin'); + spyOn(State, 'removePlugin'); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(0)); + + plugin.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['plugin', 'remove', 'thing']); + expect(State.removePlugin).toHaveBeenCalledWith(appDirectory, 'thing'); + expect(State.savePlugin).not.toHaveBeenCalled(); + done(); + }); + }); + }); +}); diff --git a/spec/tasks/prepare.spec.js b/spec/tasks/prepare.spec.js new file mode 100644 index 0000000000..1cf646b07b --- /dev/null +++ b/spec/tasks/prepare.spec.js @@ -0,0 +1,45 @@ +'use strict'; + +var Q = require('q'); +var optimist = require('optimist'); +var cordovaUtils = require('../../lib/utils/cordova'); +var IonicAppLib = require('ionic-app-lib'); +var ConfigXml = IonicAppLib.configXml; +var log = IonicAppLib.logging.logger; +var prepare = require('../../lib/ionic/prepare'); + +describe('prepare command', function() { + beforeEach(function() { + spyOn(log, 'error'); + spyOn(ConfigXml, 'setConfigXml').andReturn(Q(true)); + }); + + describe('command settings', function() { + it('should have a title', function() { + expect(prepare.title).toBeDefined(); + expect(prepare.title).not.toBeNull(); + expect(prepare.title.length).toBeGreaterThan(0); + }); + }); + + describe('cordova platform and plugin checks', function() { + + var appDirectory = '/ionic/app/path'; + var processArguments = ['node', 'ionic', 'prepare']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + beforeEach(function() { + spyOn(process, 'cwd').andReturn(appDirectory); + }); + + it('should try to install the cordova platform if it is not installed', function(done) { + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); + + prepare.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['prepare']); + done(); + }); + }); + }); +}); diff --git a/spec/tasks/push.spec.js b/spec/tasks/push.spec.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/tasks/remove.spec.js b/spec/tasks/remove.spec.js new file mode 100644 index 0000000000..cad7420485 --- /dev/null +++ b/spec/tasks/remove.spec.js @@ -0,0 +1,135 @@ +'use strict'; + +var optimist = require('optimist'); +var childProcess = require('child_process'); +var rewire = require('rewire'); +var remove = rewire('../../lib/ionic/remove'); +var IonicAppLib = require('ionic-app-lib'); +var appLibUtils = IonicAppLib.utils; +var ioLib = IonicAppLib.ioConfig; +var log = IonicAppLib.logging.logger; +var bower = require('../../lib/utils/bower'); + +describe('remove command', function() { + describe('command settings', function() { + it('should have a title', function() { + expect(remove.title).toBeDefined(); + expect(remove.title).not.toBeNull(); + expect(remove.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(remove.summary).toBeDefined(); + expect(remove.summary).not.toBeNull(); + expect(remove.summary.length).toBeGreaterThan(0); + }); + + it('should have a args', function() { + expect(remove.args).toEqual(jasmine.any(Object)); + expect(remove.args['[name]']).toEqual(jasmine.any(String)); + }); + }); + + describe('run function', function() { + var processArguments = ['node', 'ionic', 'remove', 'thing']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + beforeEach(function() { + spyOn(childProcess, 'exec'); + }); + + it('should fail if bower is not installed', function() { + spyOn(appLibUtils, 'fail'); + spyOn(bower, 'checkForBower').andReturn(false); + + // Expect failure + remove.run(null, argv, rawCliArguments); + expect(appLibUtils.fail).toHaveBeenCalledWith(bower.installMessage, 'remove'); + }); + + it('should fail if uninstall throws an error obj message if the error has a message value', function() { + spyOn(ioLib, 'injectIoComponent'); + spyOn(ioLib, 'warnMissingData'); + spyOn(appLibUtils, 'fail'); + spyOn(bower, 'checkForBower').andReturn(true); + + var uninstallBowerSpy = jasmine.createSpy('uninstallBowerSpy'); + uninstallBowerSpy.andCallFake(function() { + throw new Error('something failed'); + }); + var revertUninstallBowerSpy = remove.__set__('uninstallBowerComponent', uninstallBowerSpy); + + // Expect failure + remove.run(null, argv, rawCliArguments); + expect(uninstallBowerSpy).toHaveBeenCalledWith('thing'); + expect(appLibUtils.fail).toHaveBeenCalledWith('something failed', 'service'); + + // TODO: things seems incorrect + expect(ioLib.injectIoComponent).toHaveBeenCalledWith(false, 'thing'); + expect(ioLib.warnMissingData).toHaveBeenCalled(); + revertUninstallBowerSpy(); + }); + + it('should fail if uninstallBowerComponent throws an error if the error has a message value', function() { + spyOn(ioLib, 'injectIoComponent'); + spyOn(ioLib, 'warnMissingData'); + spyOn(appLibUtils, 'fail'); + spyOn(bower, 'checkForBower').andReturn(true); + var uninstallBowerSpy = jasmine.createSpy('uninstallBowerSpy'); + uninstallBowerSpy.andCallFake(function() { + throw 'something'; + }); + var revertInstallBowerSpy = remove.__set__('uninstallBowerComponent', uninstallBowerSpy); + + // Expect failure + remove.run(null, argv, rawCliArguments); + expect(uninstallBowerSpy).toHaveBeenCalledWith('thing'); + expect(appLibUtils.fail).toHaveBeenCalledWith('something', 'service'); + + // TODO: things seems incorrect + expect(ioLib.injectIoComponent).toHaveBeenCalledWith(false, 'thing'); + expect(ioLib.warnMissingData).toHaveBeenCalled(); + revertInstallBowerSpy(); + }); + + it('should call injectIoComponent and warnMissingData on install success.', function() { + spyOn(ioLib, 'injectIoComponent'); + spyOn(ioLib, 'warnMissingData'); + spyOn(bower, 'checkForBower').andReturn(true); + + var uninstallBowerSpy = jasmine.createSpy('uninstallBowerSpy'); + var revertInstallBowerSpy = remove.__set__('uninstallBowerComponent', uninstallBowerSpy); + + remove.run(null, argv, rawCliArguments); + expect(bower.checkForBower).toHaveBeenCalled(); + expect(uninstallBowerSpy).toHaveBeenCalledWith('thing'); + expect(ioLib.injectIoComponent).toHaveBeenCalledWith(false, 'thing'); + expect(ioLib.warnMissingData).toHaveBeenCalled(); + revertInstallBowerSpy(); + }); + }); + + describe('uninstallBowerComponent function', function() { + + it('should call childProcess exec to install the bower component', function() { + spyOn(log, 'info'); + spyOn(childProcess, 'exec').andReturn({ code: 0 }); + + var uninstallBowerComponent = remove.__get__('uninstallBowerComponent'); + uninstallBowerComponent('thing'); + expect(childProcess.exec).toHaveBeenCalledWith('bower uninstall --save-dev thing'); + expect(log.info).toHaveBeenCalledWith(jasmine.any(String)); + }); + + it('should call childProcess exec and call util fail if result.code is not equal to 0', function() { + spyOn(appLibUtils, 'fail'); + spyOn(childProcess, 'exec').andReturn({ code: 1 }); + + var uninstallBowerComponent = remove.__get__('uninstallBowerComponent'); + uninstallBowerComponent('thing'); + expect(childProcess.exec).toHaveBeenCalledWith('bower uninstall --save-dev thing'); + expect(appLibUtils.fail).toHaveBeenCalledWith(jasmine.any(String), 'remove'); + }); + }); +}); diff --git a/spec/tasks/resources.spec.js b/spec/tasks/resources.spec.js new file mode 100644 index 0000000000..0103984c4e --- /dev/null +++ b/spec/tasks/resources.spec.js @@ -0,0 +1,86 @@ +'use strict'; + +var Q = require('q'); +var optimist = require('optimist'); +var resources = require('../../lib/ionic/resources'); +var IonicAppLib = require('ionic-app-lib'); +var Project = IonicAppLib.project; +var appLibUtils = IonicAppLib.utils; +var IonicResources = IonicAppLib.resources; + +describe('resources command', function() { + describe('command settings', function() { + it('should have a title', function() { + expect(resources.title).toBeDefined(); + expect(resources.title).not.toBeNull(); + expect(resources.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(resources.summary).toBeDefined(); + expect(resources.summary).not.toBeNull(); + expect(resources.summary.length).toBeGreaterThan(0); + }); + + it('should have options', function() { + expect(resources.options).toEqual(jasmine.any(Object)); + expect(resources.options['--icon|-i']).toEqual(jasmine.any(Object)); + expect(resources.options['--splash|-s']).toEqual(jasmine.any(Object)); + }); + }); + + describe('run function', function() { + it('should fail if project cannot be loaded', function() { + var processArguments = ['node', 'ionic', 'resources']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var error = new Error('an error occurred'); + spyOn(process, 'cwd').andReturn('/current/directory'); + spyOn(Project, 'load').andCallFake(function() { + throw error; + }); + spyOn(appLibUtils, 'fail'); + + // Expect failure + resources.run(null, argv, rawCliArguments); + expect(appLibUtils.fail).toHaveBeenCalledWith(error, 'resources'); + }); + + it('should fail if applib generate fails', function(done) { + var processArguments = ['node', 'ionic', 'resources']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var error = new Error('an error occurred'); + spyOn(process, 'cwd').andReturn('/current/directory'); + spyOn(Project, 'load'); + spyOn(IonicResources, 'generate').andReturn(Q.reject(error)); + spyOn(appLibUtils, 'fail'); + + // Expect failure + resources.run(null, argv, rawCliArguments).then(function() { + expect(appLibUtils.fail).toHaveBeenCalledWith(error, 'resources'); + done(); + }); + }); + + it('should pass platforms on to the applib generate', function(done) { + var processArguments = ['node', 'ionic', 'resources', '--icon']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(process, 'cwd').andReturn('/current/directory'); + spyOn(Project, 'load'); + spyOn(IonicResources, 'generate').andReturn(Q(true)); + spyOn(appLibUtils, 'fail'); + + // Expect failure + resources.run(null, argv, rawCliArguments).then(function() { + expect(IonicResources.generate).toHaveBeenCalledWith('/current/directory', jasmine.any(Object)); + expect(appLibUtils.fail).not.toHaveBeenCalled(); + done(); + }); + }); + }); +}); diff --git a/spec/tasks/run.spec.js b/spec/tasks/run.spec.js new file mode 100644 index 0000000000..4935c8dc47 --- /dev/null +++ b/spec/tasks/run.spec.js @@ -0,0 +1,190 @@ +'use strict'; + +var Q = require('q'); +var optimist = require('optimist'); +var cordovaUtils = require('../../lib/utils/cordova'); +var os = require('os'); +var childProcess = require('child_process'); +var IonicAppLib = require('ionic-app-lib'); +var ConfigXml = IonicAppLib.configXml; +var log = IonicAppLib.logging.logger; +var run = require('../../lib/ionic/run'); + +describe('run command', function() { + beforeEach(function() { + spyOn(log, 'error'); + spyOn(ConfigXml, 'setConfigXml').andReturn(Q(true)); + }); + + describe('command settings', function() { + + it('should have a title', function() { + expect(run.title).toBeDefined(); + expect(run.title).not.toBeNull(); + expect(run.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(run.summary).toBeDefined(); + expect(run.summary).not.toBeNull(); + expect(run.summary.length).toBeGreaterThan(0); + }); + + it('should have a boolean option of --nohooks or -n that defaults to true', function() { + expect(run.options).toEqual(jasmine.any(Object)); + expect(run.options['--consolelogs|-c']).toEqual(jasmine.any(Object)); + expect(run.options['--consolelogs|-c'].boolean).toEqual(true); + expect(run.options['--serverlogs|-s']).toEqual(jasmine.any(Object)); + expect(run.options['--serverlogs|-s'].boolean).toEqual(true); + expect(run.options['--debug|--release']).toEqual(jasmine.any(Object)); + expect(run.options['--debug|--release'].boolean).toEqual(true); + }); + }); + + + describe('cordova platform checks', function() { + + // This argv should model after optimist objects + // $ ionic run -n + var processArguments = ['node', 'ionic', 'run', '-n']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + var appDirectory = '/ionic/app/path'; + + beforeEach(function() { + spyOn(process, 'cwd').andReturn(appDirectory); + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); + }); + + it('should default to iOS for the platform', function(done) { + spyOn(os, 'platform').andReturn('darwin'); + + run.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.isPlatformInstalled).toHaveBeenCalledWith('ios', appDirectory); + done(); + }); + }); + + it('should fail if the system is not Mac and the platform is iOS', function(done) { + spyOn(os, 'platform').andReturn('windows'); + + run.run(null, argv, rawCliArguments).catch(function(error) { + expect(error).toEqual('✗ You cannot run iOS unless you are on Mac OSX.'); + done(); + }); + }); + }); + + + describe('cordova platform and plugin checks', function() { + + var appDirectory = '/ionic/app/path'; + var processArguments = ['node', 'ionic', 'run', '-n']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + beforeEach(function() { + spyOn(process, 'cwd').andReturn(appDirectory); + spyOn(os, 'platform').andReturn('darwin'); + + spyOn(cordovaUtils, 'installPlatform').andReturn(Q(true)); + spyOn(cordovaUtils, 'installPlugins').andReturn(Q(true)); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); + }); + + it('should try to install the cordova platform if it is not installed', function(done) { + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(false); + + run.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.installPlatform).toHaveBeenCalledWith('ios'); + done(); + }); + }); + + it('should not try to install the cordova platform if it is installed', function(done) { + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + + run.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.installPlatform).not.toHaveBeenCalledWith(); + done(); + }); + }); + + it('should install plugins if they are not installed', function(done) { + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(false); + + run.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.arePluginsInstalled).toHaveBeenCalledWith(appDirectory); + expect(cordovaUtils.installPlugins).toHaveBeenCalledWith(); + done(); + }); + }); + + it('should not install plugins if they are installed', function(done) { + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + + run.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.arePluginsInstalled).toHaveBeenCalledWith(appDirectory); + expect(cordovaUtils.installPlugins).not.toHaveBeenCalledWith(); + done(); + }); + }); + }); + + describe('execute cordova command', function() { + var appDirectory = '/ionic/app/path'; + + beforeEach(function() { + spyOn(process, 'cwd').andReturn(appDirectory); + spyOn(os, 'platform').andReturn('darwin'); + + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); + }); + + it('should execute the command against the cordova util', function(done) { + var processArguments = ['node', 'ionic', 'run', '-n']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + run.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['run', '-n', 'ios'], false, true); + done(); + }); + }); + + + it('should execute the command against the cordova util using the platform provided', function(done) { + var processArguments = ['node', 'ionic', 'run', 'android']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + run.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['run', 'android'], false, true); + done(); + }); + }); + + it('should execute the command against the cordova util using the platform provided', function(done) { + var processArguments = ['node', 'ionic', 'run', 'android', '--livereload']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(cordovaUtils, 'setupLiveReload').andReturn(Q({ + blah: 'blah' + })); + + run.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.setupLiveReload).toHaveBeenCalledWith(argv); + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith( + ['run', 'android'], true, { blah: 'blah' }); + done(); + }); + }); + }); +}); diff --git a/spec/tasks/security.spec.js b/spec/tasks/security.spec.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/tasks/serve.spec.js b/spec/tasks/serve.spec.js new file mode 100644 index 0000000000..ebbb91a980 --- /dev/null +++ b/spec/tasks/serve.spec.js @@ -0,0 +1,254 @@ +'use strict'; + +var optimist = require('optimist'); +var rewire = require('rewire'); +var Q = require('q'); +var serveTask = rewire('../../lib/ionic/serve'); +var IonicAppLib = require('ionic-app-lib'); +var IonicProject = IonicAppLib.project; +var Serve = IonicAppLib.serve; +var log = IonicAppLib.logging.logger; +var appLibUtils = IonicAppLib.utils; + +describe('Serve', function() { + + beforeEach(function() { + spyOn(log, 'info'); + }); + + describe('command settings', function() { + it('should have a title', function() { + expect(serveTask.title).toBeDefined(); + expect(serveTask.title).not.toBeNull(); + expect(serveTask.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(serveTask.summary).toBeDefined(); + expect(serveTask.summary).not.toBeNull(); + expect(serveTask.summary.length).toBeGreaterThan(0); + }); + + it('should have args', function() { + expect(serveTask.args).toEqual(jasmine.any(Object)); + expect(serveTask.args['[options]']).toEqual(jasmine.any(String)); + }); + + it('should have options', function() { + expect(serveTask.options).toEqual(jasmine.any(Object)); + expect(serveTask.options['--consolelogs|-c']).toEqual(jasmine.any(Object)); + expect(serveTask.options['--serverlogs|-s']).toEqual(jasmine.any(Object)); + expect(serveTask.options['--port|-p']).toEqual(jasmine.any(String)); + expect(serveTask.options['--livereload-port|-r']).toEqual(jasmine.any(String)); + expect(serveTask.options['--nobrowser|-b']).toEqual(jasmine.any(Object)); + expect(serveTask.options['--nolivereload|-d']).toEqual(jasmine.any(Object)); + expect(serveTask.options['--noproxy|-x']).toEqual(jasmine.any(Object)); + expect(serveTask.options['--address']).toEqual(jasmine.any(String)); + expect(serveTask.options['--all|-a']).toEqual(jasmine.any(Object)); + expect(serveTask.options['--browser|-w']).toEqual(jasmine.any(String)); + expect(serveTask.options['--browseroption|-o']).toEqual(jasmine.any(String)); + expect(serveTask.options['--lab|-l']).toEqual(jasmine.any(Object)); + expect(serveTask.options['--nogulp']).toEqual(jasmine.any(Object)); + expect(serveTask.options['--platform|-t']).toEqual(jasmine.any(String)); + }); + }); + + describe('run command', function() { + beforeEach(function() { + spyOn(process, 'cwd').andReturn('/some/project/dir'); + spyOn(log, 'error'); + }); + + it('should fail if loading the ionic project fails', function(done) { + var processArguments = ['node', 'ionic', 'serve', '--livereload', '--nogulp']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var error = new Error('some error'); + + spyOn(IonicProject, 'load').andCallFake(function() { + throw error; + }); + spyOn(appLibUtils, 'fail'); + + serveTask.run({}, argv); + expect(log.error).toHaveBeenCalledWith(jasmine.any(String), error); + expect(appLibUtils.fail).toHaveBeenCalledWith(error.message); + done(); + }); + + it('should fail if loading the ionic project fails', function(done) { + var processArguments = ['node', 'ionic', 'serve', '--livereload', '--nogulp']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var error = new Error('some error'); + spyOn(IonicProject, 'load').andReturn({ + get: function() { + return false; + } + }); + process.env = {}; + spyOn(Serve, 'getAddress').andCallFake(function() { + throw error; + }); + + spyOn(appLibUtils, 'fail'); + + serveTask.run({}, argv); + expect(appLibUtils.fail).toHaveBeenCalledWith('Error with serve- ' + error); + done(); + }); + + it('should return a rejected promise when chain fails', function(done) { + var processArguments = ['node', 'ionic', 'serve', '-a']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var error = new Error('some error'); + spyOn(IonicProject, 'load').andReturn({ + get: function() { + return false; + } + }); + process.env = {}; + spyOn(Serve, 'getAddress').andReturn(Q()); + spyOn(Serve, 'checkPorts').andReturn(Q.reject(error)); + + spyOn(Serve, 'start'); + spyOn(Serve, 'showFinishedServeMessage'); + + serveTask.run({}, argv).catch(function(errCaught) { + expect(Serve.checkPorts).toHaveBeenCalled(); + expect(Serve.start).not.toHaveBeenCalled(); + expect(Serve.showFinishedServeMessage).not.toHaveBeenCalled(); + + expect(log.info).toHaveBeenCalledWith('There was an error serving your Ionic application:', error); + expect(errCaught).toEqual(error); + done(); + }); + }); + + it('should use IP of 0.0.0.0 if -a is supplied', function(done) { + var processArguments = ['node', 'ionic', 'serve', '-a']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicProject, 'load').andReturn({ + get: function() { + return false; + } + }); + process.env = {}; + spyOn(Serve, 'getAddress').andReturn(Q()); + + spyOn(Serve, 'checkPorts'); + spyOn(Serve, 'start'); + spyOn(Serve, 'showFinishedServeMessage'); + + serveTask.run({}, argv).then(function() { + expect(Serve.getAddress).not.toHaveBeenCalled(); + expect(Serve.start.calls[0].args[0].address).toBe('0.0.0.0'); + done(); + }); + }); + + it('should use IP of 0.0.0.0 if --all is supplied', function(done) { + var processArguments = ['node', 'ionic', 'serve', '--all']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicProject, 'load').andReturn({ + get: function() { + return false; + } + }); + process.env = {}; + spyOn(Serve, 'getAddress').andReturn(Q()); + + spyOn(Serve, 'checkPorts'); + spyOn(Serve, 'start'); + spyOn(Serve, 'showFinishedServeMessage'); + + serveTask.run({}, argv).then(function() { + expect(Serve.getAddress).not.toHaveBeenCalled(); + expect(Serve.start.calls[0].args[0].address).toBe('0.0.0.0'); + done(); + }); + }); + + it('should use IP of --address if supplied', function(done) { + var processArguments = ['node', 'ionic', 'serve', '--address', '192.168.1.10']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicProject, 'load').andReturn({ + get: function() { + return false; + } + }); + process.env = {}; + spyOn(Serve, 'getAddress').andReturn(Q()); + + spyOn(Serve, 'checkPorts'); + spyOn(Serve, 'start'); + spyOn(Serve, 'showFinishedServeMessage'); + + serveTask.run({}, argv).then(function() { + expect(Serve.getAddress).not.toHaveBeenCalled(); + expect(Serve.start.calls[0].args[0].address).toBe('192.168.1.10'); + done(); + }); + }); + + it('should use connect live reload port from env var', function(done) { + var processArguments = ['node', 'ionic', 'serve', '--livereload', '--nogulp']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicProject, 'load').andReturn({}); + spyOn(Serve, 'loadSettings').andReturn({}); + spyOn(Serve, 'getAddress').andReturn(Q()); + + spyOn(Serve, 'checkPorts'); + spyOn(Serve, 'start'); + spyOn(Serve, 'showFinishedServeMessage'); + + process.env = { CONNECT_LIVE_RELOAD_PORT: 5000 }; + + var options = { + appDirectory: '/some/project/dir', + liveReloadPort: 5000, + nogulp: true + }; + + serveTask.run({}, argv).then(function() { + expect(Serve.start).toHaveBeenCalledWith(options); + done(); + }); + }); + + it('should use connect live reload port from default', function(done) { + var processArguments = ['node', 'ionic', 'serve', '--livereload']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicProject, 'load').andReturn({ + get: function() { + return false; + } + }); + process.env = {}; + spyOn(Serve, 'getAddress').andReturn(Q()); + + spyOn(Serve, 'checkPorts'); + spyOn(Serve, 'start'); + spyOn(Serve, 'showFinishedServeMessage'); + + serveTask.run({}, argv).then(function() { + expect(Serve.start.calls[0].args[0].liveReloadPort).toBe(35729); + done(); + }); + }); + }); +}); diff --git a/spec/tasks/service.spec.js b/spec/tasks/service.spec.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/tasks/setup.spec.js b/spec/tasks/setup.spec.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/tasks/share.spec.js b/spec/tasks/share.spec.js new file mode 100644 index 0000000000..56bb37fcc3 --- /dev/null +++ b/spec/tasks/share.spec.js @@ -0,0 +1,161 @@ +'use strict'; + +var Q = require('q'); +var optimist = require('optimist'); +var share = require('../../lib/ionic/share'); +var IonicAppLib = require('ionic-app-lib'); +var Project = IonicAppLib.project; +var Share = IonicAppLib.share; +var log = IonicAppLib.logging.logger; +var Login = IonicAppLib.login; +var appLibUtils = IonicAppLib.utils; + +var LoginTask = require('../../lib/ionic/login'); + +describe('share command', function() { + describe('command settings', function() { + it('should have a title', function() { + expect(share.title).toBeDefined(); + expect(share.title).not.toBeNull(); + expect(share.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(share.summary).toBeDefined(); + expect(share.summary).not.toBeNull(); + expect(share.summary.length).toBeGreaterThan(0); + }); + + it('should have args', function() { + expect(share.args).toEqual(jasmine.any(Object)); + expect(share.args['']).toEqual(jasmine.any(String)); + }); + }); + + describe('run function', function() { + it('should fail email is missing', function() { + var processArguments = ['node', 'ionic', 'share']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(appLibUtils, 'fail'); + + // Expect failure + share.run(null, argv, rawCliArguments); + expect(appLibUtils.fail).toHaveBeenCalledWith('Invalid command', 'share'); + }); + + it('should fail if project.load fails', function() { + var processArguments = ['node', 'ionic', 'share', 'josh@ionic.io']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var error = new Error('an error occurred'); + spyOn(Project, 'load').andCallFake(function() { + throw error; + }); + spyOn(appLibUtils, 'fail'); + + // Expect failure + share.run(null, argv, rawCliArguments); + expect(appLibUtils.fail).toHaveBeenCalledWith(error.message); + }); + + it('should fail if the project does not have an app_id', function() { + var processArguments = ['node', 'ionic', 'share', 'josh@ionic.io']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var projectSpy = jasmine.createSpyObj('promiseExec', ['get']); + spyOn(Project, 'load').andReturn(projectSpy); + projectSpy.get.andReturn(''); + spyOn(appLibUtils, 'fail'); + + // Expect failure + share.run(null, argv, rawCliArguments); + expect(appLibUtils.fail).toHaveBeenCalledWith('You must first upload the app to share it'); + }); + + it('should fail if the email provided does not have an @ sign within it', function() { + var processArguments = ['node', 'ionic', 'share', 'joshionic.io']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var projectSpy = jasmine.createSpyObj('promiseExec', ['get']); + spyOn(Project, 'load').andReturn(projectSpy); + projectSpy.get.andReturn('123'); + spyOn(appLibUtils, 'fail'); + + // Expect failure + share.run(null, argv, rawCliArguments); + expect(appLibUtils.fail).toHaveBeenCalledWith('Invalid email address', 'share'); + }); + + it('should fail if the login.retrieveLogin returns a rejected promise', function(done) { + var processArguments = ['node', 'ionic', 'share', 'josh@ionic.io']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(log, 'info'); + var projectSpy = jasmine.createSpyObj('promiseExec', ['get']); + spyOn(Project, 'load').andReturn(projectSpy); + projectSpy.get.andReturn('123'); + spyOn(Login, 'retrieveLogin').andReturn(Q.reject('error occurred')); + spyOn(appLibUtils, 'fail').andReturn(Q(true)); // TODO: hack for now + + // Expect failure + share.run(null, argv, rawCliArguments).then(function() { + expect(appLibUtils.fail).toHaveBeenCalledWith('error occurred'); + done(); + }); + }); + + it('should not ask a user to login if a previous session is found', function(done) { + var processArguments = ['node', 'ionic', 'share', 'josh@ionic.io']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(log, 'info'); + spyOn(process, 'cwd').andReturn('/stuff/things'); + var projectSpy = jasmine.createSpyObj('promiseExec', ['get']); + spyOn(Project, 'load').andReturn(projectSpy); + projectSpy.get.andReturn('123'); + spyOn(Login, 'retrieveLogin').andReturn(Q({ jar: 'contents' })); + spyOn(LoginTask, 'login'); + spyOn(Share, 'shareApp').andReturn(Q(true)); + + // Expect failure + share.run(null, argv, rawCliArguments).then(function() { + expect(LoginTask.login).not.toHaveBeenCalled(); + expect(Share.shareApp).toHaveBeenCalledWith( + '/stuff/things', { jar: 'contents' }, 'josh@ionic.io' + ); + done(); + }); + }); + + it('should ask a user to login if a previous session is not found', function(done) { + var processArguments = ['node', 'ionic', 'share', 'josh@ionic.io']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(log, 'info'); + spyOn(process, 'cwd').andReturn('/stuff/things'); + var projectSpy = jasmine.createSpyObj('promiseExec', ['get']); + spyOn(Project, 'load').andReturn(projectSpy); + projectSpy.get.andReturn('123'); + spyOn(Login, 'retrieveLogin').andReturn(Q(false)); + spyOn(LoginTask, 'login').andReturn(Q({ jar: 'contents' })); + spyOn(Share, 'shareApp').andReturn(Q(true)); + + // Expect failure + share.run(null, argv, rawCliArguments).then(function() { + expect(LoginTask.login).toHaveBeenCalledWith(argv); + expect(Share.shareApp).toHaveBeenCalledWith( + '/stuff/things', { jar: 'contents' }, 'josh@ionic.io' + ); + done(); + }); + }); + }); +}); diff --git a/spec/tasks/start.spec.js b/spec/tasks/start.spec.js new file mode 100644 index 0000000000..b87f0cbbc6 --- /dev/null +++ b/spec/tasks/start.spec.js @@ -0,0 +1,179 @@ +'use strict'; + +var fs = require('fs'); +var Q = require('q'); +var optimist = require('optimist'); +var rewire = require('rewire'); +var start = rewire('../../lib/ionic/start'); +var IonicAppLib = require('ionic-app-lib'); +var appLibUtils = IonicAppLib.utils; +var Start = IonicAppLib.start; +var log = IonicAppLib.logging.logger; +var templateUtils = require('../../lib/utils/templates'); + +describe('start command', function() { + beforeEach(function() { + spyOn(log, 'info'); + }); + + describe('command settings', function() { + it('should have a title', function() { + expect(start.title).toBeDefined(); + expect(start.title).not.toBeNull(); + expect(start.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(start.summary).toBeDefined(); + expect(start.summary).not.toBeNull(); + expect(start.summary.length).toBeGreaterThan(0); + }); + + it('should have args', function() { + expect(start.args).toEqual(jasmine.any(Object)); + expect(start.args['[options]']).toEqual(jasmine.any(String)); + expect(start.args['']).toEqual(jasmine.any(String)); + expect(start.args['[template]']).toEqual(jasmine.any(String)); + }); + + it('should have options', function() { + expect(start.options).toEqual(jasmine.any(Object)); + expect(start.options['--appname|-a']).toEqual(jasmine.any(String)); + expect(start.options['--id|-i']).toEqual(jasmine.any(String)); + expect(start.options['--skip-npm']).toEqual(jasmine.any(Object)); + expect(start.options['--no-cordova|-w']).toEqual(jasmine.any(Object)); + expect(start.options['--sass|-s']).toEqual(jasmine.any(Object)); + expect(start.options['--list|-l']).toEqual(jasmine.any(Object)); + expect(start.options['--io-app-id']).toEqual(jasmine.any(String)); + expect(start.options['--template|-t']).toEqual(jasmine.any(String)); + expect(start.options['--v2|-v']).toEqual(jasmine.any(Object)); + expect(start.options['--typescript|--ts']).toEqual(jasmine.any(Object)); + expect(start.options['--zip-file|-z']).toEqual(jasmine.any(String)); + }); + }); + + describe('run function', function() { + + it('should call list and return templates when list is passed as a command', function() { + var processArguments = ['node', 'ionic', 'start', '--list']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(templateUtils, 'listTemplates'); + spyOn(appLibUtils, 'preprocessCliOptions').andCallThrough(); + + start.run({}, argv); + expect(templateUtils.listTemplates).toHaveBeenCalled(); + expect(appLibUtils.preprocessCliOptions).not.toHaveBeenCalled(); + }); + + it('should fail if a dir is not supplied to start', function() { + var processArguments = ['node', 'ionic', 'start']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(appLibUtils, 'fail'); + spyOn(appLibUtils, 'preprocessCliOptions').andCallThrough(); + + start.run({}, argv); + expect(appLibUtils.fail).toHaveBeenCalled(); + expect(appLibUtils.preprocessCliOptions).not.toHaveBeenCalled(); + }); + + it('should fail if a the dir is equal to "."', function() { + var processArguments = ['node', 'ionic', 'start', '.']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(log, 'error'); + spyOn(appLibUtils, 'preprocessCliOptions').andCallThrough(); + + start.run({}, argv); + expect(log.error).toHaveBeenCalled(); + expect(appLibUtils.preprocessCliOptions).not.toHaveBeenCalled(); + }); + + it('should prompt for overwrite if the dir already exists, and cancel if user says no', function(done) { + var processArguments = ['node', 'ionic', 'start', 'existingDir']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(fs, 'existsSync').andReturn(true); + spyOn(appLibUtils, 'preprocessCliOptions').andCallThrough(); + spyOn(Start, 'promptForOverwrite').andReturn(Q(false)); + spyOn(Start, 'startApp'); + + start.run({}, argv).then(function() { + expect(appLibUtils.preprocessCliOptions).toHaveBeenCalled(); + expect(Start.promptForOverwrite).toHaveBeenCalled(); + expect(log.info).toHaveBeenCalled(); + expect(Start.startApp).not.toHaveBeenCalled(); + done(); + }); + }); + + it('should not prompt for overwrite if the dir does not exist', function(done) { + var processArguments = ['node', 'ionic', 'start', 'newDir']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(fs, 'existsSync').andReturn(false); + spyOn(appLibUtils, 'preprocessCliOptions').andCallThrough(); + spyOn(Start, 'promptForOverwrite'); + spyOn(Start, 'startApp').andReturn(Q(true)); + spyOn(Start, 'printQuickHelp').andReturn(Q(true)); + spyOn(Start, 'promptLogin').andReturn(Q(true)); + + start.run({}, argv).then(function() { + expect(appLibUtils.preprocessCliOptions).toHaveBeenCalled(); + expect(Start.promptForOverwrite).not.toHaveBeenCalled(); + expect(Start.startApp).toHaveBeenCalled(); + expect(Start.printQuickHelp).toHaveBeenCalled(); + expect(Start.promptLogin).toHaveBeenCalled(); + done(); + }); + }); + it('should provide extra content if starting a v2 project', function(done) { + var processArguments = ['node', 'ionic', 'start', 'newDir', '--v2']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(fs, 'existsSync').andReturn(false); + spyOn(appLibUtils, 'preprocessCliOptions').andCallThrough(); + spyOn(Start, 'promptForOverwrite'); + spyOn(Start, 'startApp').andReturn(Q(true)); + spyOn(Start, 'printQuickHelp').andReturn(Q(true)); + spyOn(Start, 'promptLogin').andReturn(Q(true)); + + start.run({}, argv).then(function() { + expect(log.info).toHaveBeenCalledWith( + '\nNew to Ionic? Get started here: http://ionicframework.com/docs/v2/getting-started\n' + ); + done(); + }); + }); + + it('should catch error if any process throws', function(done) { + var processArguments = ['node', 'ionic', 'start', 'newDir']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(fs, 'existsSync').andReturn(false); + spyOn(log, 'error'); + spyOn(appLibUtils, 'preprocessCliOptions').andCallThrough(); + spyOn(Start, 'promptForOverwrite'); + spyOn(Start, 'startApp').andReturn(Q.reject(false)); + spyOn(Start, 'printQuickHelp'); + spyOn(Start, 'promptLogin'); + + start.run({}, argv).catch(function() { + expect(appLibUtils.preprocessCliOptions).toHaveBeenCalled(); + expect(Start.promptForOverwrite).not.toHaveBeenCalled(); + expect(Start.startApp).toHaveBeenCalled(); + expect(Start.printQuickHelp).not.toHaveBeenCalled(); + expect(Start.promptLogin).not.toHaveBeenCalled(); + done(); + }); + }); + }); +}); diff --git a/spec/tasks/state.spec.js b/spec/tasks/state.spec.js new file mode 100644 index 0000000000..e0db32f2a8 --- /dev/null +++ b/spec/tasks/state.spec.js @@ -0,0 +1,154 @@ +'use strict'; + +var optimist = require('optimist'); +var rewire = require('rewire'); +var state = rewire('../../lib/ionic/state'); +var IonicAppLib = require('ionic-app-lib'); +var State = IonicAppLib.state; +var IonicProject = IonicAppLib.project; +var appLibUtils = IonicAppLib.utils; +var log = IonicAppLib.logging.logger; + +describe('state command', function() { + beforeEach(function() { + spyOn(log, 'info'); + }); + + describe('command settings', function() { + it('should have a title', function() { + expect(state.title).toBeDefined(); + expect(state.title).not.toBeNull(); + expect(state.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(state.summary).toBeDefined(); + expect(state.summary).not.toBeNull(); + expect(state.summary.length).toBeGreaterThan(0); + }); + + it('should have args', function() { + expect(state.args).toEqual(jasmine.any(Object)); + expect(state.args['']).toEqual(jasmine.any(String)); + }); + + it('should have options', function() { + expect(state.options).toEqual(jasmine.any(Object)); + expect(state.options.save).toEqual(jasmine.any(String)); + expect(state.options.restore).toEqual(jasmine.any(String)); + expect(state.options.clear).toEqual(jasmine.any(String)); + expect(state.options.reset).toEqual(jasmine.any(String)); + expect(state.options['--plugins']).toEqual(jasmine.any(Object)); + expect(state.options['--platforms']).toEqual(jasmine.any(Object)); + }); + }); + + describe('run function', function() { + + it('should fail if ionic project fails to laod', function() { + var processArguments = ['node', 'ionic', 'state', 'other']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(appLibUtils, 'fail'); + spyOn(IonicProject, 'load').andCallFake(function() { + throw new Error('oh broken'); + }); + + state.run({}, argv); + expect(appLibUtils.fail).toHaveBeenCalledWith('oh broken'); + }); + + it('should fail if command is not identified', function() { + var processArguments = ['node', 'ionic', 'state', 'other']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(log, 'error'); + spyOn(IonicProject, 'load'); + + state.run({}, argv); + expect(log.error).toHaveBeenCalledWith(jasmine.any(String)); + }); + + it('should call State.saveState if command is save', function() { + var processArguments = ['node', 'ionic', 'state', 'save']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicProject, 'load'); + spyOn(process, 'cwd').andReturn('/blah/dir'); + spyOn(State, 'saveState'); + + state.run({}, argv); + expect(State.saveState).toHaveBeenCalledWith('/blah/dir', { + platforms: true, + plugins: true + }); + }); + + it('should call State.restoreState if command is restore', function() { + var processArguments = ['node', 'ionic', 'state', 'restore']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicProject, 'load'); + spyOn(process, 'cwd').andReturn('/blah/dir'); + spyOn(State, 'restoreState'); + + state.run({}, argv); + expect(State.restoreState).toHaveBeenCalledWith('/blah/dir', { + platforms: true, + plugins: true + }); + }); + + it('should call State.resetState if command is reset', function() { + var processArguments = ['node', 'ionic', 'state', 'reset']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicProject, 'load'); + spyOn(process, 'cwd').andReturn('/blah/dir'); + spyOn(State, 'resetState'); + + state.run({}, argv); + expect(State.resetState).toHaveBeenCalledWith('/blah/dir', { + platforms: true, + plugins: true + }); + }); + + it('should call State.clearState if command is clear', function() { + var processArguments = ['node', 'ionic', 'state', 'clear']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicProject, 'load'); + spyOn(process, 'cwd').andReturn('/blah/dir'); + spyOn(State, 'clearState'); + + state.run({}, argv); + expect(State.clearState).toHaveBeenCalledWith('/blah/dir', { + platforms: true, + plugins: true + }); + }); + + it('should update options for platforms and plugins values if they are passed', function() { + var processArguments = ['node', 'ionic', 'state', 'clear', '--platforms', '--plugins']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicProject, 'load'); + spyOn(process, 'cwd').andReturn('/blah/dir'); + spyOn(State, 'clearState'); + + state.run({}, argv); + expect(State.clearState).toHaveBeenCalledWith('/blah/dir', { + platforms: true, + plugins: true + }); + }); + }); +}); diff --git a/spec/tasks/upload.spec.js b/spec/tasks/upload.spec.js new file mode 100644 index 0000000000..e79ec7fde0 --- /dev/null +++ b/spec/tasks/upload.spec.js @@ -0,0 +1,99 @@ +'use strict'; + +var Q = require('q'); +var optimist = require('optimist'); +var rewire = require('rewire'); +var upload = rewire('../../lib/ionic/upload'); +var IonicAppLib = require('ionic-app-lib'); +var Login = IonicAppLib.login; +var Upload = IonicAppLib.upload; +var appLibUtils = IonicAppLib.utils; +var log = IonicAppLib.logging.logger; + +// TODO: lets not do this +var LoginTask = require('../../lib/ionic/login'); + +describe('upload command', function() { + beforeEach(function() { + spyOn(log, 'info'); + }); + + describe('command settings', function() { + it('should have a title', function() { + expect(upload.title).toBeDefined(); + expect(upload.title).not.toBeNull(); + expect(upload.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(upload.summary).toBeDefined(); + expect(upload.summary).not.toBeNull(); + expect(upload.summary.length).toBeGreaterThan(0); + }); + + it('should have options', function() { + expect(upload.options).toEqual(jasmine.any(Object)); + expect(upload.options['--email|-e']).toEqual(jasmine.any(String)); + expect(upload.options['--password|-p']).toEqual(jasmine.any(String)); + expect(upload.options['--note']).toEqual(jasmine.any(String)); + expect(upload.options['--deploy ']).toEqual(jasmine.any(String)); + }); + }); + + describe('run function', function() { + it('should fail if ionic project fails to laod', function(done) { + var processArguments = ['node', 'ionic', 'upload']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var error = new Error('oh broken'); + spyOn(appLibUtils, 'fail'); + spyOn(Login, 'retrieveLogin').andReturn(Q.reject(error)); + + upload.run({}, argv).then(function() { + expect(appLibUtils.fail).toHaveBeenCalledWith(error); + done(); + }); + }); + + it('should ask the user to login if we cannot retrieve a saved session', function(done) { + var processArguments = ['node', 'ionic', 'upload', '--note', 'note text', '--deploy', 'dev']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var jar = { + my: 'jar' + }; + spyOn(process, 'cwd').andReturn('/my/pwd'); + spyOn(Login, 'retrieveLogin').andReturn(Q(false)); + spyOn(LoginTask, 'login').andReturn(Q(jar)); + spyOn(Upload, 'doUpload').andReturn(Q(true)); + + upload.run({}, argv).then(function() { + expect(Login.retrieveLogin).toHaveBeenCalled(); + expect(LoginTask.login).toHaveBeenCalledWith(argv); + expect(Upload.doUpload).toHaveBeenCalledWith('/my/pwd', jar, 'note text', 'dev'); + done(); + }); + }); + + it('should retrieve a saved session and use it', function(done) { + var processArguments = ['node', 'ionic', 'upload', '--note', 'note text', '--deploy', 'dev']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var jar = { + my: 'jar' + }; + spyOn(process, 'cwd').andReturn('/my/pwd'); + spyOn(Login, 'retrieveLogin').andReturn(Q(jar)); + spyOn(Upload, 'doUpload').andReturn(Q(true)); + + upload.run({}, argv).then(function() { + expect(Login.retrieveLogin).toHaveBeenCalled(); + expect(Upload.doUpload).toHaveBeenCalledWith('/my/pwd', jar, 'note text', 'dev'); + done(); + }); + }); + }); +}); diff --git a/spec/utils/bower.spec.js b/spec/utils/bower.spec.js new file mode 100644 index 0000000000..c9c2e117e3 --- /dev/null +++ b/spec/utils/bower.spec.js @@ -0,0 +1,174 @@ +'use strict'; + +var rewire = require('rewire'); +var childProcess = require('child_process'); +var bowerUtils = rewire('../../lib/utils/bower'); +var fs = require('fs'); +var path = require('path'); +var IonicAppLib = require('ionic-app-lib'); +var log = IonicAppLib.logging.logger; + +describe('setIonicVersion function', function() { + it('should get bower file data, and add devDependencies if they dont exist and write back', function() { + spyOn(bowerUtils, 'getData').andReturn({ + name: 'HelloIonic', + stuff: 'things', + private: 'true' + }); + spyOn(bowerUtils, 'saveData'); + + bowerUtils.setIonicVersion('2.0.0'); + + expect(bowerUtils.getData).toHaveBeenCalled(); + expect(bowerUtils.saveData).toHaveBeenCalledWith({ + name: 'HelloIonic', + stuff: 'things', + private: 'true', + devDependencies: { + ionic: 'driftyco/ionic-bower#2.0.0' + } + }); + }); + + it('should get bower file data, and update devDependencies if they do exist and write back', function() { + spyOn(bowerUtils, 'getData').andReturn({ + name: 'HelloIonic', + stuff: 'things', + private: 'true', + devDependencies: { + ionic: 'driftyco/ionic-bower#2.0.0', + bootstrap: 'twbs/bootstrap#3.3.6' + } + }); + spyOn(bowerUtils, 'saveData'); + + bowerUtils.setIonicVersion('2.1.0'); + + expect(bowerUtils.getData).toHaveBeenCalled(); + expect(bowerUtils.saveData).toHaveBeenCalledWith({ + name: 'HelloIonic', + stuff: 'things', + private: 'true', + devDependencies: { + ionic: 'driftyco/ionic-bower#2.1.0', + bootstrap: 'twbs/bootstrap#3.3.6' + } + }); + }); +}); + +describe('setAppName function', function() { + it('should get bower file and then write name back to bower file', function() { + var newName = 'New Name'; + + spyOn(bowerUtils, 'getData').andReturn({ + name: 'HelloIonic', + stuff: 'things', + private: 'true' + }); + spyOn(bowerUtils, 'saveData'); + + bowerUtils.setAppName(newName); + + expect(bowerUtils.getData).toHaveBeenCalled(); + expect(bowerUtils.saveData).toHaveBeenCalledWith({ + name: newName, + stuff: 'things', + private: 'true' + }); + }); +}); + +describe('getData function', function() { + it('should return default values if the bower file is not found', function() { + var defaults = { + name: 'HelloIonic', + private: 'true' + }; + + var result = bowerUtils.getData(); + expect(result).toEqual(defaults); + }); + + it('should throw an error if the bower file is found but malformed', function() { + + spyOn(fs, 'existsSync').andReturn(true); + spyOn(bowerUtils, 'getData').andCallThrough(); + spyOn(fs, 'readFileSync').andReturn('{ "hi": "I am broken}'); + try { + bowerUtils.getData(); + } catch (e) {} // eslint-disable-line no-empty + + expect(bowerUtils.getData).toThrow(); + }); + + it('should return contents of the project bower.json file', function() { + var json = { + name: 'bowerFile', + private: 'true' + }; + spyOn(fs, 'existsSync').andReturn(true); + spyOn(bowerUtils, 'getData').andCallThrough(); + spyOn(fs, 'readFileSync').andReturn(JSON.stringify(json)); + var results = bowerUtils.getData(); + + expect(results).toEqual(json); + }); +}); + +describe('saveData function', function() { + it('should save bower data to file', function() { + var bowerFilePath = '/path/to/bower/bower.json'; + var json = { + name: 'bowerFile', + private: 'true' + }; + spyOn(path, 'resolve').andReturn(bowerFilePath); + spyOn(fs, 'writeFileSync'); + + bowerUtils.saveData(json); + expect(fs.writeFileSync).toHaveBeenCalledWith(bowerFilePath, JSON.stringify(json, null, 2)); + }); + + it('should write an error to console if error occurs during write', function() { + var bowerFilePath = '/path/to/bower/bower.json'; + var json = { + name: 'bowerFile', + private: 'true' + }; + spyOn(path, 'resolve').andReturn(bowerFilePath); + spyOn(log, 'error'); + spyOn(fs, 'writeFileSync').andCallFake(function() { + throw new Error('error occurred'); + }); + + bowerUtils.saveData(json); + expect(log.error).toHaveBeenCalled(); + }); +}); + +describe('checkForBower function', function() { + it('should return true if bower is installed', function() { + spyOn(childProcess, 'exec').andReturn({ + output: '0' + }); + var result = bowerUtils.checkForBower(); + expect(result).toEqual(true); + }); + + it('should return false if bower exec returns "command not found"', function() { + spyOn(childProcess, 'exec').andReturn({ + output: 'command not found' + }); + var result = bowerUtils.checkForBower(); + expect(result).toEqual(false); + }); + + it('should return false if bower exec returns "not recognized"', function() { + spyOn(childProcess, 'exec').andReturn({ + output: 'not recognized' + }); + var result = bowerUtils.checkForBower(); + expect(result).toEqual(false); + }); +}); diff --git a/spec/utils/cordova.spec.js b/spec/utils/cordova.spec.js new file mode 100644 index 0000000000..f3641e51a7 --- /dev/null +++ b/spec/utils/cordova.spec.js @@ -0,0 +1,415 @@ +'use strict'; + +var fs = require('fs'); +var Q = require('q'); +var rewire = require('rewire'); +var childProcess = require('child_process'); +var cordovaUtils = rewire('../../lib/utils/cordova'); +var optimist = require('optimist'); +var IonicAppLib = require('ionic-app-lib'); +var Project = require('ionic-app-lib').project; +var Serve = require('ionic-app-lib').serve; +var ConfigXml = require('ionic-app-lib').configXml; +var log = IonicAppLib.logging.logger; + +describe('isPlatformInstalled', function() { + it('should return true if the platform directory does exist', function() { + spyOn(fs, 'statSync').andCallFake(function() { + return; + }); + + var result = cordovaUtils.isPlatformInstalled('ios', '/tmp'); + + expect(result).toEqual(true); + expect(fs.statSync).toHaveBeenCalledWith('/tmp/platforms/ios'); + }); + + it('should return false if the platform directory does not exist', function() { + spyOn(fs, 'statSync').andCallFake(function() { + throw new Error('Dir does not exist'); + }); + + var result = cordovaUtils.isPlatformInstalled('ios', '/tmp'); + + expect(result).toEqual(false); + expect(fs.statSync).toHaveBeenCalledWith('/tmp/platforms/ios'); + }); +}); + +describe('arePluginsInstalled', function() { + it('should return true if the plugins directory does exist', function() { + spyOn(fs, 'statSync').andCallFake(function() { + return; + }); + + var result = cordovaUtils.arePluginsInstalled('/tmp'); + + expect(result).toEqual(true); + expect(fs.statSync).toHaveBeenCalledWith('/tmp/plugins'); + }); + + it('should return false if the plugins directory does not exist', function() { + spyOn(fs, 'statSync').andCallFake(function() { + throw new Error('Dir does not exist'); + }); + + var result = cordovaUtils.arePluginsInstalled('/tmp'); + + expect(result).toEqual(false); + expect(fs.statSync).toHaveBeenCalledWith('/tmp/plugins'); + }); +}); + +describe('installPlatform', function() { + beforeEach(function() { + spyOn(log, 'info'); + }); + + it('should call promiseExec', function(done) { + var installPlatform = cordovaUtils.__get__('installPlatform'); + var promiseExecSpy = jasmine.createSpy('promiseExec'); + promiseExecSpy.andReturn(Q(true)); + var revertPromiseExecSpy = cordovaUtils.__set__('promiseExec', promiseExecSpy); + + + installPlatform('ios').then(function() { + expect(promiseExecSpy).toHaveBeenCalledWith('cordova platform add ios'); + revertPromiseExecSpy(); + done(); + }); + }); +}); + +describe('installPlugins', function() { + beforeEach(function() { + spyOn(log, 'info'); + }); + + it('should call promiseExec', function(done) { + var installPlugins = cordovaUtils.__get__('installPlugins'); + var promiseExecSpy = jasmine.createSpy('promiseExec'); + promiseExecSpy.andReturn(Q(true)); + var revertPromiseExecSpy = cordovaUtils.__set__('promiseExec', promiseExecSpy); + + installPlugins().then(function() { + expect(promiseExecSpy.calls[0].args[0]).toEqual('cordova plugin add --save cordova-plugin-device'); + expect(promiseExecSpy.calls[1].args[0]).toEqual('cordova plugin add --save cordova-plugin-console'); + expect(promiseExecSpy.calls[2].args[0]).toEqual('cordova plugin add --save cordova-plugin-whitelist'); + expect(promiseExecSpy.calls[3].args[0]).toEqual('cordova plugin add --save cordova-plugin-splashscreen'); + expect(promiseExecSpy.calls[4].args[0]).toEqual('cordova plugin add --save cordova-plugin-statusbar'); + expect(promiseExecSpy.calls[5].args[0]).toEqual('cordova plugin add --save ionic-plugin-keyboard'); + revertPromiseExecSpy(); + done(); + }); + }); +}); + + +describe('setupLiveReload', function() { + var argv; + var baseDir; + + beforeEach(function() { + spyOn(log, 'info'); + argv = {}; + baseDir = '/some/base/dir'; + }); + afterEach(function() { + argv = {}; + baseDir = ''; + }); + + it('should return options on success', function(done) { + argv.all = true; + var processCwd = '/process/current/pwd'; + var project = { name: 'something' }; + var loadSettings = { address: '127.0.0.1', port: 80 }; + var serveHostValue = 'devServer value'; + var finalOptions = { + port: 80, + address: '0.0.0.0', + appDirectory: baseDir, + runLivereload: true, + launchBrowser: false, + launchLab: false, + isPlatformServe: true, + devServer: serveHostValue + }; + + spyOn(process, 'cwd').andReturn(processCwd); + spyOn(Project, 'load').andReturn(project); + spyOn(Serve, 'loadSettings').andReturn(loadSettings); + spyOn(Serve, 'getAddress'); + spyOn(Serve, 'host').andReturn(serveHostValue); + spyOn(Serve, 'checkPorts').andReturn(Q(true)); + spyOn(ConfigXml, 'setConfigXml').andReturn(Q(true)); + spyOn(Serve, 'start'); + spyOn(Serve, 'showFinishedServeMessage'); + var setupLiveReload = cordovaUtils.__get__('setupLiveReload'); + + setupLiveReload(argv, baseDir).then(function(options) { + expect(Project.load).toHaveBeenCalledWith(baseDir); + expect(Serve.loadSettings).toHaveBeenCalledWith(argv, project); + expect(Serve.getAddress).not.toHaveBeenCalled(); + expect(Serve.host.calls[0].args).toEqual(['0.0.0.0', 80]); + expect(Serve.checkPorts).toHaveBeenCalledWith(true, 80, '0.0.0.0', finalOptions); + expect(ConfigXml.setConfigXml).toHaveBeenCalledWith(processCwd, { devServer: serveHostValue }); + expect(Serve.host.calls[1].args).toEqual(['0.0.0.0', 80]); + expect(Serve.start).toHaveBeenCalledWith(finalOptions); + expect(options).toEqual(finalOptions); + done(); + }); + }); + it('should use address if it is supplied', function(done) { + argv.address = '127.0.0.1'; + var processCwd = '/process/current/pwd'; + var project = { name: 'something' }; + var loadSettings = { address: '127.0.0.1', port: 80 }; + var serveHostValue = 'devServer value'; + var finalOptions = { + port: 80, + address: '127.0.0.1', + appDirectory: baseDir, + runLivereload: true, + launchBrowser: false, + launchLab: false, + isPlatformServe: true, + devServer: serveHostValue + }; + + spyOn(process, 'cwd').andReturn(processCwd); + spyOn(Project, 'load').andReturn(project); + spyOn(Serve, 'loadSettings').andReturn(loadSettings); + spyOn(Serve, 'getAddress'); + spyOn(Serve, 'host').andReturn(serveHostValue); + spyOn(Serve, 'checkPorts').andReturn(Q(true)); + spyOn(ConfigXml, 'setConfigXml').andReturn(Q(true)); + spyOn(Serve, 'start'); + spyOn(Serve, 'showFinishedServeMessage'); + var setupLiveReload = cordovaUtils.__get__('setupLiveReload'); + + setupLiveReload(argv, baseDir).then(function(options) { + expect(Project.load).toHaveBeenCalledWith(baseDir); + expect(Serve.loadSettings).toHaveBeenCalledWith(argv, project); + expect(Serve.getAddress).not.toHaveBeenCalled(); + expect(Serve.host.calls[0].args).toEqual(['127.0.0.1', 80]); + expect(Serve.checkPorts).toHaveBeenCalledWith(true, 80, '127.0.0.1', finalOptions); + expect(ConfigXml.setConfigXml).toHaveBeenCalledWith(processCwd, { devServer: serveHostValue }); + expect(Serve.host.calls[1].args).toEqual(['127.0.0.1', 80]); + expect(Serve.start).toHaveBeenCalledWith(finalOptions); + expect(options).toEqual(finalOptions); + done(); + }); + }); + it('should return options on success and gather address if not supplied', function(done) { + var processCwd = '/process/current/pwd'; + var project = { name: 'something' }; + var loadSettings = { address: '127.0.0.1', port: 80 }; + var serveHostValue = 'devServer value'; + var finalOptions = { + port: 80, + address: '80.80.80.80', + appDirectory: baseDir, + runLivereload: true, + launchBrowser: false, + launchLab: false, + isPlatformServe: true, + devServer: serveHostValue + }; + + spyOn(process, 'cwd').andReturn(processCwd); + spyOn(Project, 'load').andReturn(project); + spyOn(Serve, 'loadSettings').andReturn(loadSettings); + spyOn(Serve, 'getAddress').andCallFake(function(options) { + options.address = '80.80.80.80'; + return Q(true); + }); + spyOn(Serve, 'host').andReturn(serveHostValue); + spyOn(Serve, 'checkPorts').andReturn(Q(true)); + spyOn(ConfigXml, 'setConfigXml').andReturn(Q(true)); + spyOn(Serve, 'start'); + spyOn(Serve, 'showFinishedServeMessage'); + var setupLiveReload = cordovaUtils.__get__('setupLiveReload'); + + setupLiveReload(argv, baseDir).then(function(options) { + expect(Project.load).toHaveBeenCalledWith(baseDir); + expect(Serve.loadSettings).toHaveBeenCalledWith(argv, project); + expect(Serve.getAddress).toHaveBeenCalled(); + expect(Serve.host.calls[0].args).toEqual(['80.80.80.80', 80]); + expect(Serve.checkPorts).toHaveBeenCalledWith(true, 80, '80.80.80.80', finalOptions); + expect(ConfigXml.setConfigXml).toHaveBeenCalledWith(processCwd, { devServer: serveHostValue }); + expect(Serve.host.calls[1].args).toEqual(['80.80.80.80', 80]); + expect(Serve.start).toHaveBeenCalledWith(finalOptions); + expect(options).toEqual(finalOptions); + done(); + }); + }); + + it('should throw an error if the an error occurs within the promise chain', function(done) { + var processCwd = '/process/current/pwd'; + var project = { name: 'something' }; + var loadSettings = { address: '127.0.0.1', port: 80 }; + var serveHostValue = 'devServer value'; + + spyOn(process, 'cwd').andReturn(processCwd); + spyOn(Project, 'load').andReturn(project); + spyOn(Serve, 'loadSettings').andReturn(loadSettings); + spyOn(Serve, 'getAddress').andCallFake(function(options) { + options.address = '80.80.80.80'; + return Q(true); + }); + spyOn(Serve, 'host').andReturn(serveHostValue); + spyOn(Serve, 'checkPorts').andReturn(Q.reject('AN ERROR OCCURRED')); + spyOn(ConfigXml, 'setConfigXml').andReturn(Q(true)); + spyOn(Serve, 'start'); + spyOn(Serve, 'showFinishedServeMessage'); + var setupLiveReload = cordovaUtils.__get__('setupLiveReload'); + + setupLiveReload(argv, baseDir).catch(function() { + expect(Project.load).toHaveBeenCalledWith(baseDir); + expect(Serve.loadSettings).toHaveBeenCalledWith(argv, project); + expect(Serve.getAddress).toHaveBeenCalled(); + expect(Serve.host.calls[0].args).toEqual(['80.80.80.80', 80]); + done(); + }); + }); +}); + +describe('execCordovaCommand', function() { + beforeEach(function() { + spyOn(childProcess, 'exec').andCallThrough(); + }); + + it('should execute the command against the cordova util', function(done) { + var optionList = [ + 'build', + '-n', + 'ios' + ]; + var isLiveReload = false; + var serveOptions = {}; + + cordovaUtils.execCordovaCommand(optionList, isLiveReload, serveOptions).catch(function() { + expect(childProcess.exec).toHaveBeenCalledWith('cordova build -n ios'); + done(); + }); + }); + + it('should execute the command against the cordova util using the platform provided', function(done) { + var optionList = [ + 'build', + 'android' + ]; + var isLiveReload = false; + var serveOptions = {}; + + cordovaUtils.execCordovaCommand(optionList, isLiveReload, serveOptions).catch(function() { + expect(childProcess.exec).toHaveBeenCalledWith('cordova build android'); + done(); + }); + }); +}); + +describe('filterArgumentsForCordova', function() { + + it('should always leave the ionic command in the cordova command list', function() { + var processArguments = ['node', 'ionic', 'build']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + var cmdName = argv._[0]; + + var resultArgs = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawCliArguments); + expect(resultArgs).toEqual(['build']); + }); + + it('should remove all commands from the ignore list', function() { + var processArguments = ['node', 'ionic', 'build', + + // Start - Ignore command list + '--livereload', '-l', + '--consolelogs', '-c', + '--serverlogs', '-s', + '--port', '-p', + '--livereload-port', + '-i', '-r', + + // End - Ignore command list + 'ios']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + var cmdName = argv._[0]; + + var resultArgs = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawCliArguments); + expect(resultArgs).toEqual(['build', 'ios']); + }); + + it('should remove the address parameter and the parameter that follows it', function() { + var processArguments = ['node', 'ionic', 'build', + '--address', '0.0.0.0', + '--blah', 'ios']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + var cmdName = argv._[0]; + + var resultArgs = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawCliArguments); + expect(resultArgs).toEqual(['build', '--blah', 'ios']); + }); + + it('should remove the port parameter and the parameter that follows it', function() { + var processArguments = ['node', 'ionic', 'build', + '--port', '80', + '--blah', 'ios']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + var cmdName = argv._[0]; + + var resultArgs = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawCliArguments); + expect(resultArgs).toEqual(['build', '--blah', 'ios']); + }); + + it('should remove the shorthand port parameter and the parameter that follows it', function() { + var processArguments = ['node', 'ionic', 'build', + '-p', '80', + '--blah', 'ios']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + var cmdName = argv._[0]; + + var resultArgs = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawCliArguments); + expect(resultArgs).toEqual(['build', '--blah', 'ios']); + }); + + it('should remove the livereload-port parameter and the parameter that follows it', function() { + var processArguments = ['node', 'ionic', 'build', + '--livereload-port', '80', + '--blah', 'ios']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + var cmdName = argv._[0]; + + var resultArgs = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawCliArguments); + expect(resultArgs).toEqual(['build', '--blah', 'ios']); + }); + + it('should ensure that the --target= parameter contains double quotes when quotes are removed', function() { + var processArguments = ['node', 'ionic', 'build', + '--target=ios']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + var cmdName = argv._[0]; + + var resultArgs = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawCliArguments); + expect(resultArgs).toEqual(['build', '--target="ios"']); + }); + it('should ensure that the --target= parameter contains double quotes when quotes are not removed', function() { + var processArguments = ['node', 'ionic', 'build', + '--target="ios"']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + var cmdName = argv._[0]; + + var resultArgs = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawCliArguments); + expect(resultArgs).toEqual(['build', '--target="ios"']); + }); +}); + diff --git a/spec/utils/help.spec.js b/spec/utils/help.spec.js new file mode 100644 index 0000000000..a3fa4a62cd --- /dev/null +++ b/spec/utils/help.spec.js @@ -0,0 +1,162 @@ +'use strict'; + +var rewire = require('rewire'); +var helpUtils = rewire('../../lib/utils/help'); +var IonicAppLib = require('ionic-app-lib'); +var log = IonicAppLib.logging.logger; + +describe('printIonic function', function() { + it('should return an array of strings', function() { + var printIonic = helpUtils.__get__('printIonic'); + var results = printIonic(); + + expect(results).toEqual(jasmine.any(Array)); + expect(results.length).toBeGreaterThan(0); + expect(results[0]).toEqual(jasmine.any(String)); + }); +}); + +describe('printTemplate function', function() { + it('should log an array as a new line seperated string', function() { + spyOn(log, 'info'); + var printTemplate = helpUtils.__get__('printTemplate'); + printTemplate(['red', 'green', '', null]); + + expect(log.info).toHaveBeenCalledWith('red\ngreen\n\n'); + }); +}); + +describe('printTaskListUsage method', function() { + it('should call printTaskDetails for all tasks with a summary', function() { + spyOn(log, 'info'); + spyOn(log, 'error'); + var printTaskRevert = helpUtils.__set__('printTaskDetails', function(param) { + log.error(param); + }); + + var taskList = [ + { + name: 'one', + summary: 'one summary' + }, + { + name: 'two', + summary: 'two summary' + }, + { + name: 'three' + } + ]; + helpUtils.printTaskListUsage(taskList, 'v2.0.0'); + expect(log.error.calls.length).toEqual(2); + printTaskRevert(); + }); +}); + +describe('printTaskListShortUsage method', function() { + it('should print ionic logo, a header and the each task details', function() { + var printIonicSpy = jasmine.createSpy('printIonicSpy'); + printIonicSpy.andReturn('ionic logo'); + var revertPrintIonic = helpUtils.__set__('printIonic', printIonicSpy); + + var printTemplateSpy = jasmine.createSpy('printTemplateSpy'); + var revertPrintTemplate = helpUtils.__set__('printTemplate', printTemplateSpy); + + spyOn(log, 'info'); + var taskList = [ + { + name: 'one', + summary: 'one summary' + }, + { + name: 'two', + summary: 'two summary' + }, + { + name: 'three' + } + ]; + helpUtils.printTaskListShortUsage(taskList, null, 'v2.0.0'); + var arrayArgs = printTemplateSpy.calls[0].args[0]; + expect(arrayArgs[0]).toEqual('ionic logo'); + expect(arrayArgs[1]).toEqual(''); + expect(arrayArgs[2]).toEqual('Usage: ionic task args'); + expect(arrayArgs[3]).toEqual(''); + expect(arrayArgs[4]).toEqual(''); + expect(arrayArgs[5]).toEqual('======================='); + expect(arrayArgs[6]).toEqual(null); + revertPrintIonic(); + revertPrintTemplate(); + }); + + it('should identify the task does not exist if taskname is provided', function() { + var printIonicSpy = jasmine.createSpy('printIonicSpy'); + printIonicSpy.andReturn('ionic logo'); + var revertPrintIonic = helpUtils.__set__('printIonic', printIonicSpy); + + var printTemplateSpy = jasmine.createSpy('printTemplateSpy'); + var revertPrintTemplate = helpUtils.__set__('printTemplate', printTemplateSpy); + + spyOn(log, 'info'); + var taskList = [ + { + name: 'one', + summary: 'one summary' + }, + { + name: 'two', + summary: 'two summary' + }, + { + name: 'three' + } + ]; + helpUtils.printTaskListShortUsage(taskList, 'fudge', 'v2.0.0'); + var arrayArgs = printTemplateSpy.calls[0].args[0]; + expect(arrayArgs[6]).toMatch('fudge is not a valid task'); + revertPrintIonic(); + revertPrintTemplate(); + }); +}); + +describe('printTaskUsage method', function() { + it('should log info', function() { + spyOn(log, 'info'); + spyOn(log, 'error'); + var printTaskRevert = helpUtils.__set__('printTaskDetails', function(param) { + log.error(param); + }); + + helpUtils.printTaskUsage({}, 'v2.0.0'); + expect(log.error.calls.length).toEqual(1); + printTaskRevert(); + }); +}); + +describe('printTaskDetails function', function() { + it('should write to process log', function() { + var settings = { + title: 'platform', + name: 'platform', + summary: 'Add platform target for building an Ionic app', + args: { + '[options]': '', + '': '' + }, + options: { + '--noresources|-r': { + title: 'Do not add default Ionic icons and splash screen resources', + boolean: true + }, + '--nosave|-e': { + title: 'Do not save the platform to the package.json file', + boolean: true + } + } + }; + spyOn(process.stdout, 'write'); + var printTaskDetails = helpUtils.__get__('printTaskDetails'); + printTaskDetails(settings); + expect(process.stdout.write).toHaveBeenCalled(); + }); +}); diff --git a/spec/utils/ionitron.spec.js b/spec/utils/ionitron.spec.js new file mode 100644 index 0000000000..bb6576fbc2 --- /dev/null +++ b/spec/utils/ionitron.spec.js @@ -0,0 +1,31 @@ +'use strict'; + +var rewire = require('rewire'); +var ionitron = rewire('../../lib/utils/ionitron'); +var IonicAppLib = require('ionic-app-lib'); +var log = IonicAppLib.logging.logger; + +describe('print function', function() { + it('should return an array of strings', function() { + spyOn(log, 'info'); + ionitron.print(); + + expect(log.info).toHaveBeenCalled(); + var printedLines = log.info.calls[0].args[0]; + expect(log.info).toHaveBeenCalled(); + expect(printedLines).not.toMatch( + '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@' + ); + }); + + it('should return an array of strings', function() { + spyOn(log, 'info'); + ionitron.print('es'); + + var printedLines = log.info.calls[0].args[0]; + expect(log.info).toHaveBeenCalled(); + expect(printedLines).not.toMatch( + '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@' + ); + }); +}); diff --git a/spec/utils/prompt.spec.js b/spec/utils/prompt.spec.js new file mode 100644 index 0000000000..f0a1275b40 --- /dev/null +++ b/spec/utils/prompt.spec.js @@ -0,0 +1,36 @@ +'use strict'; + +var prompt = require('prompt'); +var promptUtil = require('../../lib/utils/prompt'); + +describe('prompt function', function() { + it('should pass the prompt callback value to the resolved promise', function(done) { + spyOn(prompt, 'get').andCallFake(function(schema, callback) { + callback(null, 1000); + }); + promptUtil.prompt({}, {}).then(function(value) { + expect(value).toEqual(1000); + done(); + }); + }); + + it('should reject the promise with the error if an error occurs with a msg not equal to "cancelled"', function(done) { + spyOn(prompt, 'get').andCallFake(function(schema, callback) { + callback({ message: 'error happened' }, 1000); + }); + promptUtil.prompt({}, {}).catch(function(value) { + expect(value).toEqual({ message: 'error happened' }); + done(); + }); + }); + + it('should reject the promise with false if an error occurs with msg canceled', function(done) { + spyOn(prompt, 'get').andCallFake(function(schema, callback) { + callback({ message: 'canceled' }, 1000); + }); + promptUtil.prompt({}, {}).catch(function(value) { + expect(value).toEqual(false); + done(); + }); + }); +}); diff --git a/spec/utils/stats.spec.js b/spec/utils/stats.spec.js new file mode 100644 index 0000000000..7e28429770 --- /dev/null +++ b/spec/utils/stats.spec.js @@ -0,0 +1,297 @@ +'use strict'; + +var fs = require('fs'); +var rewire = require('rewire'); +var IonicStats = rewire('../../lib/utils/stats'); +var IonicInfoModule = rewire('ionic-app-lib').info; +var IonicAppLib = require('ionic-app-lib'); +var log = IonicAppLib.logging.logger; + +describe('Stats', function() { + + it('should have stats t method defined', function() { + expect(IonicStats.t).toBeDefined(); + }); + + describe('#t', function() { + var oldprocessargv; + + beforeEach(function() { + oldprocessargv = process.argv; + }); + + afterEach(function() { + process.argv = oldprocessargv; + }); + + it('should not track if process.argv is less than 3', function() { + process.argv = ['node', 'bin/ionic']; + + var mpSpy = jasmine.createSpy('mpSpy'); + var revertMpSpy = IonicStats.__set__('mp', mpSpy); + + var configSpy = jasmine.createSpyObj('ionicConfig', ['get']); + configSpy.get.andReturn(true); + + var revertConfig = IonicStats.__set__('ionicConfig', configSpy); + + IonicStats.t(); + + expect(configSpy.get).not.toHaveBeenCalled(); + expect(mpSpy).not.toHaveBeenCalled(); + revertMpSpy(); + revertConfig(); + }); + + it('should not track stats if opted out', function() { + process.argv = ['node', 'bin/ionic', 'start', 'foldername']; + + var mpSpy = jasmine.createSpy('mpSpy'); + var revertMpSpy = IonicStats.__set__('mp', mpSpy); + + var configSpy = jasmine.createSpyObj('ionicConfig', ['get']); + configSpy.get.andReturn(true); + + var revertConfig = IonicStats.__set__('ionicConfig', configSpy); + + IonicStats.t(); + + expect(configSpy.get).toHaveBeenCalled(); + expect(mpSpy).not.toHaveBeenCalled(); + revertMpSpy(); + revertConfig(); + }); + + it('should catch errors thrown mp', function() { + process.argv = ['node', 'bin/ionic', 'start', 'foldername']; + + spyOn(log, 'error'); + var configSpy = jasmine.createSpyObj('ionicConfig', ['get']); + + configSpy.get.andCallFake(function() { + throw new Error('Fake Error'); + }); + + var mpSpy = jasmine.createSpy('mpSpy'); + var revertMpSpy = IonicStats.__set__('mp', mpSpy); + var revertConfig = IonicStats.__set__('ionicConfig', configSpy); + + IonicStats.t(); + + expect(configSpy.get).toHaveBeenCalled(); + expect(mpSpy).not.toHaveBeenCalled(); + expect(log.error).toHaveBeenCalled(); + revertMpSpy(); + revertConfig(); + }); + + it('should track the correct command', function() { + process.argv = ['node', 'bin/ionic', 'update', 'foldername', '-w', 'android', 'ios']; + + spyOn(fs, 'readFileSync').andReturn('{ "version": "2.0.0-beta.25" }'); + + var mpSpy = jasmine.createSpy('mpSpy'); + var revertMpSpy = IonicStats.__set__('mp', mpSpy); + + spyOn(IonicInfoModule, 'getNodeVersion'); + spyOn(IonicInfoModule, 'getOsEnvironment'); + spyOn(IonicInfoModule, 'gatherGulpInfo').andCallFake(function(info) { + info.os = 'Mac OS X El Capitan'; + info.node = 'v5.10.1'; + info.gulp = 'v3.0.0'; + }); + + var configSpy = jasmine.createSpyObj('ionicConfig', ['get']); + configSpy.get.andReturn(false); + + var revertConfig = IonicStats.__set__('ionicConfig', configSpy); + + IonicStats.t(); + + expect(mpSpy).toHaveBeenCalledWith('update', { + ionic_version: '2.0.0-beta.25', // eslint-disable-line camelcase + cli_version: '2.0.0-beta.25', // eslint-disable-line camelcase + email: false, + account_id: false, // eslint-disable-line camelcase + /* + os: 'Mac OS X El Capitan', + gulp: 'v3.0.0', + node: 'v5.10.1', + */ + cli_release_tag: 'beta', // eslint-disable-line camelcase + '--no-cordova': true, + android: true, + ios: true, + platform: 'android,ios' + }); + revertMpSpy(); + revertConfig(); + }); + + it('should track the correct command with no platforms or releaseTags', function() { + process.argv = ['node', 'bin/ionic', 'update', 'foldername', '-w']; + + spyOn(fs, 'readFileSync').andReturn('{ "version": "2.0.0" }'); + + var mpSpy = jasmine.createSpy('mpSpy'); + var revertMpSpy = IonicStats.__set__('mp', mpSpy); + + spyOn(IonicInfoModule, 'getNodeVersion'); + spyOn(IonicInfoModule, 'getOsEnvironment'); + spyOn(IonicInfoModule, 'gatherGulpInfo').andCallFake(function(info) { + info.os = 'Mac OS X El Capitan'; + info.node = 'v5.10.1'; + info.gulp = 'v3.0.0'; + }); + + var configSpy = jasmine.createSpyObj('ionicConfig', ['get']); + configSpy.get.andReturn(false); + + var revertConfig = IonicStats.__set__('ionicConfig', configSpy); + + IonicStats.t(); + + expect(mpSpy).toHaveBeenCalledWith('update', { + ionic_version: '2.0.0', // eslint-disable-line camelcase + cli_version: '2.0.0', // eslint-disable-line camelcase + email: false, + account_id: false, // eslint-disable-line camelcase + /* + os: 'Mac OS X El Capitan', + gulp: 'v3.0.0', + node: 'v5.10.1', + */ + '--no-cordova': true + }); + revertMpSpy(); + revertConfig(); + }); + }); + + describe('mp function', function() { + it('should call track after getting unique Id from ionicConfig', function() { + var configSpy = jasmine.createSpyObj('ionicConfig', ['get', 'set']); + configSpy.get.andReturn('uniq121'); + + var trackSpy = jasmine.createSpy('trackSpy'); + var revertTrackSpy = IonicStats.__set__('track', trackSpy); + + var revertConfig = IonicStats.__set__('ionicConfig', configSpy); + var mp = IonicStats.__get__('mp'); + + mp('update', { + ionic_version: '2.0.0', // eslint-disable-line camelcase + cli_version: '2.0.0', // eslint-disable-line camelcase + email: false, + account_id: false, // eslint-disable-line camelcase + os: 'Mac OS X El Capitan', + gulp: 'v3.0.0', + node: 'v5.10.1', + '--no-cordova': true + }); + + expect(configSpy.get).toHaveBeenCalled(); + expect(configSpy.set).not.toHaveBeenCalled(); + expect(trackSpy).toHaveBeenCalledWith('update', 'uniq121', { + ionic_version: '2.0.0', // eslint-disable-line camelcase + cli_version: '2.0.0', // eslint-disable-line camelcase + email: false, + account_id: false, // eslint-disable-line camelcase + os: 'Mac OS X El Capitan', + gulp: 'v3.0.0', + node: 'v5.10.1', + '--no-cordova': true + }, jasmine.any(Function)); + revertTrackSpy(); + revertConfig(); + }); + + it('should call track after getting unique Id from createId', function() { + var configSpy = jasmine.createSpyObj('ionicConfig', ['get', 'set']); + configSpy.get.andReturn(null); + + var createIdSpy = jasmine.createSpy('createIdSpy'); + createIdSpy.andReturn('uniq454'); + var revertCreateIdSpy = IonicStats.__set__('createId', createIdSpy); + + var trackSpy = jasmine.createSpy('trackSpy'); + var revertTrackSpy = IonicStats.__set__('track', trackSpy); + + var revertConfig = IonicStats.__set__('ionicConfig', configSpy); + var mp = IonicStats.__get__('mp'); + + mp('update', { + ionic_version: '2.0.0', // eslint-disable-line camelcase + cli_version: '2.0.0', // eslint-disable-line camelcase + email: false, + account_id: false, // eslint-disable-line camelcase + os: 'Mac OS X El Capitan', + gulp: 'v3.0.0', + node: 'v5.10.1', + '--no-cordova': true + }); + + expect(configSpy.get).toHaveBeenCalled(); + expect(configSpy.set).toHaveBeenCalled(); + expect(trackSpy).toHaveBeenCalledWith('update', 'uniq454', { + ionic_version: '2.0.0', // eslint-disable-line camelcase + cli_version: '2.0.0', // eslint-disable-line camelcase + email: false, + account_id: false, // eslint-disable-line camelcase + os: 'Mac OS X El Capitan', + gulp: 'v3.0.0', + node: 'v5.10.1', + '--no-cordova': true + }, jasmine.any(Function)); + + revertCreateIdSpy(); + revertTrackSpy(); + revertConfig(); + }); + }); + + describe('track function', function() { + it('should return a unique id', function() { + var track = IonicStats.__get__('track'); + var requestSpy = jasmine.createSpy('requestSpy'); + requestSpy.andCallFake(function(options, callback) { + callback(null, { code: 200 }, 'thumbs up'); + }); + var revertRequestSpy = IonicStats.__set__('request', requestSpy); + + + track('update', 'uniq676', { + ionic_version: '2.0.0' // eslint-disable-line camelcase + }, function(err, res, body) { + expect(requestSpy).toHaveBeenCalledWith({ + url: 'https://t.ionic.io/event/cli', + method: 'POST', + json: { + _event: 'update', + _uuid: 'uniq676', + data: { + ionic_version: '2.0.0' // eslint-disable-line camelcase + } + }, + proxy: null + }, jasmine.any(Function)); + + expect(err).toEqual(null); + expect(res.code).toEqual(200); + expect(body).toEqual('thumbs up'); + + revertRequestSpy(); + }); + }); + }); + + describe('createId function', function() { + it('should return a unique id', function() { + var createId = IonicStats.__get__('createId'); + var uniqueId = createId(); + var uniqueId2 = createId(); + + expect(uniqueId).not.toEqual(uniqueId2); + }); + }); +}); diff --git a/spec/utils/store.spec.js b/spec/utils/store.spec.js new file mode 100644 index 0000000000..cc354cd501 --- /dev/null +++ b/spec/utils/store.spec.js @@ -0,0 +1,211 @@ +'use strict'; + +var fs = require('fs'); +var IonicStore = require('../../lib/utils/store'); + +describe('IonicStore', function() { + + describe('IonicStore Constructor homedir', function() { + it('IonicStore should use HOME env variable first', function() { + process.env.HOME = '/home/ionicuser'; + process.env.USERPROFILE = '/home/ionicuser1'; + process.env.HOMEPATH = '/home/ionicuser2'; + spyOn(fs, 'existsSync').andReturn(true); + spyOn(fs, 'readFileSync').andReturn('{ "file": "store" }'); + + var store = new IonicStore('myfilename'); + + expect(store.homeDir).toEqual('/home/ionicuser'); + }); + + it('IonicStore should use HOME env variable second', function() { + delete process.env.HOME; + process.env.USERPROFILE = '/home/ionicuser1'; + process.env.HOMEPATH = '/home/ionicuser2'; + spyOn(fs, 'existsSync').andReturn(true); + spyOn(fs, 'readFileSync').andReturn('{ "file": "store" }'); + + var store = new IonicStore('myfilename'); + + expect(store.homeDir).toEqual('/home/ionicuser1'); + }); + + it('IonicStore should use HOME env variable third', function() { + delete process.env.HOME; + delete process.env.USERPROFILE; + process.env.HOMEPATH = '/home/ionicuser2'; + spyOn(fs, 'existsSync').andReturn(true); + spyOn(fs, 'readFileSync').andReturn('{ "file": "store" }'); + + var store = new IonicStore('myfilename'); + + expect(store.homeDir).toEqual('/home/ionicuser2'); + }); + }); + + describe('constructor values', function() { + it('should load file provided to constructor', function() { + process.env.HOME = '/home/ionicuser'; + spyOn(fs, 'existsSync').andReturn(true); + spyOn(fs, 'mkdirSync'); + spyOn(fs, 'readFileSync').andReturn('{ "file": "store" }'); + + var store = new IonicStore('myfilename'); + + expect(fs.mkdirSync).not.toHaveBeenCalled(); + expect(store.fileName).toEqual('myfilename.data'); + expect(store.homeDir).toEqual('/home/ionicuser'); + expect(store.privateDir).toEqual('/home/ionicuser/.ionic'); + expect(store.filePath).toEqual('/home/ionicuser/.ionic/myfilename.data'); + expect(store.data).toEqual({ + file: 'store' + }); + }); + + it('should create the privateDir if it does not exist', function() { + process.env.HOME = '/home/ionicuser'; + spyOn(fs, 'existsSync').andReturn(false); + spyOn(fs, 'mkdirSync'); + spyOn(fs, 'readFileSync').andReturn('{ "file": "store" }'); + + var store = new IonicStore('myfilename.json'); + + expect(fs.mkdirSync).toHaveBeenCalledWith('/home/ionicuser/.ionic'); + expect(store.fileName).toEqual('myfilename.json'); + expect(store.homeDir).toEqual('/home/ionicuser'); + expect(store.privateDir).toEqual('/home/ionicuser/.ionic'); + expect(store.filePath).toEqual('/home/ionicuser/.ionic/myfilename.json'); + expect(store.data).toEqual({ + file: 'store' + }); + }); + + it('should keep filename intact if it contains a dot', function() { + process.env.HOME = '/home/ionicuser'; + spyOn(fs, 'existsSync').andReturn(true); + spyOn(fs, 'mkdirSync'); + spyOn(fs, 'readFileSync').andReturn('{ "file": "store" }'); + + var store = new IonicStore('myfilename.json'); + + expect(store.fileName).toEqual('myfilename.json'); + expect(store.homeDir).toEqual('/home/ionicuser'); + expect(store.privateDir).toEqual('/home/ionicuser/.ionic'); + expect(store.filePath).toEqual('/home/ionicuser/.ionic/myfilename.json'); + expect(store.data).toEqual({ + file: 'store' + }); + }); + + it('should end early if no filename provided', function() { + process.env.HOME = '/home/ionicuser'; + spyOn(fs, 'existsSync').andReturn(true); + spyOn(fs, 'mkdirSync'); + spyOn(fs, 'readFileSync').andReturn('{ "file": "store" }'); + + var store = new IonicStore(); + + expect(store.fileName).toBeUndefined(); + expect(store.homeDir).toBeUndefined(); + expect(store.privateDir).toBeUndefined(); + expect(store.filePath).toBeUndefined(); + expect(store.data).toEqual({}); + }); + }); + + describe('get function', function() { + var store; + beforeEach(function() { + process.env.HOME = '/home/ionicuser'; + spyOn(fs, 'existsSync').andReturn(true); + spyOn(fs, 'mkdirSync'); + spyOn(fs, 'readFileSync').andReturn('{ "file": "store" }'); + store = new IonicStore('myfilename'); + }); + + it('should return the stored value if it exists', function() { + var result = store.get('file'); + expect(result).toEqual('store'); + }); + + it('should return undefined if the value does not exists', function() { + var result = store.get('file2'); + expect(result).toBeUndefined(); + }); + it('should return the entire store if no value is passed', function() { + var result = store.get(); + expect(result).toEqual({ file: 'store' }); + }); + }); + + describe('set function', function() { + var store; + beforeEach(function() { + process.env.HOME = '/home/ionicuser'; + spyOn(fs, 'existsSync').andReturn(true); + spyOn(fs, 'mkdirSync'); + spyOn(fs, 'readFileSync').andReturn('{ "file": "store" }'); + store = new IonicStore('myfilename'); + }); + + it('should update the keys value if the key exists', function() { + store.set('file', 'newval'); + expect(store.get('file')).toEqual('newval'); + }); + + it('should create the keys value if the key does not exist', function() { + store.set('newfile', 'newval'); + expect(store.get('newfile')).toEqual('newval'); + }); + }); + + describe('save function', function() { + var store; + beforeEach(function() { + process.env.HOME = '/home/ionicuser'; + spyOn(fs, 'existsSync').andReturn(true); + spyOn(fs, 'mkdirSync'); + spyOn(fs, 'readFileSync').andReturn('{ "file": "store" }'); + store = new IonicStore('myfilename'); + }); + + it('should return the stored value if it exists', function() { + spyOn(fs, 'writeFileSync'); + store.save(); + + expect(fs.writeFileSync).toHaveBeenCalledWith( + '/home/ionicuser/.ionic/myfilename.data', + '{\n "file": "store"\n}' + ); + }); + it('should return the stored value if it exists', function() { + spyOn(console, 'error'); + spyOn(fs, 'writeFileSync').andCallFake(function() { + throw new Error('error'); + }); + store.save(); + + expect(console.error).toHaveBeenCalled(); + }); + }); + + describe('remove function', function() { + var store; + beforeEach(function() { + process.env.HOME = '/home/ionicuser'; + spyOn(fs, 'existsSync').andReturn(true); + spyOn(fs, 'mkdirSync'); + spyOn(fs, 'readFileSync').andReturn('{ "file": "store" }'); + store = new IonicStore('myfilename'); + }); + + it('should key and value if the key exists', function() { + store.remove('file'); + expect(store.get('file')).toBeUndefined(); + }); + it('should not throw an error if the key/value being removed does not exist', function() { + store.remove('file2'); + expect(store.get('file2')).toBeUndefined(); + }); + }); +}); diff --git a/spec/utils/table.spec.js b/spec/utils/table.spec.js new file mode 100644 index 0000000000..495af59d7f --- /dev/null +++ b/spec/utils/table.spec.js @@ -0,0 +1,63 @@ +'use strict'; + +var cliTable = require('cli-table'); +var Table = require('../../lib/utils/table'); + +describe('Table prototype', function() { + it('should call through to cliTable with defaulted options', function() { + spyOn(cliTable, 'call').andCallThrough(); + + var t = new Table(); + + expect(cliTable.call).toHaveBeenCalledWith(t, { + chars: { + top: '', + 'top-mid': '', + 'top-left': '', + 'top-right': '', + bottom: '', + 'bottom-mid': '', + 'bottom-left': '', + 'bottom-right': '', + left: ' ', + 'left-mid': '', + right: '', + 'right-mid': '' + }, + style: { + compact: true, + head: ['yellow'] + } + }); + }); + + it('should call through to cliTable with extended options', function() { + spyOn(cliTable, 'call').andCallThrough(); + + var t = new Table({ + other: 'red' + }); + + expect(cliTable.call).toHaveBeenCalledWith(t, { + chars: { + top: '', + 'top-mid': '', + 'top-left': '', + 'top-right': '', + bottom: '', + 'bottom-mid': '', + 'bottom-left': '', + 'bottom-right': '', + left: ' ', + 'left-mid': '', + right: '', + 'right-mid': '' + }, + style: { + compact: true, + head: ['yellow'] + }, + other: 'red' + }); + }); +}); diff --git a/spec/utils/templates.spec.js b/spec/utils/templates.spec.js new file mode 100644 index 0000000000..0e75efd953 --- /dev/null +++ b/spec/utils/templates.spec.js @@ -0,0 +1,162 @@ +'use strict'; + +var Q = require('q'); +var rewire = require('rewire'); +var templateUtils = rewire('../../lib/utils/templates'); +var IonicAppLib = require('ionic-app-lib'); +var log = IonicAppLib.logging.logger; + +describe('listTemplates method', function() { + it('should should call fetchStarterTemplates pull out templates/sort and send to list', function(done) { + var fetchStarterTemplatesRevert = templateUtils.__set__('fetchStarterTemplates', function() { + return Q({ + items: [ + { + name: 'A Template', + description: 'A Description' + }, + { + name: 'C Template', + description: 'C Description' + }, + { + name: 'B Template', + description: 'B Description' + } + ] + }); + }); + var listRevert = templateUtils.__set__('list', function(list) { + return list; + }); + templateUtils.listTemplates().then(function(list) { + expect(list).toEqual([ + { + name: 'A Template', + description: 'A Description' + }, + { + name: 'B Template', + description: 'B Description' + }, + { + name: 'C Template', + description: 'C Description' + } + ]); + fetchStarterTemplatesRevert(); + listRevert(); + done(); + }); + }); + + it('should call fail fetchStarterTemplate throws', function(done) { + var error = 'Something failed'; + var fetchStarterTemplatesRevert = templateUtils.__set__('fetchStarterTemplates', function() { + return Q.reject(error); + }); + templateUtils.listTemplates().catch(function(err) { + expect(err).toEqual(error); + fetchStarterTemplatesRevert(); + done(); + }); + }); +}); + +describe('fetchStarterTemplates function', function() { + beforeEach(function() { + spyOn(log, 'info'); + }); + + it('use request to gather templates and return a promise', function(done) { + var templatesJson = { + items: [ + { + name: 'A Template', + description: 'A Description' + }, + { + name: 'C Template', + description: 'C Description' + }, + { + name: 'B Template', + description: 'B Description' + } + ] + }; + var templatesString = JSON.stringify(templatesJson); + var revertRequest = templateUtils.__set__('request', function(options, callback) { + callback(null, { statusCode: '200' }, templatesString); + }); + var fetchStarterTemplates = templateUtils.__get__('fetchStarterTemplates'); + + fetchStarterTemplates().then(function(templatesReturned) { + expect(templatesReturned).toEqual(templatesJson); + revertRequest(); + done(); + }); + }); + + it('use request to gather templates and fail on invalid json', function(done) { + var revertRequest = templateUtils.__set__('request', function(options, callback) { + callback(null, { statusCode: '200' }, '{'); + }); + spyOn(log, 'error'); + var fetchStarterTemplates = templateUtils.__get__('fetchStarterTemplates'); + + fetchStarterTemplates().catch(function() { + expect(log.error).toHaveBeenCalled(); + revertRequest(); + done(); + }); + }); + + it('use request to gather templates and fail on an response not equal to 200', function(done) { + var revertRequest = templateUtils.__set__('request', function(options, callback) { + callback(null, { statusCode: '400' }, null); + }); + spyOn(log, 'error'); + var fetchStarterTemplates = templateUtils.__get__('fetchStarterTemplates'); + + fetchStarterTemplates().catch(function() { + expect(log.error).toHaveBeenCalled(); + revertRequest(); + done(); + }); + }); +}); + +describe('list function', function() { + + it('on an response not equal to 200', function() { + var templates = [ + { + name: 'ionic-starter-a-template', + description: 'A Description' + }, + { + name: 'ionic-starter-b-template', + description: 'B Description' + }, + { + name: 'c-template', + description: 'C Description' + } + ]; + spyOn(log, 'info'); + var list = templateUtils.__get__('list'); + + list(templates); + expect(log.info.calls[0].args).toEqual(['\n']); + expect(log.info.calls[1].args[0]).toMatch('a-template'); // Use match because of colors + expect(log.info.calls[1].args[1]).toEqual('...........'); + expect(log.info.calls[1].args[2]).toEqual(templates[0].description); + expect(log.info.calls[2].args[0]).toMatch('b-template'); // Use match because of colors + expect(log.info.calls[2].args[1]).toEqual('...........'); + expect(log.info.calls[2].args[2]).toEqual(templates[1].description); + expect(log.info.calls[3].args[0]).toMatch('c-template'); // Use match because of colors + expect(log.info.calls[3].args[1]).toEqual('...........'); + expect(log.info.calls[3].args[2]).toEqual(templates[2].description); + }); +}); From 3505ce82c798803ee52d772bfb2a9e194e471db2 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 1 Jun 2016 16:32:20 -0500 Subject: [PATCH 0981/1100] Corrected an issue with the help command related to run configuration. --- CHANGELOG.md | 13 +++++++++++++ lib/ionic/browser.js | 1 - lib/ionic/run.js | 6 +++--- package.json | 4 ++-- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1202c010b..aba3dd67e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ + +# [2.0.0-beta.26](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.25...v2.0.0-beta.26) (2016-06-01) + + +### Bug Fixes +* **tests:** add tests to cover cli tasks and utilities. ([6c7a040](https://github.com/driftyco/ionic-cli/commit/6c7a040)) +* **eslint:** add parseInt to all request response checks because type coersion was removed. ([5e283ce](https://github.com/driftyco/ionic-cli/commit/5e283ce)) +* **login:** correct email regex that was split to change decrease line length. ([2dbd92c](https://github.com/driftyco/ionic-cli/commit/2dbd92c)) +* **serve:** remove unused lab preview template ([a100e8b](https://github.com/driftyco/ionic-cli/commit/a100e8b)) +* **share:** use app-lib project utility ([6317e76](https://github.com/driftyco/ionic-cli/commit/6317e76)) +* **store:** don't error if config isn't found ([cd72553](https://github.com/driftyco/ionic-cli/commit/cd72553)) + + # [2.0.0-beta.25](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.24...v2.0.0-beta.25) (2016-04-20) diff --git a/lib/ionic/browser.js b/lib/ionic/browser.js index bb9249dbb5..91f0d8b2de 100644 --- a/lib/ionic/browser.js +++ b/lib/ionic/browser.js @@ -17,7 +17,6 @@ var settings = { boolean: true } }, - module: './ionic/browser', isProjectTask: true }; diff --git a/lib/ionic/run.js b/lib/ionic/run.js index c0b2cc01d5..c03f6708ff 100644 --- a/lib/ionic/run.js +++ b/lib/ionic/run.js @@ -24,8 +24,7 @@ var cordovaRunEmulateOptions = { title: '', boolean: true }, - '--device|--emulator|--target=FOO': '', - isProjectTask: true + '--device|--emulator|--target=FOO': '' }; var settings = { @@ -36,7 +35,8 @@ var settings = { '[options]': '', '': '' }, - options: cordovaRunEmulateOptions + options: cordovaRunEmulateOptions, + isProjectTask: true }; function run(ionic, argv, rawCliArguments) { diff --git a/package.json b/package.json index 7c5af1585c..919dc95b4f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.0.0-beta.25", + "version": "2.0.0-beta.26", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -67,7 +67,7 @@ "gulp": "3.8.8", "gulp-util": "3.0.7", "inquirer": "0.11.2", - "ionic-app-lib": "2.0.0-beta.15", + "ionic-app-lib": "2.0.0-beta.16", "moment": "2.11.1", "open": "0.0.5", "optimist": "0.6.0", From 3f801038b80c8c1ff3b9b9bbc83ab11a37cf1fb4 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 1 Jun 2016 20:09:02 -0500 Subject: [PATCH 0982/1100] enhancement(start): change start task to default to typescript if project is started as v2. --- lib/ionic/start.js | 5 +++++ spec/tasks/start.spec.js | 27 ++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 72d540c10b..eb67ab8e57 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -62,6 +62,11 @@ function run(ionic, argv) { return templateUtils.listTemplates(); } + // If we are starting a version 2 project then typescript should be set by default + if ((argv.v2 || argv.v) && !argv.ts && !argv.typescript) { + argv.ts = true; + } + if (argv._.length < 2) { return appLibUtils.fail('Invalid command', 'start'); } diff --git a/spec/tasks/start.spec.js b/spec/tasks/start.spec.js index b87f0cbbc6..ebf6c6c807 100644 --- a/spec/tasks/start.spec.js +++ b/spec/tasks/start.spec.js @@ -133,7 +133,30 @@ describe('start command', function() { done(); }); }); - it('should provide extra content if starting a v2 project', function(done) { + + it('should default to typescript if starting a v2 project with -v', function(done) { + var processArguments = ['node', 'ionic', 'start', 'newDir', '-v']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(fs, 'existsSync').andReturn(false); + spyOn(appLibUtils, 'preprocessCliOptions').andCallThrough(); + spyOn(Start, 'promptForOverwrite'); + spyOn(Start, 'startApp').andReturn(Q(true)); + spyOn(Start, 'printQuickHelp').andReturn(Q(true)); + spyOn(Start, 'promptLogin').andReturn(Q(true)); + + start.run({}, argv).then(function() { + expect(appLibUtils.preprocessCliOptions.calls[0].args[0].v).toEqual(true); + expect(appLibUtils.preprocessCliOptions.calls[0].args[0].ts).toEqual(true); + expect(log.info).toHaveBeenCalledWith( + '\nNew to Ionic? Get started here: http://ionicframework.com/docs/v2/getting-started\n' + ); + done(); + }); + }); + + it('should default to typescript if starting a v2 project with --v2', function(done) { var processArguments = ['node', 'ionic', 'start', 'newDir', '--v2']; var rawCliArguments = processArguments.slice(2); var argv = optimist(rawCliArguments).argv; @@ -146,6 +169,8 @@ describe('start command', function() { spyOn(Start, 'promptLogin').andReturn(Q(true)); start.run({}, argv).then(function() { + expect(appLibUtils.preprocessCliOptions.calls[0].args[0].v2).toEqual(true); + expect(appLibUtils.preprocessCliOptions.calls[0].args[0].ts).toEqual(true); expect(log.info).toHaveBeenCalledWith( '\nNew to Ionic? Get started here: http://ionicframework.com/docs/v2/getting-started\n' ); From c655cce1763d4a9ea4c6fc1905b21db791a89c45 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 1 Jun 2016 20:14:59 -0500 Subject: [PATCH 0983/1100] chore(changelog): Updated changelog for beta26.w --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aba3dd67e5..9d2c3d55ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # [2.0.0-beta.26](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.25...v2.0.0-beta.26) (2016-06-01) +### Features + +* **start:** default all version 2 projects that are started to use typescript. + ### Bug Fixes * **tests:** add tests to cover cli tasks and utilities. ([6c7a040](https://github.com/driftyco/ionic-cli/commit/6c7a040)) From 56a9bec2bb65ffc4c3b0991435d2a1ed762d7851 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 1 Jun 2016 20:17:32 -0500 Subject: [PATCH 0984/1100] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d2c3d55ba..0908449a36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ### Features -* **start:** default all version 2 projects that are started to use typescript. +* **start:** default all v2 projects that are started to use typescript. ([3f80103](https://github.com/driftyco/ionic-cli/commit/3f80103)) ### Bug Fixes From 5549b828c87b2ea5cf29ccb1c562d519bb6ee1b5 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 2 Jun 2016 09:05:43 -0500 Subject: [PATCH 0985/1100] fix(commands): ensure that rawCliArguments are passed to all tasks that are ran from cli.js. --- lib/cli.js | 12 +++---- spec/cli.spec.js | 82 ++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 82 insertions(+), 12 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index b39089bb65..d975e472f3 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -141,7 +141,7 @@ Cli.run = function run(processArgv) { // No need to do project specific actions, cd to root, etc. if (!task.isProjectTask) { - return Q.fcall(task.run.bind(task), Cli, argv); + return Q.fcall(task.run.bind(task), Cli, argv, rawCliArguments); } var root = Utils.cdIonicRoot(); @@ -157,7 +157,7 @@ Cli.run = function run(processArgv) { } log.debug('node_modules directory not found, not running gulp hooks'); - return Q.fcall(task.run.bind(task), Cli, argv); + return Q.fcall(task.run.bind(task), Cli, argv, rawCliArguments); } try { @@ -184,17 +184,17 @@ Cli.run = function run(processArgv) { } log.debug('No gulpfile found, not running gulp hooks'); - return Q.fcall(task.run.bind(task), Cli, argv); + return Q.fcall(task.run.bind(task), Cli, argv, rawCliArguments); } - return Cli.runWithGulp(argv, task); + return Cli.runWithGulp(argv, task, rawCliArguments); } catch (ex) { return Utils.fail(ex); } }; -Cli.runWithGulp = function runWithGulp(argv, taskInstance) { +Cli.runWithGulp = function runWithGulp(argv, taskInstance, rawCliArguments) { var cmdName = argv._[0]; var beforeHook = cmdName + ':before'; var afterHook = cmdName + ':after'; @@ -229,7 +229,7 @@ Cli.runWithGulp = function runWithGulp(argv, taskInstance) { // run cmd .then(function() { - return Q.fcall(taskInstance.run.bind(taskInstance), Cli, argv); + return Q.fcall(taskInstance.run.bind(taskInstance), Cli, argv, rawCliArguments); }) // run afterHook diff --git a/spec/cli.spec.js b/spec/cli.spec.js index 14e249227d..b3758d3bb6 100644 --- a/spec/cli.spec.js +++ b/spec/cli.spec.js @@ -2,6 +2,7 @@ var IonicAppLib = require('ionic-app-lib'); var semver = require('semver'); +var optimist = require('optimist'); var Ionitron = require('../lib/utils/ionitron'); var Q = require('q'); var helpUtils = require('../lib/utils/help'); @@ -162,6 +163,10 @@ describe('Cli', function() { }); it('should change cwd to project root for project tasks', function(done) { + var processArguments = ['node', 'bin/ionic', 'fake']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + var FakeTask = { name: 'fake', title: 'fake', @@ -171,15 +176,77 @@ describe('Cli', function() { isProjectTask: true }; spyOn(IonicCli, 'getTaskSettingsByName').andReturn(FakeTask); + spyOn(FakeTask, 'run').andReturn(Q(true)); - IonicCli.run(['node', 'bin/ionic', 'fake']) + IonicCli.run(processArguments) .then(function() { expect(Utils.cdIonicRoot).toHaveBeenCalled(); + expect(FakeTask.run).toHaveBeenCalledWith(IonicCli, argv, rawCliArguments); + done(); + }); + }); + + it('should skip loading gulpfile if node_modules does not exist', function(done) { + var processArguments = ['node', 'bin/ionic', 'fake']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var FakeTask = { + name: 'fake', + title: 'fake', + run: function() { + return Q(true); + }, + isProjectTask: true + }; + spyOn(IonicCli, 'getTaskSettingsByName').andReturn(FakeTask); + spyOn(fs, 'existsSync').andReturn(false); + spyOn(FakeTask, 'run').andReturn(Q(true)); + spyOn(IonicCli, 'loadGulpfile'); + spyOn(IonicCli, 'runWithGulp'); + + IonicCli.run(processArguments) + .then(function() { + expect(IonicCli.loadGulpfile).not.toHaveBeenCalled(); + expect(IonicCli.runWithGulp).not.toHaveBeenCalled(); + expect(FakeTask.run).toHaveBeenCalledWith(IonicCli, argv, rawCliArguments); + done(); + }); + }); + + it('should skip runWithGulp if a gulpfile does not exist', function(done) { + var processArguments = ['node', 'bin/ionic', 'fake']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var FakeTask = { + name: 'fake', + title: 'fake', + run: function() { + return Q(true); + }, + isProjectTask: true + }; + spyOn(IonicCli, 'getTaskSettingsByName').andReturn(FakeTask); + spyOn(fs, 'existsSync').andReturn(true); + spyOn(IonicCli, 'loadGulpfile').andReturn(false); + spyOn(FakeTask, 'run').andReturn(Q(true)); + spyOn(IonicCli, 'runWithGulp'); + + IonicCli.run(processArguments) + .then(function() { + expect(IonicCli.loadGulpfile).toHaveBeenCalled(); + expect(IonicCli.runWithGulp).not.toHaveBeenCalled(); + expect(FakeTask.run).toHaveBeenCalledWith(IonicCli, argv, rawCliArguments); done(); }); }); it('should not change cwd to project root for non project tasks', function(done) { + var processArguments = ['node', 'bin/ionic', 'fake']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + var FakeTask = { name: 'fake', title: 'fake', @@ -189,10 +256,12 @@ describe('Cli', function() { isProjectTask: false }; spyOn(IonicCli, 'getTaskSettingsByName').andReturn(FakeTask); + spyOn(FakeTask, 'run').andReturn(Q(true)); - IonicCli.run(['node', 'bin/ionic', 'fake']) + IonicCli.run(processArguments) .then(function() { expect(Utils.cdIonicRoot).not.toHaveBeenCalled(); + expect(FakeTask.run).toHaveBeenCalledWith(IonicCli, argv, rawCliArguments); done(); }); }); @@ -267,7 +336,6 @@ describe('Cli', function() { IonicCli.run(['node', 'bin/ionic', 'fake']) .then(function() { expect(IonicCli.loadGulpfile).toHaveBeenCalled(); - expect(FakeTask.run).toHaveBeenCalled(); expect(IonicCli.runWithGulp).not.toHaveBeenCalled(); done(); }); @@ -557,6 +625,7 @@ describe('Cli', function() { describe('runWithGulp function', function() { var fakeTask; var argv; + var rawCliArguments; var qCallbacks; beforeEach(function() { @@ -572,6 +641,7 @@ describe('Cli', function() { _: ['fake'], v2: true }; + rawCliArguments = ['fake', '--v2']; gulp.tasks = { 'fake:before': function() {}, 'fake:after': function() {} @@ -598,7 +668,7 @@ describe('Cli', function() { it('should try to load gulp, exit if it fails', function() { spyOn(path, 'resolve').andReturn('./wrong_path'); - IonicCli.runWithGulp(argv, fakeTask); + IonicCli.runWithGulp(argv, fakeTask, rawCliArguments); expect(log.error).toHaveBeenCalledWith('\nGulpfile detected, but gulp is not installed'.red); expect(log.error).toHaveBeenCalledWith('Do you need to run `npm install`?\n'.red); @@ -611,12 +681,12 @@ describe('Cli', function() { it('should run logEvents, the command and the gulp hooks', function(done) { - IonicCli.runWithGulp(argv, fakeTask).then(function() { + IonicCli.runWithGulp(argv, fakeTask, rawCliArguments).then(function() { expect(IonicCli.logEvents).toHaveBeenCalled(); expect(gulp.start).toHaveBeenCalledWith('fake:before', qCallbacks[0]); expect(gulp.start).toHaveBeenCalledWith('fake:after', qCallbacks[1]); - expect(fakeTask.run).toHaveBeenCalledWith(IonicCli, argv); + expect(fakeTask.run).toHaveBeenCalledWith(IonicCli, argv, rawCliArguments); expect(log.error).not.toHaveBeenCalled(); expect(process.exit).not.toHaveBeenCalled(); From df20815abe63879f89bffcb13bf4aa8d0ba028a3 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 2 Jun 2016 09:15:31 -0500 Subject: [PATCH 0986/1100] chore(): update package.json and changelog for beta.27 release. --- CHANGELOG.md | 9 +++++++++ package.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0908449a36..5f700b3afa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ + +# [2.0.0-beta.27](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.25...v2.0.0-beta.27) (2016-06-02) + + +### Bug Fixes + +* **commands:** ensure that rawCliArguments are passed to all tasks that are ran from cli.js. ([5549b82](https://github.com/driftyco/ionic-cli/commit/5549b82)) + + # [2.0.0-beta.26](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.25...v2.0.0-beta.26) (2016-06-01) diff --git a/package.json b/package.json index 919dc95b4f..9b22176f5e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.0.0-beta.26", + "version": "2.0.0-beta.27", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From c30f9d8467f1cae0f46137dbb17836e8d8333a36 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 2 Jun 2016 10:03:45 -0500 Subject: [PATCH 0987/1100] fix(start): ensure that --no-ts flag provided to --v2 project start pulls the javascript version. --- lib/ionic/start.js | 2 +- spec/tasks/start.spec.js | 44 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index eb67ab8e57..8c1f934189 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -63,7 +63,7 @@ function run(ionic, argv) { } // If we are starting a version 2 project then typescript should be set by default - if ((argv.v2 || argv.v) && !argv.ts && !argv.typescript) { + if ((argv.v2 || argv.v) && !argv.hasOwnProperty('ts') && !argv.hasOwnProperty('typescript')) { argv.ts = true; } diff --git a/spec/tasks/start.spec.js b/spec/tasks/start.spec.js index ebf6c6c807..c8cd698c5e 100644 --- a/spec/tasks/start.spec.js +++ b/spec/tasks/start.spec.js @@ -178,6 +178,50 @@ describe('start command', function() { }); }); + it('should use javascript if starting a v2 project with --v2 and --no-ts', function(done) { + var processArguments = ['node', 'ionic', 'start', 'newDir', '--v2', '--no-ts']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(fs, 'existsSync').andReturn(false); + spyOn(appLibUtils, 'preprocessCliOptions').andCallThrough(); + spyOn(Start, 'promptForOverwrite'); + spyOn(Start, 'startApp').andReturn(Q(true)); + spyOn(Start, 'printQuickHelp').andReturn(Q(true)); + spyOn(Start, 'promptLogin').andReturn(Q(true)); + + start.run({}, argv).then(function() { + expect(appLibUtils.preprocessCliOptions.calls[0].args[0].v2).toEqual(true); + expect(appLibUtils.preprocessCliOptions.calls[0].args[0].ts).toEqual(false); + expect(log.info).toHaveBeenCalledWith( + '\nNew to Ionic? Get started here: http://ionicframework.com/docs/v2/getting-started\n' + ); + done(); + }); + }); + + it('should use javascript if starting a v2 project with --v2 and --no-typescript', function(done) { + var processArguments = ['node', 'ionic', 'start', 'newDir', '--v2', '--no-typescript']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(fs, 'existsSync').andReturn(false); + spyOn(appLibUtils, 'preprocessCliOptions').andCallThrough(); + spyOn(Start, 'promptForOverwrite'); + spyOn(Start, 'startApp').andReturn(Q(true)); + spyOn(Start, 'printQuickHelp').andReturn(Q(true)); + spyOn(Start, 'promptLogin').andReturn(Q(true)); + + start.run({}, argv).then(function() { + expect(appLibUtils.preprocessCliOptions.calls[0].args[0].v2).toEqual(true); + expect(appLibUtils.preprocessCliOptions.calls[0].args[0].typescript).toEqual(false); + expect(log.info).toHaveBeenCalledWith( + '\nNew to Ionic? Get started here: http://ionicframework.com/docs/v2/getting-started\n' + ); + done(); + }); + }); + it('should catch error if any process throws', function(done) { var processArguments = ['node', 'ionic', 'start', 'newDir']; var rawCliArguments = processArguments.slice(2); From 05536494179a9cd04e26c28be9e6496ad453a221 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 2 Jun 2016 10:05:41 -0500 Subject: [PATCH 0988/1100] Publish beta.28 and update changelog. --- CHANGELOG.md | 9 +++++++++ package.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f700b3afa..8aaa68a5e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ + +# [2.0.0-beta.28](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.25...v2.0.0-beta.28) (2016-06-02) + + +### Bug Fixes + +* **start:** ensure that --no-ts flag provided to --v2 project start pulls the javascript version. ([c30f9d8](https://github.com/driftyco/ionic-cli/commit/c30f9d8)) + + # [2.0.0-beta.27](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.25...v2.0.0-beta.27) (2016-06-02) diff --git a/package.json b/package.json index 9b22176f5e..55b40b596a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.0.0-beta.27", + "version": "2.0.0-beta.28", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 7ddd42e5536d915ef412db93c8d1a7c375d7a7e4 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 2 Jun 2016 12:10:35 -0500 Subject: [PATCH 0989/1100] fix(commands): add generate, prepare, address, compile to the available command list. --- lib/config/commands.js | 4 ++++ spec/config/commands.spec.js | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 spec/config/commands.spec.js diff --git a/lib/config/commands.js b/lib/config/commands.js index 203b4eff41..1320a11937 100644 --- a/lib/config/commands.js +++ b/lib/config/commands.js @@ -32,7 +32,9 @@ var affordances = { var orderedListOfCommands = [ 'start', 'serve', + 'generate', 'platform', + 'prepare', 'run', 'emulate', 'build', @@ -58,6 +60,8 @@ var orderedListOfCommands = [ 'link', 'hooks', 'state', + 'address', + 'compile', 'docs' ]; diff --git a/spec/config/commands.spec.js b/spec/config/commands.spec.js new file mode 100644 index 0000000000..27418821f3 --- /dev/null +++ b/spec/config/commands.spec.js @@ -0,0 +1,23 @@ +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var commands = require('../../lib/config/commands'); + +describe('orderedListOfCommands', function() { + var listOfCommands = commands.orderedListOfCommands.slice(0); + + it('should have a corresponding command every task in ionic folder', function() { + var taskFileList = fs + .readdirSync(path.join(__dirname, '../../lib/ionic')) + .filter(function(file) { + var stat = fs.statSync(path.join(__dirname, '../../lib/ionic', file)); + return !stat.isDirectory(); + }) + .map(function(file) { + return file.replace('.js', ''); + }) + .sort(); + expect(taskFileList).toEqual(listOfCommands.sort()); + }); +}); From 31add44eee2ca5d8fdd0aa0c8af4089c072684c7 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 2 Jun 2016 12:32:06 -0500 Subject: [PATCH 0990/1100] chore(): update package.json version and changelog. --- CHANGELOG.md | 9 +++++++++ package.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8aaa68a5e8..6d57fcd27b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ + +# [2.0.0-beta.29](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.25...v2.0.0-beta.29) (2016-06-02) + + +### Bug Fixes + +* **commands:** add generate, prepare, address, compile to the available command list. ([7ddd42e](https://github.com/driftyco/ionic-cli/commit/7ddd42e)) + + # [2.0.0-beta.28](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.25...v2.0.0-beta.28) (2016-06-02) diff --git a/package.json b/package.json index 55b40b596a..57ef2b7f23 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.0.0-beta.28", + "version": "2.0.0-beta.29", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 53bcbbbdb902616121bacafd08396b8245dd2f60 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 6 Jun 2016 11:23:45 -0500 Subject: [PATCH 0991/1100] fix(): ensure when bower exec throws an error it is caught. fixes #1037. --- lib/utils/bower.js | 12 ++++++++---- spec/utils/bower.spec.js | 8 ++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/utils/bower.js b/lib/utils/bower.js index 66c88e967d..b103e6b70a 100644 --- a/lib/utils/bower.js +++ b/lib/utils/bower.js @@ -48,10 +48,14 @@ function saveData(bowerData) { } function checkForBower() { - var result = childProcess.exec('bower -v', { silent: true }); - if (result.output.indexOf('command not found') === -1 && result.output.indexOf('not recognized') === -1) { - return true; - } + try { + var result = childProcess.exec('bower -v', { silent: true }); + if (result.hasOwnProperty('output') && + result.output.indexOf('command not found') === -1 && + result.output.indexOf('not recognized') === -1) { + return true; + } + } catch (ex) {} // eslint-disable-line no-empty return false; } diff --git a/spec/utils/bower.spec.js b/spec/utils/bower.spec.js index c9c2e117e3..d28abed897 100644 --- a/spec/utils/bower.spec.js +++ b/spec/utils/bower.spec.js @@ -171,4 +171,12 @@ describe('checkForBower function', function() { var result = bowerUtils.checkForBower(); expect(result).toEqual(false); }); + + it('should return false if bower exec throws an error', function() { + spyOn(childProcess, 'exec').andCallFake(function() { + throw new Error('something happened'); + }); + var result = bowerUtils.checkForBower(); + expect(result).toEqual(false); + }); }); From f36acef1cfe744ea4cf5dfdc9bac7d6089a43ce6 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 6 Jun 2016 11:34:48 -0500 Subject: [PATCH 0992/1100] fix(): ensure that the project base directory is passed to setupLiveReload. fixes #1034 --- lib/ionic/emulate.js | 4 ++-- lib/ionic/run.js | 4 ++-- spec/tasks/emulate.spec.js | 2 +- spec/tasks/run.spec.js | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/ionic/emulate.js b/lib/ionic/emulate.js index c54e0f6981..54319e32c4 100644 --- a/lib/ionic/emulate.js +++ b/lib/ionic/emulate.js @@ -67,11 +67,11 @@ function run(ionic, argv, rawCliArguments) { return Q.all(promiseList).then(function() { if (isLiveReload) { - return cordovaUtils.setupLiveReload(argv); + return cordovaUtils.setupLiveReload(argv, appDirectory); } // ensure the content node was set back to its original - return ConfigXml.setConfigXml(process.cwd(), { + return ConfigXml.setConfigXml(appDirectory, { resetContent: true, errorWhenNotFound: false }); diff --git a/lib/ionic/run.js b/lib/ionic/run.js index c03f6708ff..3897812818 100644 --- a/lib/ionic/run.js +++ b/lib/ionic/run.js @@ -67,11 +67,11 @@ function run(ionic, argv, rawCliArguments) { return Q.all(promiseList).then(function() { if (isLiveReload) { - return cordovaUtils.setupLiveReload(argv); + return cordovaUtils.setupLiveReload(argv, appDirectory); } // ensure the content node was set back to its original - return ConfigXml.setConfigXml(process.cwd(), { + return ConfigXml.setConfigXml(appDirectory, { resetContent: true, errorWhenNotFound: false }); diff --git a/spec/tasks/emulate.spec.js b/spec/tasks/emulate.spec.js index b0dc4bee9d..a4f3c71070 100644 --- a/spec/tasks/emulate.spec.js +++ b/spec/tasks/emulate.spec.js @@ -183,7 +183,7 @@ describe('emulate command', function() { spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); emulate.run(null, argv, rawCliArguments).then(function() { - expect(cordovaUtils.setupLiveReload).toHaveBeenCalledWith(argv); + expect(cordovaUtils.setupLiveReload).toHaveBeenCalledWith(argv. appDirectory); expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith( ['emulate', 'android'], true, { blah: 'blah' }); done(); diff --git a/spec/tasks/run.spec.js b/spec/tasks/run.spec.js index 4935c8dc47..d57b3d0f4e 100644 --- a/spec/tasks/run.spec.js +++ b/spec/tasks/run.spec.js @@ -180,7 +180,7 @@ describe('run command', function() { })); run.run(null, argv, rawCliArguments).then(function() { - expect(cordovaUtils.setupLiveReload).toHaveBeenCalledWith(argv); + expect(cordovaUtils.setupLiveReload).toHaveBeenCalledWith(argv, appDirectory); expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith( ['run', 'android'], true, { blah: 'blah' }); done(); From 1728ae0afccbed7e7f1043deed52dfec8150bc19 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 6 Jun 2016 11:53:22 -0500 Subject: [PATCH 0993/1100] chore(): fix test that had a syntax error for emulate task. --- spec/tasks/emulate.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/tasks/emulate.spec.js b/spec/tasks/emulate.spec.js index a4f3c71070..f3487234a5 100644 --- a/spec/tasks/emulate.spec.js +++ b/spec/tasks/emulate.spec.js @@ -183,7 +183,7 @@ describe('emulate command', function() { spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); emulate.run(null, argv, rawCliArguments).then(function() { - expect(cordovaUtils.setupLiveReload).toHaveBeenCalledWith(argv. appDirectory); + expect(cordovaUtils.setupLiveReload).toHaveBeenCalledWith(argv, appDirectory); expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith( ['emulate', 'android'], true, { blah: 'blah' }); done(); From 7a282dbc1a26fbba45014177eb947a99c86ffe25 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 6 Jun 2016 13:20:22 -0500 Subject: [PATCH 0994/1100] chore(): CHANGELOG and package update for release beta30. --- CHANGELOG.md | 15 ++++++++++++--- package.json | 2 +- scripts/release.js | 16 +++++++++++----- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d57fcd27b..ca0c32ff4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ + +# [2.0.0-beta.30](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.29...v2.0.0-beta.30) (2016-06-06) + + +### Bug Fixes +* ensure that the project base directory is passed to setupLiveReload. fixes [#1034](https://github.com/driftyco/ionic-cli/issues/1034) ([f36acef](https://github.com/driftyco/ionic-cli/commit/f36acef)), closes [#1034](https://github.com/driftyco/ionic-cli/issues/1034) +* ensure when bower exec throws an error it is caught. fixes [#1037](https://github.com/driftyco/ionic-cli/issues/1037). ([53bcbbb](https://github.com/driftyco/ionic-cli/commit/53bcbbb)), closes [#1037](https://github.com/driftyco/ionic-cli/issues/1037) + + -# [2.0.0-beta.29](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.25...v2.0.0-beta.29) (2016-06-02) +# [2.0.0-beta.29](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.28...v2.0.0-beta.29) (2016-06-02) ### Bug Fixes @@ -8,7 +17,7 @@ -# [2.0.0-beta.28](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.25...v2.0.0-beta.28) (2016-06-02) +# [2.0.0-beta.28](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.27...v2.0.0-beta.28) (2016-06-02) ### Bug Fixes @@ -17,7 +26,7 @@ -# [2.0.0-beta.27](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.25...v2.0.0-beta.27) (2016-06-02) +# [2.0.0-beta.27](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.26...v2.0.0-beta.27) (2016-06-02) ### Bug Fixes diff --git a/package.json b/package.json index 57ef2b7f23..1fed23fd9d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.0.0-beta.29", + "version": "2.0.0-beta.30", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", diff --git a/scripts/release.js b/scripts/release.js index 9ffa999755..cf772c600f 100644 --- a/scripts/release.js +++ b/scripts/release.js @@ -1,3 +1,9 @@ +/* + * This script will create a new github release using the verion + * specified from within the package.json file. The output from + * the changelog command will be the body of the tag. The name + * is the package.JSON.version + */ var changelog = require('conventional-changelog'); var GithubApi = require('github'); var packageJSON = require('../package.json'); @@ -15,16 +21,16 @@ github.authenticate({ return changelog({ preset: 'angular' }) -.pipe(through.obj(function(file, enc, cb){ +.pipe(through.obj(function(file) { github.releases.createRelease({ owner: 'driftyco', repo: 'ionic-cli', - target_commitish: 'v2', - tag_name: 'v' + packageJSON.version, + target_commitish: 'v2', // eslint-disable-line camelcase + tag_name: 'v' + packageJSON.version, // eslint-disable-line camelcase name: packageJSON.version, body: file.toString(), - prerelease: true, - }, function(err, data){ + prerelease: true + }, function(err, data) { if (err) console.log('error: ' + err); console.log(data); }); From 78f0cb14d773b19d4969357bcb7d1629608df4c0 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Fri, 10 Jun 2016 11:29:57 -0500 Subject: [PATCH 0995/1100] chore(): ensured all variations of ionic-app-lib usage were the same --- lib/ionic/docs.js | 5 +++-- lib/ionic/lib.js | 2 +- lib/ionic/service.js | 2 +- lib/utils/stats.js | 7 ++++--- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/ionic/docs.js b/lib/ionic/docs.js index d2cb860ea3..6aa8b0a4a1 100644 --- a/lib/ionic/docs.js +++ b/lib/ionic/docs.js @@ -4,7 +4,8 @@ var prompt = require('prompt'); var _ = require('underscore'); var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle var IonicDocs = require('./resources/docs.json'); -var log = require('ionic-app-lib').logging.logger; +var IonicAppLib = require('ionic-app-lib'); +var log = IonicAppLib.logging.logger; var COLUMN_LIMIT = 3; var settings = { @@ -53,7 +54,7 @@ function list() { } function openDefault() { - var Info = require('ionic-app-lib').info; + var Info = IonicAppLib.info; var envInfo = Info.gatherInfo(); var ionicVersion = envInfo.ionic; diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index 8e9c015c7e..fd0047abb7 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -12,9 +12,9 @@ var argv = require('optimist').argv; var prompt = require('prompt'); var IonicAppLib = require('ionic-app-lib'); var utils = IonicAppLib.utils; +var log = IonicAppLib.logging.logger; var exec = require('child_process').exec; var Q = require('q'); -var log = require('ionic-app-lib').logging.logger; var settings = { title: 'lib', diff --git a/lib/ionic/service.js b/lib/ionic/service.js index 3feeb7e0f3..4ed0c6a831 100644 --- a/lib/ionic/service.js +++ b/lib/ionic/service.js @@ -6,8 +6,8 @@ var exec = require('child_process').exec; var _ = require('underscore'); var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle var bower = require('../utils/bower'); -var log = require('ionic-app-lib').logging.logger; var IonicAppLib = require('ionic-app-lib'); +var log = IonicAppLib.logging.logger; var fail = IonicAppLib.utils.fail; var settings = { diff --git a/lib/utils/stats.js b/lib/utils/stats.js index 2f3f42aeca..c8ae31190e 100644 --- a/lib/utils/stats.js +++ b/lib/utils/stats.js @@ -2,9 +2,10 @@ 'use strict'; var request = require('request'); -var IonicConfig = require('ionic-app-lib').config; -var IonicInfo = require('ionic-app-lib').info; -var log = require('ionic-app-lib').logging.logger; +var IonicAppLib = require('ionic-app-lib'); +var IonicConfig = IonicAppLib.config; +var IonicInfo = IonicAppLib.info; +var log = IonicAppLib.logging.logger; var path = require('path'); var fs = require('fs'); From c4ee8eb52da54e68b672480c6351131188ec9a53 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 13 Jun 2016 09:58:22 -0500 Subject: [PATCH 0996/1100] chore(): rename variables to create a consistent usage of IonicAppLib across the codebase. --- lib/cli.js | 18 +++++++++--------- lib/ionic/address.js | 7 ++++--- lib/ionic/lib.js | 4 ++-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index d975e472f3..1cc1b13e53 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -13,7 +13,7 @@ var fs = require('fs'); var settings = require('../package.json'); var Tasks = require('./config/commands').allCommands; var orderedListOfTasks = require('./config/commands').orderedListOfCommands; -var Utils = IonicAppLib.utils; +var appLibUtils = IonicAppLib.utils; var Logging = IonicAppLib.logging; var log = Logging.logger; var Q = require('q'); @@ -144,7 +144,7 @@ Cli.run = function run(processArgv) { return Q.fcall(task.run.bind(task), Cli, argv, rawCliArguments); } - var root = Utils.cdIonicRoot(); + var root = appLibUtils.cdIonicRoot(); var project = IonicProject.load(root); argv.v2 = project && project.get('v2'); @@ -190,7 +190,7 @@ Cli.run = function run(processArgv) { return Cli.runWithGulp(argv, task, rawCliArguments); } catch (ex) { - return Utils.fail(ex); + return appLibUtils.fail(ex); } }; @@ -521,7 +521,7 @@ Cli.printNewsUpdates = function printNewsUpdates(skipNewsCheck) { log.info('+---------------------------------------------------------+\n'.green); } catch (ex) { q.reject('Error occurred in downloading the CLI messages:', ex); - Utils.fail(ex); + appLibUtils.fail(ex); } q.resolve(messagesJson); } else { @@ -542,14 +542,14 @@ Cli.gatherInfo = function gatherInfo() { Cli.handleUncaughtExceptions = function handleUncaughtExceptions(err) { log.error('An uncaught exception occurred and has been reported to Ionic'.red.bold); var errorMessage = typeof err === 'string' ? err : err.message; - Utils.errorHandler(errorMessage); + appLibUtils.errorHandler(errorMessage); process.exit(1); }; Cli.attachErrorHandling = function attachErrorHandling() { - Utils.errorHandler = function errorHandler(msg) { + appLibUtils.errorHandler = function errorHandler(msg) { try { - log.debug('Cli.Utils.errorHandler msg', msg, typeof msg); + log.debug('Cli.appLibUtils.errorHandler msg', msg, typeof msg); var stack = typeof msg == 'string' ? '' : msg.stack; var errorMessage = typeof msg == 'string' ? msg : msg.message; if (msg) { @@ -578,11 +578,11 @@ Cli.attachErrorHandling = function attachErrorHandling() { // Backwards compatability for those commands that havent been // converted yet. Cli.fail = function fail(err, taskHelp) { - Utils.fail(err, taskHelp); + appLibUtils.fail(err, taskHelp); }; Cli.getContentSrc = function getContentSrc() { - return Utils.getContentSrc(process.cwd()); + return appLibUtils.getContentSrc(process.cwd()); }; Cli.doRuntimeCheck = function doRuntimeCheck(version) { diff --git a/lib/ionic/address.js b/lib/ionic/address.js index 035049cc78..880932f1b6 100644 --- a/lib/ionic/address.js +++ b/lib/ionic/address.js @@ -2,6 +2,7 @@ var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle var IonicAppLib = require('ionic-app-lib'); +var IonicConfig = IonicAppLib.config; var Serve = IonicAppLib.serve; var settings = { @@ -13,9 +14,9 @@ var settings = { function run(ionic, argv) { // eslint-disable-line no-unused-vars // reset any address configs - var ionicConfig = IonicAppLib.config.load(); - ionicConfig.set('ionicServeAddress', null); - ionicConfig.set('platformServeAddress', null); + var ionConfig = IonicConfig.load(); + ionConfig.set('ionicServeAddress', null); + ionConfig.set('platformServeAddress', null); return Serve.getAddress({ isAddressCmd: true }); } diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index fd0047abb7..0bc8559596 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -11,7 +11,7 @@ var unzip = require('unzip'); var argv = require('optimist').argv; var prompt = require('prompt'); var IonicAppLib = require('ionic-app-lib'); -var utils = IonicAppLib.utils; +var appLibUtils = IonicAppLib.utils; var log = IonicAppLib.logging.logger; var exec = require('child_process').exec; var Q = require('q'); @@ -33,7 +33,7 @@ var settings = { function run(ionic, argv) { if (!fs.existsSync(path.resolve('www'))) { - return utils.fail('"www" directory cannot be found. Make sure the working directory ' + + return appLibUtils.fail('"www" directory cannot be found. Make sure the working directory ' + 'is at the top level of an Ionic project.', 'lib'); } From 9de215758874c856d3220c924e908c10b94e322e Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 13 Jun 2016 10:55:19 -0500 Subject: [PATCH 0997/1100] fix(bower): change check for bower to use execSync. --- lib/utils/bower.js | 7 +++---- spec/utils/bower.spec.js | 19 +++++++++---------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/lib/utils/bower.js b/lib/utils/bower.js index b103e6b70a..9e24f66a71 100644 --- a/lib/utils/bower.js +++ b/lib/utils/bower.js @@ -49,10 +49,9 @@ function saveData(bowerData) { function checkForBower() { try { - var result = childProcess.exec('bower -v', { silent: true }); - if (result.hasOwnProperty('output') && - result.output.indexOf('command not found') === -1 && - result.output.indexOf('not recognized') === -1) { + var result = childProcess.execSync('bower -v', { silent: true, encoding: 'utf8' }); + if (result.indexOf('command not found') === -1 && + result.indexOf('not recognized') === -1) { return true; } } catch (ex) {} // eslint-disable-line no-empty diff --git a/spec/utils/bower.spec.js b/spec/utils/bower.spec.js index d28abed897..3c523a2239 100644 --- a/spec/utils/bower.spec.js +++ b/spec/utils/bower.spec.js @@ -149,31 +149,30 @@ describe('saveData function', function() { describe('checkForBower function', function() { it('should return true if bower is installed', function() { - spyOn(childProcess, 'exec').andReturn({ - output: '0' - }); + spyOn(childProcess, 'execSync').andReturn('1.7.9\n'); + var result = bowerUtils.checkForBower(); + expect(result).toEqual(true); + }); + it('should return true if bower is installed', function() { + spyOn(childProcess, 'execSync').andReturn('0'); var result = bowerUtils.checkForBower(); expect(result).toEqual(true); }); it('should return false if bower exec returns "command not found"', function() { - spyOn(childProcess, 'exec').andReturn({ - output: 'command not found' - }); + spyOn(childProcess, 'execSync').andReturn('command not found'); var result = bowerUtils.checkForBower(); expect(result).toEqual(false); }); it('should return false if bower exec returns "not recognized"', function() { - spyOn(childProcess, 'exec').andReturn({ - output: 'not recognized' - }); + spyOn(childProcess, 'execSync').andReturn('not recognized'); var result = bowerUtils.checkForBower(); expect(result).toEqual(false); }); it('should return false if bower exec throws an error', function() { - spyOn(childProcess, 'exec').andCallFake(function() { + spyOn(childProcess, 'execSync').andCallFake(function() { throw new Error('something happened'); }); var result = bowerUtils.checkForBower(); From 2379b41cd93dd43441364ac88e587e9552924a0c Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 13 Jun 2016 12:05:03 -0500 Subject: [PATCH 0998/1100] fix(login): show error message from login results on login failure. --- lib/ionic/login.js | 6 +----- spec/tasks/login.spec.js | 33 +++++---------------------------- 2 files changed, 6 insertions(+), 33 deletions(-) diff --git a/lib/ionic/login.js b/lib/ionic/login.js index a4cf977eb0..0883e9201b 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -45,11 +45,7 @@ function login(argv) { return cookieJar; }) .catch(function(ex) { - log.error('Error logging in'); - throw ex; - - // log.info(ex); - // log.info(ex.stack); + log.error(ex); }); } diff --git a/spec/tasks/login.spec.js b/spec/tasks/login.spec.js index 1bad90f05c..3608518d35 100644 --- a/spec/tasks/login.spec.js +++ b/spec/tasks/login.spec.js @@ -118,21 +118,18 @@ describe('login command', function() { }); }); - it('should throw an error on a failed log in', function(done) { + it('should display the error on a failed log in attempt', function(done) { var processArguments = ['node', 'ionic', 'login', '--email', 'josh@ionic.io', '--password', 'asdf1234']; var rawCliArguments = processArguments.slice(2); var argv = optimist(rawCliArguments).argv; - var error = new Error('error occurred'); + var error = 'Email or Password incorrect. Please visit'; spyOn(log, 'error'); spyOn(login, 'promptForLogin'); - spyOn(appLibLogin, 'requestLogIn').andCallFake(function() { - throw error; - }); + spyOn(appLibLogin, 'requestLogIn').andReturn(Q.reject(error)); - login.login(argv).catch(function(err) { - expect(log.error).toHaveBeenCalledWith('Error logging in'); - expect(err).toEqual(error); + login.login(argv).then(function() { + expect(log.error).toHaveBeenCalledWith(error); done(); }); }); @@ -159,25 +156,5 @@ describe('login command', function() { done(); }); }); - - it('should return a rejected promise on failure', function(done) { - var processArguments = ['node', 'ionic', 'login']; - var rawCliArguments = processArguments.slice(2); - var argv = optimist(rawCliArguments).argv; - - spyOn(log, 'info'); - spyOn(prompt, 'start'); - spyOn(prompt, 'get').andCallFake(function(schema, callback) { - callback('an error occurred', null); - }); - - var promptForLogin = login.__get__('promptForLogin'); - - promptForLogin(argv).catch(function(results) { - expect(prompt.get).toHaveBeenCalled(); - expect(results).toEqual('Error logging in: an error occurred'); - done(); - }); - }); }); }); From 3f87bfb09e37214301c27863c53bb7a9cabdf555 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 13 Jun 2016 16:00:19 -0500 Subject: [PATCH 0999/1100] feat(start): remove javascript as an option for v2 projects because they should all be typescript. --- lib/ionic/start.js | 9 ---- spec/cli.spec.js | 2 +- spec/tasks/start.spec.js | 95 ++++++---------------------------------- 3 files changed, 15 insertions(+), 91 deletions(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 8c1f934189..48b74a6bd1 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -48,10 +48,6 @@ var settings = { boolean: true, title: 'Start a Ionic v2 project' }, - '--typescript|--ts': { - boolean: true, - title: '(with --v2 only) Use TypeScript in starter' - }, '--zip-file|-z': 'URL to download zipfile for starter template' }, isProjectTask: false @@ -62,11 +58,6 @@ function run(ionic, argv) { return templateUtils.listTemplates(); } - // If we are starting a version 2 project then typescript should be set by default - if ((argv.v2 || argv.v) && !argv.hasOwnProperty('ts') && !argv.hasOwnProperty('typescript')) { - argv.ts = true; - } - if (argv._.length < 2) { return appLibUtils.fail('Invalid command', 'start'); } diff --git a/spec/cli.spec.js b/spec/cli.spec.js index b3758d3bb6..7c49e3a4d9 100644 --- a/spec/cli.spec.js +++ b/spec/cli.spec.js @@ -150,7 +150,7 @@ describe('Cli', function() { var booleanOptions = IonicCli.getListOfBooleanOptions(task.options); // We expect 6 total = 3 options, each with short hand notation. - expect(booleanOptions.length).toBe(11); + expect(booleanOptions.length).toBe(9); }); it('should track stats for cli', function(done) { diff --git a/spec/tasks/start.spec.js b/spec/tasks/start.spec.js index c8cd698c5e..e6d6c13448 100644 --- a/spec/tasks/start.spec.js +++ b/spec/tasks/start.spec.js @@ -47,7 +47,6 @@ describe('start command', function() { expect(start.options['--io-app-id']).toEqual(jasmine.any(String)); expect(start.options['--template|-t']).toEqual(jasmine.any(String)); expect(start.options['--v2|-v']).toEqual(jasmine.any(Object)); - expect(start.options['--typescript|--ts']).toEqual(jasmine.any(Object)); expect(start.options['--zip-file|-z']).toEqual(jasmine.any(String)); }); }); @@ -134,74 +133,32 @@ describe('start command', function() { }); }); - it('should default to typescript if starting a v2 project with -v', function(done) { - var processArguments = ['node', 'ionic', 'start', 'newDir', '-v']; - var rawCliArguments = processArguments.slice(2); - var argv = optimist(rawCliArguments).argv; - - spyOn(fs, 'existsSync').andReturn(false); - spyOn(appLibUtils, 'preprocessCliOptions').andCallThrough(); - spyOn(Start, 'promptForOverwrite'); - spyOn(Start, 'startApp').andReturn(Q(true)); - spyOn(Start, 'printQuickHelp').andReturn(Q(true)); - spyOn(Start, 'promptLogin').andReturn(Q(true)); - - start.run({}, argv).then(function() { - expect(appLibUtils.preprocessCliOptions.calls[0].args[0].v).toEqual(true); - expect(appLibUtils.preprocessCliOptions.calls[0].args[0].ts).toEqual(true); - expect(log.info).toHaveBeenCalledWith( - '\nNew to Ionic? Get started here: http://ionicframework.com/docs/v2/getting-started\n' - ); - done(); - }); - }); - - it('should default to typescript if starting a v2 project with --v2', function(done) { - var processArguments = ['node', 'ionic', 'start', 'newDir', '--v2']; + it('should catch error if any process throws', function(done) { + var processArguments = ['node', 'ionic', 'start', 'newDir']; var rawCliArguments = processArguments.slice(2); var argv = optimist(rawCliArguments).argv; spyOn(fs, 'existsSync').andReturn(false); + spyOn(log, 'error'); spyOn(appLibUtils, 'preprocessCliOptions').andCallThrough(); spyOn(Start, 'promptForOverwrite'); - spyOn(Start, 'startApp').andReturn(Q(true)); - spyOn(Start, 'printQuickHelp').andReturn(Q(true)); - spyOn(Start, 'promptLogin').andReturn(Q(true)); + spyOn(Start, 'startApp').andReturn(Q.reject(false)); + spyOn(Start, 'printQuickHelp'); + spyOn(Start, 'promptLogin'); - start.run({}, argv).then(function() { - expect(appLibUtils.preprocessCliOptions.calls[0].args[0].v2).toEqual(true); - expect(appLibUtils.preprocessCliOptions.calls[0].args[0].ts).toEqual(true); - expect(log.info).toHaveBeenCalledWith( - '\nNew to Ionic? Get started here: http://ionicframework.com/docs/v2/getting-started\n' - ); + start.run({}, argv).catch(function() { + expect(appLibUtils.preprocessCliOptions).toHaveBeenCalled(); + expect(Start.promptForOverwrite).not.toHaveBeenCalled(); + expect(Start.startApp).toHaveBeenCalled(); + expect(Start.printQuickHelp).not.toHaveBeenCalled(); + expect(Start.promptLogin).not.toHaveBeenCalled(); done(); }); }); - it('should use javascript if starting a v2 project with --v2 and --no-ts', function(done) { - var processArguments = ['node', 'ionic', 'start', 'newDir', '--v2', '--no-ts']; - var rawCliArguments = processArguments.slice(2); - var argv = optimist(rawCliArguments).argv; - spyOn(fs, 'existsSync').andReturn(false); - spyOn(appLibUtils, 'preprocessCliOptions').andCallThrough(); - spyOn(Start, 'promptForOverwrite'); - spyOn(Start, 'startApp').andReturn(Q(true)); - spyOn(Start, 'printQuickHelp').andReturn(Q(true)); - spyOn(Start, 'promptLogin').andReturn(Q(true)); - - start.run({}, argv).then(function() { - expect(appLibUtils.preprocessCliOptions.calls[0].args[0].v2).toEqual(true); - expect(appLibUtils.preprocessCliOptions.calls[0].args[0].ts).toEqual(false); - expect(log.info).toHaveBeenCalledWith( - '\nNew to Ionic? Get started here: http://ionicframework.com/docs/v2/getting-started\n' - ); - done(); - }); - }); - - it('should use javascript if starting a v2 project with --v2 and --no-typescript', function(done) { - var processArguments = ['node', 'ionic', 'start', 'newDir', '--v2', '--no-typescript']; + it('should print a getting started message with v2 projects', function(done) { + var processArguments = ['node', 'ionic', 'start', 'newDir', '--v2']; var rawCliArguments = processArguments.slice(2); var argv = optimist(rawCliArguments).argv; @@ -214,35 +171,11 @@ describe('start command', function() { start.run({}, argv).then(function() { expect(appLibUtils.preprocessCliOptions.calls[0].args[0].v2).toEqual(true); - expect(appLibUtils.preprocessCliOptions.calls[0].args[0].typescript).toEqual(false); expect(log.info).toHaveBeenCalledWith( '\nNew to Ionic? Get started here: http://ionicframework.com/docs/v2/getting-started\n' ); done(); }); }); - - it('should catch error if any process throws', function(done) { - var processArguments = ['node', 'ionic', 'start', 'newDir']; - var rawCliArguments = processArguments.slice(2); - var argv = optimist(rawCliArguments).argv; - - spyOn(fs, 'existsSync').andReturn(false); - spyOn(log, 'error'); - spyOn(appLibUtils, 'preprocessCliOptions').andCallThrough(); - spyOn(Start, 'promptForOverwrite'); - spyOn(Start, 'startApp').andReturn(Q.reject(false)); - spyOn(Start, 'printQuickHelp'); - spyOn(Start, 'promptLogin'); - - start.run({}, argv).catch(function() { - expect(appLibUtils.preprocessCliOptions).toHaveBeenCalled(); - expect(Start.promptForOverwrite).not.toHaveBeenCalled(); - expect(Start.startApp).toHaveBeenCalled(); - expect(Start.printQuickHelp).not.toHaveBeenCalled(); - expect(Start.promptLogin).not.toHaveBeenCalled(); - done(); - }); - }); }); }); From 632237619b840ef07169a17ce81e3b589d4dd627 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 13 Jun 2016 16:53:23 -0500 Subject: [PATCH 1000/1100] fix(start): ensure that folders can be created even if they are numeric. --- lib/ionic/start.js | 4 ++++ spec/tasks/start.spec.js | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 48b74a6bd1..37eeaf2464 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -66,6 +66,10 @@ function run(ionic, argv) { return log.error('Please name your Ionic project something meaningful other than \'.\''.red); } + // Ensure that the folder name provided is a String + // ie 5 becomes '5' AND '5' stays as '5' + argv._[1] = argv._[1].toString(); + var promptPromise; var options = appLibUtils.preprocessCliOptions(argv); var startingApp = true; diff --git a/spec/tasks/start.spec.js b/spec/tasks/start.spec.js index e6d6c13448..109f476bc0 100644 --- a/spec/tasks/start.spec.js +++ b/spec/tasks/start.spec.js @@ -157,6 +157,24 @@ describe('start command', function() { }); + it('should treat all numerically named folders and strings not integers', function(done) { + var processArguments = ['node', 'ionic', 'start', '5887']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(fs, 'existsSync').andReturn(false); + spyOn(appLibUtils, 'preprocessCliOptions').andCallThrough(); + spyOn(Start, 'promptForOverwrite'); + spyOn(Start, 'startApp').andReturn(Q(true)); + spyOn(Start, 'printQuickHelp').andReturn(Q(true)); + spyOn(Start, 'promptLogin').andReturn(Q(true)); + + start.run({}, argv).then(function() { + expect(appLibUtils.preprocessCliOptions.calls[0].args[0]._[1]).toEqual('5887'); + done(); + }); + }); + it('should print a getting started message with v2 projects', function(done) { var processArguments = ['node', 'ionic', 'start', 'newDir', '--v2']; var rawCliArguments = processArguments.slice(2); From c0b7b537a739515408934d5f54ff809dc42aa2bd Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Tue, 14 Jun 2016 13:30:45 -0500 Subject: [PATCH 1001/1100] chore(): remove deprecated commands browser and setup. --- lib/config/commands.js | 2 -- lib/ionic/browser.js | 34 ---------------------------------- lib/ionic/setup.js | 23 ----------------------- spec/tasks/setup.spec.js | 0 4 files changed, 59 deletions(-) delete mode 100644 lib/ionic/browser.js delete mode 100644 lib/ionic/setup.js delete mode 100644 spec/tasks/setup.spec.js diff --git a/lib/config/commands.js b/lib/config/commands.js index 1320a11937..bb25a20529 100644 --- a/lib/config/commands.js +++ b/lib/config/commands.js @@ -43,14 +43,12 @@ var orderedListOfCommands = [ 'upload', 'share', 'lib', - 'setup', 'login', 'io', 'security', 'push', 'package', 'config', - 'browser', 'service', 'add', 'remove', diff --git a/lib/ionic/browser.js b/lib/ionic/browser.js deleted file mode 100644 index 91f0d8b2de..0000000000 --- a/lib/ionic/browser.js +++ /dev/null @@ -1,34 +0,0 @@ -// Deprecated -'use strict'; - -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle - -var settings = { - title: 'browser', - name: 'browser', - summary: 'Add another browser for a platform ' + '(beta)'.yellow, - args: { - '': '"add remove rm info versions upgrade list ls revert"', - '[browser]': 'The browser you wish to add or remove (Crosswalk)' - }, - options: { - '--nosave|-n': { - title: 'Do not save the platforms and plugins to the package.json file', - boolean: true - } - }, - isProjectTask: true -}; - -function run() { - console.log( - 'The browser task has been deprecated.\n' + - 'Please use the Cordova Crosswalk plugin instead:\n\n' + - 'ionic platform add android\n' + - 'ionic plugin add cordova-plugin-crosswalk-webview\n' - ); -} - -module.exports = extend(settings, { - run: run -}); diff --git a/lib/ionic/setup.js b/lib/ionic/setup.js deleted file mode 100644 index e27ce3d4ff..0000000000 --- a/lib/ionic/setup.js +++ /dev/null @@ -1,23 +0,0 @@ -'use strict'; - -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle - -var settings = { - title: 'setup', - name: 'setup', - summary: 'Configure the project with a build tool ' + '(beta)'.yellow, - args: { - '[sass]': 'Setup the project to use Sass CSS precompiling' - }, - isProjectTask: true -}; - -function run() { - - // TODO link to gulp hook docs - console.log('The setup task has been deprecated.\n'); -} - -module.exports = extend(settings, { - run: run -}); diff --git a/spec/tasks/setup.spec.js b/spec/tasks/setup.spec.js deleted file mode 100644 index e69de29bb2..0000000000 From 2a3269ba231e165b8454a39924d0c72095e17df3 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Tue, 14 Jun 2016 15:51:40 -0500 Subject: [PATCH 1002/1100] Updated help documentation of cordova commands to ensure that is the first option. --- lib/ionic/build.js | 4 ++-- lib/ionic/emulate.js | 4 ++-- lib/ionic/platform.js | 4 ++-- lib/ionic/run.js | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/ionic/build.js b/lib/ionic/build.js index 85eb723527..4f9f613f0e 100644 --- a/lib/ionic/build.js +++ b/lib/ionic/build.js @@ -12,8 +12,8 @@ var settings = { name: 'build', summary: 'Build (prepare + compile) an Ionic project for a given platform.\n', args: { - '[options]': '', - '': '' + '': '', + '[options]': '' }, options: { '--nohooks|-n': { diff --git a/lib/ionic/emulate.js b/lib/ionic/emulate.js index 54319e32c4..18693ddc0d 100644 --- a/lib/ionic/emulate.js +++ b/lib/ionic/emulate.js @@ -32,8 +32,8 @@ var settings = { name: 'emulate', summary: 'Emulate an Ionic project on a simulator or emulator', args: { - '[options]': '', - '': '' + '': '', + '[options]': '' }, options: cordovaRunEmulateOptions, isProjectTask: true diff --git a/lib/ionic/platform.js b/lib/ionic/platform.js index 5f4a4d17d2..a1696b08b4 100644 --- a/lib/ionic/platform.js +++ b/lib/ionic/platform.js @@ -13,8 +13,8 @@ var settings = { name: 'platform', summary: 'Add platform target for building an Ionic app', args: { - '[options]': '', - '': '' + '': '', + '[options]': '' }, options: { '--noresources|-r': { diff --git a/lib/ionic/run.js b/lib/ionic/run.js index 3897812818..b4387818c8 100644 --- a/lib/ionic/run.js +++ b/lib/ionic/run.js @@ -32,8 +32,8 @@ var settings = { name: 'run', summary: 'Run an Ionic project on a connected device', args: { - '[options]': '', - '': '' + '': '', + '[options]': '' }, options: cordovaRunEmulateOptions, isProjectTask: true From b3608f36923816eb4c091cb8bd555aa76448a0d4 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Tue, 14 Jun 2016 16:04:04 -0500 Subject: [PATCH 1003/1100] chore(): add deprecation messages to all bower commands. --- lib/ionic/add.js | 6 ++++++ lib/ionic/lib.js | 6 ++++++ lib/ionic/list.js | 6 ++++++ lib/ionic/remove.js | 6 ++++++ lib/ionic/service.js | 10 ++++++++-- 5 files changed, 32 insertions(+), 2 deletions(-) diff --git a/lib/ionic/add.js b/lib/ionic/add.js index 5073376ce2..b8892afe01 100644 --- a/lib/ionic/add.js +++ b/lib/ionic/add.js @@ -45,6 +45,12 @@ function installBowerComponent(componentName) { */ function run(ionic, argv) { + // This command will be deprecated in the future. + var deprecationMsg = 'This command has been ' + 'deprecate'.red + '. All ' + + 'resources are currently available in NPM and we recommend that you use NPM to manage these.\n' + + 'More information is available here: https://github.com/driftyco/ionic-cli/wiki/Migrating-to-NPM-from-bower\n'; + log.info(deprecationMsg.bold); + if (!bower.checkForBower()) { appLibUtils.fail(bower.installMessage, 'add'); return; diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index 0bc8559596..783b7c24e3 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -32,6 +32,12 @@ var settings = { function run(ionic, argv) { + // This command will be deprecated in the future. + var deprecationMsg = 'This command has been ' + 'deprecate'.red + '. All ' + + 'resources are currently available in NPM and we recommend that you use NPM to manage these.\n' + + 'More information is available here: https://github.com/driftyco/ionic-cli/wiki/Migrating-to-NPM-from-bower\n'; + log.info(deprecationMsg.bold); + if (!fs.existsSync(path.resolve('www'))) { return appLibUtils.fail('"www" directory cannot be found. Make sure the working directory ' + 'is at the top level of an Ionic project.', 'lib'); diff --git a/lib/ionic/list.js b/lib/ionic/list.js index dd84ccc511..e4019c9108 100644 --- a/lib/ionic/list.js +++ b/lib/ionic/list.js @@ -37,6 +37,12 @@ function listComponents() { */ function run() { + // This command will be deprecated in the future. + var deprecationMsg = 'This command has been ' + 'deprecate'.red + '. All ' + + 'resources are currently available in NPM and we recommend that you use NPM to manage these.\n' + + 'More information is available here: https://github.com/driftyco/ionic-cli/wiki/Migrating-to-NPM-from-bower\n'; + log.info(deprecationMsg.bold); + try { listComponents(); } catch (error) { diff --git a/lib/ionic/remove.js b/lib/ionic/remove.js index 17f429b5b7..b4f96fd4b0 100644 --- a/lib/ionic/remove.js +++ b/lib/ionic/remove.js @@ -42,6 +42,12 @@ function uninstallBowerComponent(componentName) { */ function run(ionic, argv) { + // This command will be deprecated in the future. + var deprecationMsg = 'This command has been ' + 'deprecate'.red + '. All ' + + 'resources are currently available in NPM and we recommend that you use NPM to manage these.\n' + + 'More information is available here: https://github.com/driftyco/ionic-cli/wiki/Migrating-to-NPM-from-bower\n'; + log.info(deprecationMsg.bold); + if (!bower.checkForBower()) { appLibUtils.fail(bower.installMessage, 'remove'); return; diff --git a/lib/ionic/service.js b/lib/ionic/service.js index 4ed0c6a831..bdf430c6bc 100644 --- a/lib/ionic/service.js +++ b/lib/ionic/service.js @@ -166,8 +166,14 @@ function removeService(serviceName) { // For each plugins - call 'ionic add plugin ' function run(ionic, argv) { - if (!bower.IonicBower.checkForBower()) { - fail(bower.IonicBower.installMessage, 'service'); + // This command will be deprecated in the future. + var deprecationMsg = 'This command has been ' + 'deprecate'.red + '. All ' + + 'resources are currently available in NPM and we recommend that you use NPM to manage these.\n' + + 'More information is available here: https://github.com/driftyco/ionic-cli/wiki/Migrating-to-NPM-from-bower\n'; + log.info(deprecationMsg.bold); + + if (!bower.checkForBower()) { + fail(bower.installMessage, 'service'); return; } From d04ca786946742126b0011704f03eeff034e5cdd Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Fri, 17 Jun 2016 11:30:27 -0500 Subject: [PATCH 1004/1100] Commit changes to package.json and Changelog for beta.31 --- CHANGELOG.md | 16 ++++++++++++++++ package.json | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca0c32ff4b..05ac3798dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ + +# [2.0.0-beta.31](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.30...v2.0.0-beta.31) (2016-06-13) + + +### Bug Fixes + +* **bower:** change check for bower to use execSync. ([9de2157](https://github.com/driftyco/ionic-cli/commit/9de2157)) +* **login:** show error message from login results on login failure. ([2379b41](https://github.com/driftyco/ionic-cli/commit/2379b41)) +* **start:** ensure that folders can be created even if they are numeric. ([6322376](https://github.com/driftyco/ionic-cli/commit/6322376)) + + +### Features + +* **start:** remove javascript as an option for v2 projects because they should all be typescript. ([3f87bfb](https://github.com/driftyco/ionic-cli/commit/3f87bfb)) + + # [2.0.0-beta.30](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.29...v2.0.0-beta.30) (2016-06-06) diff --git a/package.json b/package.json index 1fed23fd9d..a7fdd5e3ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.0.0-beta.30", + "version": "2.0.0-beta.31", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -67,7 +67,7 @@ "gulp": "3.8.8", "gulp-util": "3.0.7", "inquirer": "0.11.2", - "ionic-app-lib": "2.0.0-beta.16", + "ionic-app-lib": "2.0.0-beta.17", "moment": "2.11.1", "open": "0.0.5", "optimist": "0.6.0", From 18c567549d82beaa921c2afcd4abbd6c2c2ff402 Mon Sep 17 00:00:00 2001 From: Tim Lancina Date: Thu, 23 Jun 2016 17:00:18 -0500 Subject: [PATCH 1005/1100] chore(): update to 1.7.16 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8c0e844ea3..f6c46bf475 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "1.7.15", + "version": "1.7.16", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -68,7 +68,7 @@ "finalhandler": "0.2.0", "form-data": "0.1.4", "gulp": "3.8.8", - "ionic-app-lib": "0.7.2", + "ionic-app-lib": "0.7.3", "moment": "^2.10.6", "ncp": "0.4.2", "npm": "2.1.3", From f64121f5fa24def0deb17d8179758a29ef464a40 Mon Sep 17 00:00:00 2001 From: Tim Lancina Date: Thu, 23 Jun 2016 17:44:55 -0500 Subject: [PATCH 1006/1100] chore(): update to beta.32 --- CHANGELOG.md | 6 ++++++ package.json | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05ac3798dd..af4a1a2efc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ + +# [2.0.0-beta.32](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.31...v2.0.0-beta.32) (2016-06-23) + +### Bug Fixes +* **upload** Update ionic-app-lib dependency to fix HTML entities being created in index.html on upload. Closes [#562](https://github.com/driftyco/ionic-cli/issues/562), [#600](https://github.com/driftyco/ionic-cli/issues/600), [#640](https://github.com/driftyco/ionic-cli/issues/642) + # [2.0.0-beta.31](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.30...v2.0.0-beta.31) (2016-06-13) diff --git a/package.json b/package.json index a7fdd5e3ad..c1f3982b55 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.0.0-beta.31", + "version": "2.0.0-beta.32", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -67,7 +67,7 @@ "gulp": "3.8.8", "gulp-util": "3.0.7", "inquirer": "0.11.2", - "ionic-app-lib": "2.0.0-beta.17", + "ionic-app-lib": "2.0.0-beta.18", "moment": "2.11.1", "open": "0.0.5", "optimist": "0.6.0", From b006e3f50d0fd5740f1ad45278c0855ad81d8ffd Mon Sep 17 00:00:00 2001 From: Tim Lancina Date: Thu, 23 Jun 2016 17:58:09 -0500 Subject: [PATCH 1007/1100] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af4a1a2efc..88619bcad8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ # [2.0.0-beta.32](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.31...v2.0.0-beta.32) (2016-06-23) ### Bug Fixes -* **upload** Update ionic-app-lib dependency to fix HTML entities being created in index.html on upload. Closes [#562](https://github.com/driftyco/ionic-cli/issues/562), [#600](https://github.com/driftyco/ionic-cli/issues/600), [#640](https://github.com/driftyco/ionic-cli/issues/642) +* **upload** Update ionic-app-lib dependency to fix HTML entities being created in index.html on upload. Closes [#562](https://github.com/driftyco/ionic-cli/issues/562), [#600](https://github.com/driftyco/ionic-cli/issues/600), [#640](https://github.com/driftyco/ionic-cli/issues/640) # [2.0.0-beta.31](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.30...v2.0.0-beta.31) (2016-06-13) From 5b400e712947ec1dff4e92d8fa53668cdd297568 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 7 Jul 2016 14:22:22 -0500 Subject: [PATCH 1008/1100] fix(cordova): correct issue where node 6 shows numbers when printing from stdout. fixes #1078 --- lib/utils/cordova.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/utils/cordova.js b/lib/utils/cordova.js index 3e1b02378e..c6b71e2968 100644 --- a/lib/utils/cordova.js +++ b/lib/utils/cordova.js @@ -59,7 +59,7 @@ function arePluginsInstalled(baseDir) { * @return {Promise} Promise upon completion */ function installPlatform(platform) { - log.info(('• You\'re trying to build for ' + platform + 'but don\'t have the platform installed yet.').yellow); + log.info(('• You\'re trying to build for ' + platform + ' but don\'t have the platform installed yet.').yellow); log.info('∆ Installing ' + platform + ' for you.'); return promiseExec('cordova platform add ' + platform).then(function() { @@ -75,7 +75,7 @@ function execCordovaCommand(optionList, isLiveReload, serveOptions) { var cordovaProcess = childProcess.exec('cordova ' + optionList.join(' ')); cordovaProcess.stdout.on('data', function(data) { - log.info(data); + log.info(data.toString()); }); cordovaProcess.stderr.on('data', function(data) { From 142128ab81bb33d44fe2494845a9d7eea6bc3b3c Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 7 Jul 2016 15:48:54 -0500 Subject: [PATCH 1009/1100] =?UTF-8?q?fix(cordova):=20ensure=20that=20each?= =?UTF-8?q?=20cordova=20command=20will=20properly=20catch=20wh=E2=80=A6=20?= =?UTF-8?q?(#1154)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(cordova): ensure that each cordova command will properly catch when cordova throws an error. * fix(cordova): show error message to user when iOS is only available on an OSX machine. --- lib/ionic/build.js | 9 ++++++- lib/ionic/compile.js | 6 +++++ lib/ionic/emulate.js | 9 ++++++- lib/ionic/platform.js | 5 ++++ lib/ionic/plugin.js | 5 ++++ lib/ionic/prepare.js | 6 +++++ lib/ionic/run.js | 9 ++++++- spec/tasks/build.spec.js | 34 +++++++++++++++++++++--- spec/tasks/compile.spec.js | 14 ++++++++-- spec/tasks/emulate.spec.js | 38 +++++++++++++++++++++++--- spec/tasks/platform.spec.js | 53 ++++++++++++++++++++++++++++++++++++- spec/tasks/plugin.spec.js | 46 ++++++++++++++++++++++++++++++++ spec/tasks/prepare.spec.js | 22 +++++++++++++++ spec/tasks/run.spec.js | 38 +++++++++++++++++++++++--- 14 files changed, 277 insertions(+), 17 deletions(-) diff --git a/lib/ionic/build.js b/lib/ionic/build.js index 4f9f613f0e..a2ff942961 100644 --- a/lib/ionic/build.js +++ b/lib/ionic/build.js @@ -5,6 +5,7 @@ var os = require('os'); var Q = require('q'); var IonicAppLib = require('ionic-app-lib'); var ConfigXml = IonicAppLib.configXml; +var log = IonicAppLib.logging.logger; var cordovaUtils = require('../utils/cordova'); var settings = { @@ -37,7 +38,8 @@ function run(ionic, argv, rawCliArguments) { } if (runPlatform === 'ios' && os.platform() !== 'darwin') { - return Q.reject('✗ You cannot run iOS unless you are on Mac OSX.'); + log.error('✗ You cannot run iOS unless you are on Mac OSX.'); + return Q(); } var promiseList = [] @@ -59,6 +61,11 @@ function run(ionic, argv, rawCliArguments) { .then(function() { var optionList = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawArgs); return cordovaUtils.execCordovaCommand(optionList); + }) + .catch(function(ex) { + if (ex instanceof Error) { + log.error(ex); + } }); } diff --git a/lib/ionic/compile.js b/lib/ionic/compile.js index b368179e0d..f66ab97162 100644 --- a/lib/ionic/compile.js +++ b/lib/ionic/compile.js @@ -3,6 +3,7 @@ var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle var IonicAppLib = require('ionic-app-lib'); var ConfigXml = IonicAppLib.configXml; +var log = IonicAppLib.logging.logger; var cordovaUtils = require('../utils/cordova'); var settings = { @@ -23,6 +24,11 @@ function run(ionic, argv, rawCliArguments) { }).then(function() { var optionList = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawArgs); return cordovaUtils.execCordovaCommand(optionList); + }) + .catch(function(ex) { + if (ex instanceof Error) { + log.error(ex); + } }); } diff --git a/lib/ionic/emulate.js b/lib/ionic/emulate.js index 18693ddc0d..696b4fbea9 100644 --- a/lib/ionic/emulate.js +++ b/lib/ionic/emulate.js @@ -5,6 +5,7 @@ var os = require('os'); var Q = require('q'); var IonicAppLib = require('ionic-app-lib'); var ConfigXml = IonicAppLib.configXml; +var log = IonicAppLib.logging.logger; var cordovaUtils = require('../utils/cordova'); var cordovaRunEmulateOptions = { @@ -54,7 +55,8 @@ function run(ionic, argv, rawCliArguments) { } if (runPlatform === 'ios' && os.platform() !== 'darwin') { - return Q.reject('✗ You cannot run iOS unless you are on Mac OSX.'); + log.error('✗ You cannot run iOS unless you are on Mac OSX.'); + return Q(); } var promiseList = [] @@ -79,6 +81,11 @@ function run(ionic, argv, rawCliArguments) { .then(function(serveOptions) { var optionList = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawArgs); return cordovaUtils.execCordovaCommand(optionList, isLiveReload, serveOptions); + }) + .catch(function(ex) { + if (ex instanceof Error) { + log.error(ex); + } }); } diff --git a/lib/ionic/platform.js b/lib/ionic/platform.js index a1696b08b4..fd2103022f 100644 --- a/lib/ionic/platform.js +++ b/lib/ionic/platform.js @@ -71,6 +71,11 @@ function run(ionic, argv, rawCliArguments) { log.info('Removing platform from package.json file'); return State.removePlatform(appDirectory, argumentName); } + }) + .catch(function(ex) { + if (ex instanceof Error) { + log.error(ex); + } }); } diff --git a/lib/ionic/plugin.js b/lib/ionic/plugin.js index f5493e0735..5139387046 100644 --- a/lib/ionic/plugin.js +++ b/lib/ionic/plugin.js @@ -70,6 +70,11 @@ function run(ionic, argv, rawCliArguments) { log.info('Removing plugin from package.json file'); return State.removePlugin(appDirectory, argumentName); } + }) + .catch(function(ex) { + if (ex instanceof Error) { + log.error(ex); + } }); } diff --git a/lib/ionic/prepare.js b/lib/ionic/prepare.js index f780130e66..0270fd2f3c 100644 --- a/lib/ionic/prepare.js +++ b/lib/ionic/prepare.js @@ -3,6 +3,7 @@ var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle var IonicAppLib = require('ionic-app-lib'); var ConfigXml = IonicAppLib.configXml; +var log = IonicAppLib.logging.logger; var cordovaUtils = require('../utils/cordova'); var settings = { @@ -24,6 +25,11 @@ function run(ionic, argv, rawCliArguments) { .then(function() { var optionList = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawArgs); return cordovaUtils.execCordovaCommand(optionList); + }) + .catch(function(ex) { + if (ex instanceof Error) { + log.error(ex); + } }); } diff --git a/lib/ionic/run.js b/lib/ionic/run.js index b4387818c8..6f54c6f086 100644 --- a/lib/ionic/run.js +++ b/lib/ionic/run.js @@ -5,6 +5,7 @@ var os = require('os'); var Q = require('q'); var IonicAppLib = require('ionic-app-lib'); var ConfigXml = IonicAppLib.configXml; +var log = IonicAppLib.logging.logger; var cordovaUtils = require('../utils/cordova'); var cordovaRunEmulateOptions = { @@ -54,7 +55,8 @@ function run(ionic, argv, rawCliArguments) { } if (runPlatform === 'ios' && os.platform() !== 'darwin') { - return Q.reject('✗ You cannot run iOS unless you are on Mac OSX.'); + log.error('✗ You cannot run iOS unless you are on Mac OSX.'); + return Q(); } var promiseList = [] @@ -79,6 +81,11 @@ function run(ionic, argv, rawCliArguments) { .then(function(serveOptions) { var optionList = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawArgs); return cordovaUtils.execCordovaCommand(optionList, isLiveReload, serveOptions); + }) + .catch(function(ex) { + if (ex instanceof Error) { + log.error(ex); + } }); } diff --git a/spec/tasks/build.spec.js b/spec/tasks/build.spec.js index 4fdf6a5d9c..1a563782b0 100644 --- a/spec/tasks/build.spec.js +++ b/spec/tasks/build.spec.js @@ -70,14 +70,13 @@ describe('build command', function() { it('should fail if the system is not Mac and the platform is iOS', function(done) { spyOn(os, 'platform').andReturn('windows'); - build.run(null, argv, rawCliArguments).catch(function(error) { - expect(error).toEqual('✗ You cannot run iOS unless you are on Mac OSX.'); + build.run(null, argv, rawCliArguments).then(function() { + expect(log.error).toHaveBeenCalledWith('✗ You cannot run iOS unless you are on Mac OSX.'); done(); }); }); }); - describe('cordova platform and plugin checks', function() { var appDirectory = '/ionic/app/path'; @@ -91,11 +90,11 @@ describe('build command', function() { spyOn(cordovaUtils, 'installPlatform').andReturn(Q(true)); spyOn(cordovaUtils, 'installPlugins').andReturn(Q(true)); - spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(0)); }); it('should try to install the cordova platform if it is not installed', function(done) { spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(false); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(0)); build.run(null, argv, rawCliArguments).then(function() { expect(cordovaUtils.installPlatform).toHaveBeenCalledWith('ios'); @@ -106,6 +105,7 @@ describe('build command', function() { it('should not try to install the cordova platform if it is installed', function(done) { spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(0)); build.run(null, argv, rawCliArguments).then(function() { expect(cordovaUtils.installPlatform).not.toHaveBeenCalledWith(); @@ -116,6 +116,7 @@ describe('build command', function() { it('should install plugins if they are not installed', function(done) { spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(false); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(0)); build.run(null, argv, rawCliArguments).then(function() { expect(cordovaUtils.arePluginsInstalled).toHaveBeenCalledWith(appDirectory); @@ -127,6 +128,7 @@ describe('build command', function() { it('should not install plugins if they are installed', function(done) { spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(0)); build.run(null, argv, rawCliArguments).then(function() { expect(cordovaUtils.arePluginsInstalled).toHaveBeenCalledWith(appDirectory); @@ -134,5 +136,29 @@ describe('build command', function() { done(); }); }); + + it('should fail if any functions throw', function(done) { + var error = new Error('error occurred'); + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q.reject(error)); + + build.run({}, argv, rawCliArguments).then(function() { + expect(log.error).toHaveBeenCalledWith(error); + done(); + }); + }); + + it('should fail if any functions throw and not log if not an instance of an Error', function(done) { + var error = 1; + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q.reject(error)); + + build.run({}, argv, rawCliArguments).then(function() { + expect(log.error).not.toHaveBeenCalled(); + done(); + }); + }); }); }); diff --git a/spec/tasks/compile.spec.js b/spec/tasks/compile.spec.js index eaeae971e1..5a8de1c475 100644 --- a/spec/tasks/compile.spec.js +++ b/spec/tasks/compile.spec.js @@ -31,8 +31,18 @@ describe('compile command', function() { var error = new Error('error occurred'); spyOn(ConfigXml, 'setConfigXml').andReturn(Q.reject(error)); - compile.run({}, argv, rawCliArguments).catch(function(ex) { - expect(ex).toEqual(error); + compile.run({}, argv, rawCliArguments).then(function() { + expect(log.error).toHaveBeenCalledWith(error); + done(); + }); + }); + + it('should fail if any functions throw and not log if error is not instanceof Error', function(done) { + var error = 1; + spyOn(ConfigXml, 'setConfigXml').andReturn(Q.reject(error)); + + compile.run({}, argv, rawCliArguments).then(function() { + expect(log.error).not.toHaveBeenCalled(); done(); }); }); diff --git a/spec/tasks/emulate.spec.js b/spec/tasks/emulate.spec.js index f3487234a5..9529948092 100644 --- a/spec/tasks/emulate.spec.js +++ b/spec/tasks/emulate.spec.js @@ -57,7 +57,7 @@ describe('emulate command', function() { it('should default to iOS for the platform', function(done) { spyOn(os, 'platform').andReturn('darwin'); - emulate.run(null, argv, rawCliArguments).catch(function() { + emulate.run(null, argv, rawCliArguments).then(function() { expect(cordovaUtils.isPlatformInstalled).toHaveBeenCalledWith('ios', appDirectory); done(); }); @@ -66,8 +66,8 @@ describe('emulate command', function() { it('should fail if the system is not Mac and the platform is iOS', function(done) { spyOn(os, 'platform').andReturn('windows'); - emulate.run(null, argv, rawCliArguments).catch(function(error) { - expect(error).toEqual('✗ You cannot run iOS unless you are on Mac OSX.'); + emulate.run(null, argv, rawCliArguments).then(function() { + expect(log.error).toHaveBeenCalledWith('✗ You cannot run iOS unless you are on Mac OSX.'); done(); }); }); @@ -140,6 +140,38 @@ describe('emulate command', function() { spyOn(os, 'platform').andReturn('darwin'); }); + it('should fail if any functions throw', function(done) { + var processArguments = ['node', 'ionic', 'emulate', '-n']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var error = new Error('error occurred'); + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q.reject(error)); + + emulate.run({}, argv, rawCliArguments).then(function() { + expect(log.error).toHaveBeenCalledWith(error); + done(); + }); + }); + + it('should fail if any functions throw and not log if not an instance of an Error', function(done) { + var processArguments = ['node', 'ionic', 'emulate', '-n']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var error = 1; + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q.reject(error)); + + emulate.run({}, argv, rawCliArguments).then(function() { + expect(log.error).not.toHaveBeenCalled(); + done(); + }); + }); + it('should execute the command against the cordova util', function(done) { var processArguments = ['node', 'ionic', 'emulate', '-n']; var rawCliArguments = processArguments.slice(2); diff --git a/spec/tasks/platform.spec.js b/spec/tasks/platform.spec.js index 0a2540dd10..f177afc452 100644 --- a/spec/tasks/platform.spec.js +++ b/spec/tasks/platform.spec.js @@ -48,13 +48,41 @@ describe('platform command', function() { beforeEach(function() { spyOn(process, 'cwd').andReturn(appDirectory); - spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(0)); + }); + + it('should fail if any functions throw', function(done) { + var processArguments = ['node', 'ionic', 'platform', '-n']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var error = new Error('error occurred'); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q.reject(error)); + + platform.run({}, argv, rawCliArguments).then(function() { + expect(log.error).toHaveBeenCalledWith(error); + done(); + }); + }); + + it('should fail if any functions throw and not log if not an instance of an Error', function(done) { + var processArguments = ['node', 'ionic', 'platform', '-n']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var error = 1; + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q.reject(error)); + + platform.run({}, argv, rawCliArguments).then(function() { + expect(log.error).not.toHaveBeenCalled(); + done(); + }); }); it('should use commands provided', function(done) { var processArguments = ['node', 'ionic', 'platform', '-n']; var rawCliArguments = processArguments.slice(2); var argv = optimist(rawCliArguments).argv; + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(0)); platform.run(null, argv, rawCliArguments).then(function() { expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['platform', '-n']); @@ -62,6 +90,27 @@ describe('platform command', function() { }); }); + it('should not save if no-save flag is provided', function(done) { + var processArguments = ['node', 'ionic', 'platform', 'add', 'ios', '--nosave']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(IonicResources, 'copyIconFilesIntoResources').andReturn(Q(true)); + spyOn(IonicResources, 'addIonicIcons').andReturn(Q(true)); + spyOn(State, 'savePlatform'); + spyOn(State, 'removePlatform'); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(0)); + + platform.run(null, argv, rawCliArguments).then(function() { + expect(IonicResources.copyIconFilesIntoResources).toHaveBeenCalledWith(appDirectory); + expect(IonicResources.addIonicIcons).toHaveBeenCalledWith(appDirectory, 'ios'); + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['platform', 'add', 'ios', '--nosave']); + expect(State.savePlatform).not.toHaveBeenCalled(); + expect(State.removePlatform).not.toHaveBeenCalled(); + done(); + }); + }); + it('should execute cordova using commands provided', function(done) { var processArguments = ['node', 'ionic', 'platform', 'add', 'ios']; var rawCliArguments = processArguments.slice(2); @@ -71,6 +120,7 @@ describe('platform command', function() { spyOn(IonicResources, 'addIonicIcons').andReturn(Q(true)); spyOn(State, 'savePlatform'); spyOn(State, 'removePlatform'); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(0)); platform.run(null, argv, rawCliArguments).then(function() { expect(IonicResources.copyIconFilesIntoResources).toHaveBeenCalledWith(appDirectory); @@ -91,6 +141,7 @@ describe('platform command', function() { spyOn(IonicResources, 'addIonicIcons').andReturn(Q(true)); spyOn(State, 'savePlatform'); spyOn(State, 'removePlatform'); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(0)); platform.run(null, argv, rawCliArguments).then(function() { expect(IonicResources.copyIconFilesIntoResources).not.toHaveBeenCalled(); diff --git a/spec/tasks/plugin.spec.js b/spec/tasks/plugin.spec.js index 89c034f738..352576efb0 100644 --- a/spec/tasks/plugin.spec.js +++ b/spec/tasks/plugin.spec.js @@ -65,6 +65,52 @@ describe('plugin command', function() { }); }); + it('should fail if any functions throw', function(done) { + var processArguments = ['node', 'ionic', 'plugin', 'add', 'thing', '--variable', 'hello']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var error = new Error('error occurred'); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q.reject(error)); + + plugin.run({}, argv, rawCliArguments).then(function() { + expect(log.error).toHaveBeenCalledWith(error); + done(); + }); + }); + + it('should fail if any functions throw and not log if not an instance of an Error', function(done) { + var processArguments = ['node', 'ionic', 'plugin', 'add', 'thing', '--variable', 'hello']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var error = 1; + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q.reject(error)); + + plugin.run({}, argv, rawCliArguments).then(function() { + expect(log.error).not.toHaveBeenCalled(); + done(); + }); + }); + + it('should not save if nosave flag is passed', function(done) { + var processArguments = ['node', 'ionic', 'plugin', 'add', 'thing', '--variable', 'hello', '--nosave']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(State, 'savePlugin'); + spyOn(State, 'removePlugin'); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(0)); + + plugin.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith( + ['plugin', 'add', 'thing', '--variable', 'hello', '--nosave']); + expect(State.savePlugin).not.toHaveBeenCalled(); + expect(State.removePlugin).not.toHaveBeenCalled(); + done(); + }); + }); + it('should add plugins', function(done) { var processArguments = ['node', 'ionic', 'plugin', 'add', 'thing', '--variable', 'hello']; var rawCliArguments = processArguments.slice(2); diff --git a/spec/tasks/prepare.spec.js b/spec/tasks/prepare.spec.js index 1cf646b07b..0330dfca60 100644 --- a/spec/tasks/prepare.spec.js +++ b/spec/tasks/prepare.spec.js @@ -41,5 +41,27 @@ describe('prepare command', function() { done(); }); }); + + it('should fail if any functions throw', function(done) { + var error = new Error('error occurred'); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q.reject(error)); + + prepare.run({}, argv, rawCliArguments).then(function() { + expect(log.error).toHaveBeenCalledWith(error); + done(); + }); + }); + + it('should fail if any functions throw and not log if not an instance of an Error', function(done) { + var argv = optimist(rawCliArguments).argv; + + var error = 1; + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q.reject(error)); + + prepare.run({}, argv, rawCliArguments).then(function() { + expect(log.error).not.toHaveBeenCalled(); + done(); + }); + }); }); }); diff --git a/spec/tasks/run.spec.js b/spec/tasks/run.spec.js index d57b3d0f4e..a2ee1c1190 100644 --- a/spec/tasks/run.spec.js +++ b/spec/tasks/run.spec.js @@ -4,7 +4,6 @@ var Q = require('q'); var optimist = require('optimist'); var cordovaUtils = require('../../lib/utils/cordova'); var os = require('os'); -var childProcess = require('child_process'); var IonicAppLib = require('ionic-app-lib'); var ConfigXml = IonicAppLib.configXml; var log = IonicAppLib.logging.logger; @@ -70,8 +69,8 @@ describe('run command', function() { it('should fail if the system is not Mac and the platform is iOS', function(done) { spyOn(os, 'platform').andReturn('windows'); - run.run(null, argv, rawCliArguments).catch(function(error) { - expect(error).toEqual('✗ You cannot run iOS unless you are on Mac OSX.'); + run.run(null, argv, rawCliArguments).then(function() { + expect(log.error).toHaveBeenCalledWith('✗ You cannot run iOS unless you are on Mac OSX.'); done(); }); }); @@ -144,7 +143,6 @@ describe('run command', function() { spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); - spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); }); it('should execute the command against the cordova util', function(done) { @@ -152,18 +150,49 @@ describe('run command', function() { var rawCliArguments = processArguments.slice(2); var argv = optimist(rawCliArguments).argv; + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); + run.run(null, argv, rawCliArguments).then(function() { expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['run', '-n', 'ios'], false, true); done(); }); }); + it('should fail if any functions throw', function(done) { + var processArguments = ['node', 'ionic', 'run', '-n']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var error = new Error('error occurred'); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q.reject(error)); + + run.run({}, argv, rawCliArguments).then(function() { + expect(log.error).toHaveBeenCalledWith(error); + done(); + }); + }); + + it('should fail if any functions throw and not log if not an instance of an Error', function(done) { + var processArguments = ['node', 'ionic', 'run', '-n']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var error = 1; + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q.reject(error)); + + run.run({}, argv, rawCliArguments).then(function() { + expect(log.error).not.toHaveBeenCalled(); + done(); + }); + }); it('should execute the command against the cordova util using the platform provided', function(done) { var processArguments = ['node', 'ionic', 'run', 'android']; var rawCliArguments = processArguments.slice(2); var argv = optimist(rawCliArguments).argv; + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); + run.run(null, argv, rawCliArguments).then(function() { expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['run', 'android'], false, true); done(); @@ -178,6 +207,7 @@ describe('run command', function() { spyOn(cordovaUtils, 'setupLiveReload').andReturn(Q({ blah: 'blah' })); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); run.run(null, argv, rawCliArguments).then(function() { expect(cordovaUtils.setupLiveReload).toHaveBeenCalledWith(argv, appDirectory); From 1b461af79666a2966aaa459ea594e833ddedc268 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 7 Jul 2016 16:00:51 -0500 Subject: [PATCH 1010/1100] chore(): move all extend references to a single util file. --- lib/config/commands.js | 2 +- lib/ionic/add.js | 2 +- lib/ionic/address.js | 2 +- lib/ionic/build.js | 2 +- lib/ionic/compile.js | 2 +- lib/ionic/config.js | 2 +- lib/ionic/docs.js | 2 +- lib/ionic/emulate.js | 2 +- lib/ionic/generate.js | 2 +- lib/ionic/help.js | 2 +- lib/ionic/hooks.js | 2 +- lib/ionic/info.js | 2 +- lib/ionic/io.js | 2 +- lib/ionic/lib.js | 2 +- lib/ionic/link.js | 2 +- lib/ionic/list.js | 2 +- lib/ionic/login.js | 2 +- lib/ionic/package.js | 2 +- lib/ionic/platform.js | 2 +- lib/ionic/plugin.js | 2 +- lib/ionic/prepare.js | 2 +- lib/ionic/push.js | 2 +- lib/ionic/remove.js | 2 +- lib/ionic/resources.js | 2 +- lib/ionic/run.js | 2 +- lib/ionic/security.js | 2 +- lib/ionic/serve.js | 2 +- lib/ionic/service.js | 2 +- lib/ionic/share.js | 2 +- lib/ionic/start.js | 2 +- lib/ionic/state.js | 2 +- lib/ionic/upload.js | 2 +- lib/utils/extend.js | 4 ++++ 33 files changed, 36 insertions(+), 32 deletions(-) create mode 100644 lib/utils/extend.js diff --git a/lib/config/commands.js b/lib/config/commands.js index bb25a20529..b0e132aa35 100644 --- a/lib/config/commands.js +++ b/lib/config/commands.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var path = require('path'); var COMMAND_DIRECTORY = 'ionic'; diff --git a/lib/ionic/add.js b/lib/ionic/add.js index b8892afe01..c78bc8ff6a 100644 --- a/lib/ionic/add.js +++ b/lib/ionic/add.js @@ -2,7 +2,7 @@ require('colors'); -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var childProcess = require('child_process'); var IonicAppLib = require('ionic-app-lib'); var appLibUtils = IonicAppLib.utils; diff --git a/lib/ionic/address.js b/lib/ionic/address.js index 880932f1b6..c9a31022c7 100644 --- a/lib/ionic/address.js +++ b/lib/ionic/address.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var IonicAppLib = require('ionic-app-lib'); var IonicConfig = IonicAppLib.config; var Serve = IonicAppLib.serve; diff --git a/lib/ionic/build.js b/lib/ionic/build.js index a2ff942961..cade8f6daa 100644 --- a/lib/ionic/build.js +++ b/lib/ionic/build.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var os = require('os'); var Q = require('q'); var IonicAppLib = require('ionic-app-lib'); diff --git a/lib/ionic/compile.js b/lib/ionic/compile.js index f66ab97162..bde2e8b27d 100644 --- a/lib/ionic/compile.js +++ b/lib/ionic/compile.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var IonicAppLib = require('ionic-app-lib'); var ConfigXml = IonicAppLib.configXml; var log = IonicAppLib.logging.logger; diff --git a/lib/ionic/config.js b/lib/ionic/config.js index c06c35eeab..0b46b1d9d6 100644 --- a/lib/ionic/config.js +++ b/lib/ionic/config.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var IonicAppLib = require('ionic-app-lib'); var ioLib = IonicAppLib.ioConfig; var IonicProject = IonicAppLib.project; diff --git a/lib/ionic/docs.js b/lib/ionic/docs.js index 6aa8b0a4a1..374401ed02 100644 --- a/lib/ionic/docs.js +++ b/lib/ionic/docs.js @@ -2,7 +2,7 @@ var prompt = require('prompt'); var _ = require('underscore'); -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var IonicDocs = require('./resources/docs.json'); var IonicAppLib = require('ionic-app-lib'); var log = IonicAppLib.logging.logger; diff --git a/lib/ionic/emulate.js b/lib/ionic/emulate.js index 696b4fbea9..22b7472a69 100644 --- a/lib/ionic/emulate.js +++ b/lib/ionic/emulate.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var os = require('os'); var Q = require('q'); var IonicAppLib = require('ionic-app-lib'); diff --git a/lib/ionic/generate.js b/lib/ionic/generate.js index f98d760daa..7d3c7906e2 100644 --- a/lib/ionic/generate.js +++ b/lib/ionic/generate.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var path = require('path'); var ionicAppLib = require('ionic-app-lib'); var log = ionicAppLib.logging.logger; diff --git a/lib/ionic/help.js b/lib/ionic/help.js index 9aa180dccd..1005ed78cd 100644 --- a/lib/ionic/help.js +++ b/lib/ionic/help.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var helpUtil = require('../utils/help'); var settings = { diff --git a/lib/ionic/hooks.js b/lib/ionic/hooks.js index 0b5b9f67d1..122a965058 100644 --- a/lib/ionic/hooks.js +++ b/lib/ionic/hooks.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var shelljs = require('shelljs'); var IonicAppLib = require('ionic-app-lib'); var log = IonicAppLib.logging.logger; diff --git a/lib/ionic/info.js b/lib/ionic/info.js index db3692644f..3953c0a003 100644 --- a/lib/ionic/info.js +++ b/lib/ionic/info.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var path = require('path'); var IonicAppLib = require('ionic-app-lib'); var Info = IonicAppLib.info; diff --git a/lib/ionic/io.js b/lib/ionic/io.js index 32510d3fc7..13d18453f5 100644 --- a/lib/ionic/io.js +++ b/lib/ionic/io.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var IonicAppLib = require('ionic-app-lib'); var ioLib = IonicAppLib.ioConfig; var IonicProject = IonicAppLib.project; diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index 783b7c24e3..bb3791659c 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -2,7 +2,7 @@ require('colors'); -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var fs = require('fs'); var path = require('path'); var shell = require('shelljs'); diff --git a/lib/ionic/link.js b/lib/ionic/link.js index 31f92016b0..debf0637ef 100644 --- a/lib/ionic/link.js +++ b/lib/ionic/link.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var fs = require('fs'); var path = require('path'); var IonicAppLib = require('ionic-app-lib'); diff --git a/lib/ionic/list.js b/lib/ionic/list.js index e4019c9108..ab264cbb2e 100644 --- a/lib/ionic/list.js +++ b/lib/ionic/list.js @@ -2,7 +2,7 @@ require('colors'); -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var path = require('path'); var IonicAppLib = require('ionic-app-lib'); var appLibUtils = IonicAppLib.utils; diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 0883e9201b..3a42709af4 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var IonicAppLib = require('ionic-app-lib'); var Login = IonicAppLib.login; var log = IonicAppLib.logging.logger; diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 3cb921a6ea..7283be0d7b 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -2,7 +2,7 @@ var path = require('path'); var _ = require('underscore'); -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var Q = require('q'); var moment = require('moment'); var expandTilde = require('expand-tilde'); diff --git a/lib/ionic/platform.js b/lib/ionic/platform.js index fd2103022f..153be064e1 100644 --- a/lib/ionic/platform.js +++ b/lib/ionic/platform.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var IonicAppLib = require('ionic-app-lib'); var IonicResources = IonicAppLib.resources; var ConfigXml = IonicAppLib.configXml; diff --git a/lib/ionic/plugin.js b/lib/ionic/plugin.js index 5139387046..4bb3c57796 100644 --- a/lib/ionic/plugin.js +++ b/lib/ionic/plugin.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var IonicAppLib = require('ionic-app-lib'); var ConfigXml = IonicAppLib.configXml; var State = IonicAppLib.state; diff --git a/lib/ionic/prepare.js b/lib/ionic/prepare.js index 0270fd2f3c..77cfda0613 100644 --- a/lib/ionic/prepare.js +++ b/lib/ionic/prepare.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var IonicAppLib = require('ionic-app-lib'); var ConfigXml = IonicAppLib.configXml; var log = IonicAppLib.logging.logger; diff --git a/lib/ionic/push.js b/lib/ionic/push.js index 5f93b4bcbe..c9e927c7d4 100644 --- a/lib/ionic/push.js +++ b/lib/ionic/push.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var fs = require('fs'); var path = require('path'); var parseUrl = require('url').parse; diff --git a/lib/ionic/remove.js b/lib/ionic/remove.js index b4f96fd4b0..e90bd3200e 100644 --- a/lib/ionic/remove.js +++ b/lib/ionic/remove.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var childProcess = require('child_process'); var IonicAppLib = require('ionic-app-lib'); var appLibUtils = IonicAppLib.utils; diff --git a/lib/ionic/resources.js b/lib/ionic/resources.js index 9f02faad48..6497d161cb 100644 --- a/lib/ionic/resources.js +++ b/lib/ionic/resources.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var IonicAppLib = require('ionic-app-lib'); var IonicResources = IonicAppLib.resources; var appLibUtils = IonicAppLib.utils; diff --git a/lib/ionic/run.js b/lib/ionic/run.js index 6f54c6f086..dc47b13b9b 100644 --- a/lib/ionic/run.js +++ b/lib/ionic/run.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var os = require('os'); var Q = require('q'); var IonicAppLib = require('ionic-app-lib'); diff --git a/lib/ionic/security.js b/lib/ionic/security.js index 42b7fdbfa3..728b08e416 100644 --- a/lib/ionic/security.js +++ b/lib/ionic/security.js @@ -5,7 +5,7 @@ var path = require('path'); var Q = require('q'); var expandTilde = require('expand-tilde'); var _ = require('underscore'); -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var IonicAppLib = require('ionic-app-lib'); var fail = IonicAppLib.utils.fail; var Login = IonicAppLib.login; diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 147ba75488..ba9e68b973 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var IonicAppLib = require('ionic-app-lib'); var appLibUtils = IonicAppLib.utils; var events = IonicAppLib.events; diff --git a/lib/ionic/service.js b/lib/ionic/service.js index bdf430c6bc..9a10490101 100644 --- a/lib/ionic/service.js +++ b/lib/ionic/service.js @@ -4,7 +4,7 @@ var fs = require('fs'); var path = require('path'); var exec = require('child_process').exec; var _ = require('underscore'); -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var bower = require('../utils/bower'); var IonicAppLib = require('ionic-app-lib'); var log = IonicAppLib.logging.logger; diff --git a/lib/ionic/share.js b/lib/ionic/share.js index cbdb8c909e..f61be09fee 100644 --- a/lib/ionic/share.js +++ b/lib/ionic/share.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var LoginTask = require('./login'); var IonicAppLib = require('ionic-app-lib'); var IonicProject = IonicAppLib.project; diff --git a/lib/ionic/start.js b/lib/ionic/start.js index 37eeaf2464..bc891fd7b2 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -1,7 +1,7 @@ require('shelljs/global'); require('colors'); -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var fs = require('fs'); var Q = require('q'); var templateUtils = require('../utils/templates'); diff --git a/lib/ionic/state.js b/lib/ionic/state.js index c5bbf2072e..daa4d8cfb9 100644 --- a/lib/ionic/state.js +++ b/lib/ionic/state.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var shelljs = require('shelljs'); var IonicAppLib = require('ionic-app-lib'); var IonicProject = IonicAppLib.project; diff --git a/lib/ionic/upload.js b/lib/ionic/upload.js index 5ac346937d..ee47ffa96b 100644 --- a/lib/ionic/upload.js +++ b/lib/ionic/upload.js @@ -1,6 +1,6 @@ 'use strict'; -var extend = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle +var extend = require('../utils/extend'); var IonicAppLib = require('ionic-app-lib'); var Login = IonicAppLib.login; var LoginTask = require('./login'); diff --git a/lib/utils/extend.js b/lib/utils/extend.js new file mode 100644 index 0000000000..e150c391d7 --- /dev/null +++ b/lib/utils/extend.js @@ -0,0 +1,4 @@ +'use strict'; + +// This module is used to allow use of the extend command +module.exports = Object.assign || require('util')._extend; // eslint-disable-line no-underscore-dangle From 92dfc6c3b9ce847fdd2eae8639636a0a75d4c730 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 7 Jul 2016 16:18:29 -0500 Subject: [PATCH 1011/1100] chore(tests): add tests to increase coverage on login, plugin and resources. --- spec/tasks/login.spec.js | 21 +++++++++++++++++++++ spec/tasks/plugin.spec.js | 18 ++++++++++++++++++ spec/tasks/resources.spec.js | 18 ++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/spec/tasks/login.spec.js b/spec/tasks/login.spec.js index 3608518d35..aec4cd21c6 100644 --- a/spec/tasks/login.spec.js +++ b/spec/tasks/login.spec.js @@ -156,5 +156,26 @@ describe('login command', function() { done(); }); }); + + it('should ask for email and password and eject an error if error', function(done) { + var processArguments = ['node', 'ionic', 'login']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + var error = 'error logging in'; + + spyOn(log, 'info'); + spyOn(prompt, 'start'); + spyOn(prompt, 'get').andCallFake(function(schema, callback) { + callback(error); + }); + + var promptForLogin = login.__get__('promptForLogin'); + + promptForLogin(argv).catch(function(error) { + expect(prompt.get).toHaveBeenCalled(); + expect(error).toEqual(error); + done(); + }); + }); }); }); diff --git a/spec/tasks/plugin.spec.js b/spec/tasks/plugin.spec.js index 352576efb0..4160aed14d 100644 --- a/spec/tasks/plugin.spec.js +++ b/spec/tasks/plugin.spec.js @@ -111,6 +111,24 @@ describe('plugin command', function() { }); }); + it('should add array of variables', function(done) { + var processArguments = ['node', 'ionic', 'plugin', 'add', 'thing', '--variable', 'hello', '--variable', 'man']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(State, 'savePlugin'); + spyOn(State, 'removePlugin'); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(0)); + + plugin.run(null, argv, rawCliArguments).then(function() { + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith( + ['plugin', 'add', 'thing', '--variable', 'hello', '--variable', 'man']); + expect(State.savePlugin).toHaveBeenCalledWith(appDirectory, 'thing', ['hello', 'man']); + expect(State.removePlugin).not.toHaveBeenCalled(); + done(); + }); + }); + it('should add plugins', function(done) { var processArguments = ['node', 'ionic', 'plugin', 'add', 'thing', '--variable', 'hello']; var rawCliArguments = processArguments.slice(2); diff --git a/spec/tasks/resources.spec.js b/spec/tasks/resources.spec.js index 0103984c4e..1be3c72e73 100644 --- a/spec/tasks/resources.spec.js +++ b/spec/tasks/resources.spec.js @@ -65,6 +65,24 @@ describe('resources command', function() { }); }); + it('should fail if applib generate fails and not log error if not instanceof Error', function(done) { + var processArguments = ['node', 'ionic', 'resources']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + var error = 'an error occurred'; + spyOn(process, 'cwd').andReturn('/current/directory'); + spyOn(Project, 'load'); + spyOn(IonicResources, 'generate').andReturn(Q.reject(error)); + spyOn(appLibUtils, 'fail'); + + // Expect failure + resources.run(null, argv, rawCliArguments).then(function() { + expect(appLibUtils.fail).not.toHaveBeenCalled(); + done(); + }); + }); + it('should pass platforms on to the applib generate', function(done) { var processArguments = ['node', 'ionic', 'resources', '--icon']; var rawCliArguments = processArguments.slice(2); From 1d5d3ad1da06fc23a47db19f4562b7f66b65b0f1 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 11 Jul 2016 10:35:55 -0500 Subject: [PATCH 1012/1100] chore(): add eslintignore file and fix eslint errors. --- .eslintignore | 1 + spec/tasks/info.spec.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 .eslintignore diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000000..4ebc8aea50 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +coverage diff --git a/spec/tasks/info.spec.js b/spec/tasks/info.spec.js index 3d532fd2c7..0c95995d97 100644 --- a/spec/tasks/info.spec.js +++ b/spec/tasks/info.spec.js @@ -5,7 +5,6 @@ var info = require('../../lib/ionic/info'); var IonicAppLib = require('ionic-app-lib'); var appLibUtils = IonicAppLib.utils; var appLibInfo = IonicAppLib.info; -var log = IonicAppLib.logging.logger; describe('info command', function() { describe('command settings', function() { From e673f1378d41e9472d2cf9506d6f768c094d74e2 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 11 Jul 2016 14:18:00 -0500 Subject: [PATCH 1013/1100] chore(): update eslint and istanbul versions. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index c1f3982b55..7481fb3f2f 100644 --- a/package.json +++ b/package.json @@ -85,9 +85,9 @@ "devDependencies": { "conventional-changelog-cli": "1.1.1", "coveralls": "^2.2.0", - "eslint": "^2.8.0", + "eslint": "^3.0.1", "github": "0.2.4", - "istanbul": "^0.4.3", + "istanbul": "^0.4.4", "jasmine-node": "1.14.5", "mock-require": "1.3.0", "rewire": "2.5.1" From eeba919de63598cda03bb14a0ee63c7b4bc5ef47 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 25 Jul 2016 15:42:37 -0500 Subject: [PATCH 1014/1100] chore(): update package.json and CHANGELOG. --- CHANGELOG.md | 10 ++++++++++ package.json | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88619bcad8..483626d7ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ + +# [2.0.0-beta.33](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.32...v2.0.0-beta.33) (2016-07-25) + + +### Bug Fixes + +* **cordova:** ensure that each cordova command will properly catch wh… ([#1154](https://github.com/driftyco/ionic-cli/issues/1154)) ([142128a](https://github.com/driftyco/ionic-cli/commit/142128a)) + + + # [2.0.0-beta.32](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.31...v2.0.0-beta.32) (2016-06-23) diff --git a/package.json b/package.json index 7481fb3f2f..88abedcaa0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.0.0-beta.32", + "version": "2.0.0-beta.33", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -67,7 +67,7 @@ "gulp": "3.8.8", "gulp-util": "3.0.7", "inquirer": "0.11.2", - "ionic-app-lib": "2.0.0-beta.18", + "ionic-app-lib": "2.0.0-beta.19", "moment": "2.11.1", "open": "0.0.5", "optimist": "0.6.0", From 3b7c4c08a704252de48d9453b8ecc5d5ecb3d109 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 27 Jul 2016 10:41:18 -0500 Subject: [PATCH 1015/1100] Updated to beta 34 because of issues with bundling dependencies in npm 3. --- CHANGELOG.md | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 483626d7ca..a8805113fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ + +# [2.0.0-beta.34](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.33...v2.0.0-beta.34) (2016-07-27) + + +### Bug Fixes + +* **npm:** republish with npm 2 to fix bundled dependencies + # [2.0.0-beta.33](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.32...v2.0.0-beta.33) (2016-07-25) diff --git a/package.json b/package.json index 88abedcaa0..8478c8cbfb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.0.0-beta.33", + "version": "2.0.0-beta.34", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From cc4ee640338ac90040dd9f54e6b74a272be2e7f1 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 27 Jul 2016 10:54:43 -0500 Subject: [PATCH 1016/1100] Updated to fix npm bundled dependencies. --- CHANGELOG.md | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8805113fb..8d5557d55b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ + +# [2.0.0-beta.35](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.34...v2.0.0-beta.35) (2016-07-27) + + +### Bug Fixes + +* **npm:** republish with npm 2 to fix bundled dependencies (again) + # [2.0.0-beta.34](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.33...v2.0.0-beta.34) (2016-07-27) diff --git a/package.json b/package.json index 8478c8cbfb..8a09e2868e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.0.0-beta.34", + "version": "2.0.0-beta.35", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 930857718d7cd8a09316d26f1689e5ac822e4271 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Tue, 2 Aug 2016 15:50:19 -0500 Subject: [PATCH 1017/1100] fix(stats): Updated cli to only track on valid commands. --- lib/cli.js | 6 +++--- spec/cli.spec.js | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index 1cc1b13e53..ca1461b0f1 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -119,9 +119,6 @@ Cli.run = function run(processArgv) { }); } - // Gather stats on the current command execution - IonicStats.t(); - var taskName = argv._[0]; var task = Cli.getTaskSettingsByName(taskName); log.debug('Task:', task); @@ -134,6 +131,9 @@ Cli.run = function run(processArgv) { var booleanOptions = Cli.getListOfBooleanOptions(task.options); + // Gather stats on the current command execution + IonicStats.t(); + // Remove all possible task boolean options from the args argv = optimist(rawCliArguments) .boolean(booleanOptions) diff --git a/spec/cli.spec.js b/spec/cli.spec.js index 7c49e3a4d9..e430a477ec 100644 --- a/spec/cli.spec.js +++ b/spec/cli.spec.js @@ -162,6 +162,15 @@ describe('Cli', function() { }); }); + it('should not track stats for cli for a bogus command', function(done) { + + IonicCli.run(['node', 'bin/ionic', 'helper']) + .then(function() { + expect(IonicStats.t).not.toHaveBeenCalled(); + done(); + }); + }); + it('should change cwd to project root for project tasks', function(done) { var processArguments = ['node', 'bin/ionic', 'fake']; var rawCliArguments = processArguments.slice(2); From cb01e1d92711ca61d6d3a9443284d9c2d61676a3 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 3 Aug 2016 11:54:44 -0500 Subject: [PATCH 1018/1100] fix(): change all exec commands within add, remove, and service to use sync. --- lib/ionic/add.js | 21 ++++++------ lib/ionic/remove.js | 20 ++++++------ lib/ionic/service.js | 68 ++++++++++++++++++++++++--------------- spec/tasks/add.spec.js | 14 ++++---- spec/tasks/remove.spec.js | 14 ++++---- 5 files changed, 77 insertions(+), 60 deletions(-) diff --git a/lib/ionic/add.js b/lib/ionic/add.js index c78bc8ff6a..50f81105b5 100644 --- a/lib/ionic/add.js +++ b/lib/ionic/add.js @@ -23,18 +23,17 @@ var settings = { function installBowerComponent(componentName) { var bowerInstallCommand = 'bower install --save-dev ' + componentName; - var result = childProcess.exec(bowerInstallCommand); - - if (result.code !== 0) { - - // Error happened, report it. - var errorMessage = 'Bower error, check that "'.red.bold + componentName.verbose + '"'.red.bold + - ' exists,'.red.bold + '\nor try running "'.red.bold + bowerInstallCommand.verbose + '" for more info.'.red.bold; + try { + var result = childProcess.execSync(bowerInstallCommand); + if (result.code === 0) { + return log.info('Bower component installed - ' + componentName); + } + } catch (e) {} // eslint-disable-line no-empty - appLibUtils.fail(errorMessage, 'add'); - } else { - log.info('Bower component installed - ' + componentName); - } + // Error happened, report it. + var errorMessage = 'Bower error, check that "'.red.bold + componentName + '"'.red.bold + + ' exists,'.red.bold + '\nor try running "'.red.bold + bowerInstallCommand + '" for more info.'.red.bold; + appLibUtils.fail(errorMessage, 'add'); } /** diff --git a/lib/ionic/remove.js b/lib/ionic/remove.js index e90bd3200e..2feccf90a3 100644 --- a/lib/ionic/remove.js +++ b/lib/ionic/remove.js @@ -21,17 +21,19 @@ var settings = { function uninstallBowerComponent(componentName) { var bowerUninstallCommand = 'bower uninstall --save-dev ' + componentName; - var result = childProcess.exec(bowerUninstallCommand); + try { + var result = childProcess.execSync(bowerUninstallCommand); - if (result.code !== 0) { - var errorMessage = 'Failed to find the bower component "'.red.bold + componentName.verbose + - '"'.red.bold + '.\nAre you sure it exists?'.red.bold; + if (result.code === 0) { + var message = 'Bower component removed - ' + componentName; + return log.info(message.red); + } + } catch (e) {} // eslint-disable-line no-empty - appLibUtils.fail(errorMessage, 'remove'); - } else { - var message = 'Bower component removed - ' + componentName; - log.info(message.red); - } + var errorMessage = 'Failed to find the bower component "'.red.bold + componentName + + '"'.red.bold + '.\nAre you sure it exists?'.red.bold; + + appLibUtils.fail(errorMessage, 'remove'); } /** diff --git a/lib/ionic/service.js b/lib/ionic/service.js index 9a10490101..0cdc5878f0 100644 --- a/lib/ionic/service.js +++ b/lib/ionic/service.js @@ -2,7 +2,7 @@ var fs = require('fs'); var path = require('path'); -var exec = require('child_process').exec; +var execSync = require('child_process').execSync; var _ = require('underscore'); var extend = require('../utils/extend'); var bower = require('../utils/bower'); @@ -59,29 +59,35 @@ function addServiceToIonicJson(serviceName) { function installBowerComponent(serviceName) { var bowerInstallCommand = 'bower link ionic-service-' + serviceName; - var result = exec(bowerInstallCommand); - if (result.code !== 0) { + try { + var result = execSync(bowerInstallCommand); - // Error happened, report it. - var errorMessage = 'Failed to find the service "'.bold + serviceName.verbose + - '"'.bold + '.\nAre you sure it exists?'.bold; - fail(errorMessage, 'service'); - } else { - addServiceToIonicJson(serviceName); - } + if (result.code !== 0) { + return addServiceToIonicJson(serviceName); + } + } catch (e) {} // eslint-disable-line no-empty + + // Error happened, report it. + var errorMessage = 'Failed to find the service "'.bold + serviceName.verbose + + '"'.bold + '.\nAre you sure it exists?'.bold; + fail(errorMessage, 'service'); } function uninstallBowerComponent(serviceName) { var bowerUninstallCommand = 'bower unlink ionic-service-' + serviceName; - var result = exec(bowerUninstallCommand); + try { + var result = execSync(bowerUninstallCommand); - if (result.code !== 0) { - var errorMessage = 'Failed to find the service "'.bold + serviceName.verbose + - '"'.bold + '.\nAre you sure it exists?'.bold; - fail(errorMessage, 'service'); - } + if (result.code !== 0) { + return; + } + } catch (e) {} // eslint-disable-line no-empty + + var errorMessage = 'Failed to find the service "'.bold + serviceName.verbose + + '"'.bold + '.\nAre you sure it exists?'.bold; + fail(errorMessage, 'service'); } function getBowerComponentsLocation() { @@ -117,12 +123,17 @@ function installBowerPlugins(directory, serviceName) { log.info('Installing cordova plugin - ' + plugin.name + ' (' + plugin.id + ')'); var installPluginCmd = 'ionic plugin add ' + plugin.uri; log.info(installPluginCmd); - var pluginInstallResult = exec(installPluginCmd); - if (pluginInstallResult.code !== 0) { - var errorMessage = 'Failed to find the plugin "'.bold + plugin.name.verbose + '"'.bold + '.'.bold; - fail(errorMessage, 'service'); - } + try { + var pluginInstallResult = execSync(installPluginCmd); + + if (pluginInstallResult.code === 0) { + return; + } + } catch (e) {} // eslint-disable-line no-empty + + var errorMessage = 'Failed to find the plugin "'.bold + plugin.name.verbose + '"'.bold + '.'.bold; + fail(errorMessage, 'service'); }); } @@ -131,12 +142,17 @@ function uninstallBowerPlugins(bowerJson) { log.info('Uninstalling cordova plugin - ' + plugin.name); var uninstallPluginCmd = 'ionic plugin rm ' + plugin.id; log.info(uninstallPluginCmd); - var pluginRemoveResult = exec(uninstallPluginCmd); - if (pluginRemoveResult.code !== 0) { - var errorMessage = 'Failed to find the plugin to remove "'.bold + plugin.name.verbose + '"'.bold + '.'.bold; - fail(errorMessage, 'service'); - } + try { + var pluginRemoveResult = execSync(uninstallPluginCmd); + + if (pluginRemoveResult.code === 0) { + return; + } + } catch (e) {} // eslint-disable-line no-empty + + var errorMessage = 'Failed to find the plugin to remove "'.bold + plugin.name.verbose + '"'.bold + '.'.bold; + fail(errorMessage, 'service'); }); } diff --git a/spec/tasks/add.spec.js b/spec/tasks/add.spec.js index 4cf2811309..80a7324ac2 100644 --- a/spec/tasks/add.spec.js +++ b/spec/tasks/add.spec.js @@ -36,7 +36,7 @@ describe('add command', function() { var argv = optimist(rawCliArguments).argv; beforeEach(function() { - spyOn(childProcess, 'exec'); + spyOn(childProcess, 'execSync'); }); it('should fail if bower is not installed', function() { @@ -111,23 +111,23 @@ describe('add command', function() { describe('installBowerComponent function', function() { - it('should call childProcess exec to install the bower component', function() { + it('should call childProcess execSync to install the bower component', function() { spyOn(log, 'info'); - spyOn(childProcess, 'exec').andReturn({ code: 0 }); + spyOn(childProcess, 'execSync').andReturn({ code: 0 }); var installBowerComponent = add.__get__('installBowerComponent'); installBowerComponent('thing'); - expect(childProcess.exec).toHaveBeenCalledWith('bower install --save-dev thing'); + expect(childProcess.execSync).toHaveBeenCalledWith('bower install --save-dev thing'); expect(log.info).toHaveBeenCalledWith('Bower component installed - thing'); }); - it('should call childProcess exec and call util fail if result.code is not equal to 0', function() { + it('should call childProcess execSync and call util fail if result.code is not equal to 0', function() { spyOn(appLibUtils, 'fail'); - spyOn(childProcess, 'exec').andReturn({ code: 1 }); + spyOn(childProcess, 'execSync').andReturn({ code: 1 }); var installBowerComponent = add.__get__('installBowerComponent'); installBowerComponent('thing'); - expect(childProcess.exec).toHaveBeenCalledWith('bower install --save-dev thing'); + expect(childProcess.execSync).toHaveBeenCalledWith('bower install --save-dev thing'); expect(appLibUtils.fail).toHaveBeenCalledWith(jasmine.any(String), 'add'); }); }); diff --git a/spec/tasks/remove.spec.js b/spec/tasks/remove.spec.js index cad7420485..58aef91f3d 100644 --- a/spec/tasks/remove.spec.js +++ b/spec/tasks/remove.spec.js @@ -36,7 +36,7 @@ describe('remove command', function() { var argv = optimist(rawCliArguments).argv; beforeEach(function() { - spyOn(childProcess, 'exec'); + spyOn(childProcess, 'execSync'); }); it('should fail if bower is not installed', function() { @@ -112,23 +112,23 @@ describe('remove command', function() { describe('uninstallBowerComponent function', function() { - it('should call childProcess exec to install the bower component', function() { + it('should call childProcess execSync to install the bower component', function() { spyOn(log, 'info'); - spyOn(childProcess, 'exec').andReturn({ code: 0 }); + spyOn(childProcess, 'execSync').andReturn({ code: 0 }); var uninstallBowerComponent = remove.__get__('uninstallBowerComponent'); uninstallBowerComponent('thing'); - expect(childProcess.exec).toHaveBeenCalledWith('bower uninstall --save-dev thing'); + expect(childProcess.execSync).toHaveBeenCalledWith('bower uninstall --save-dev thing'); expect(log.info).toHaveBeenCalledWith(jasmine.any(String)); }); - it('should call childProcess exec and call util fail if result.code is not equal to 0', function() { + it('should call childProcess execSync and call util fail if result.code is not equal to 0', function() { spyOn(appLibUtils, 'fail'); - spyOn(childProcess, 'exec').andReturn({ code: 1 }); + spyOn(childProcess, 'execSync').andReturn({ code: 1 }); var uninstallBowerComponent = remove.__get__('uninstallBowerComponent'); uninstallBowerComponent('thing'); - expect(childProcess.exec).toHaveBeenCalledWith('bower uninstall --save-dev thing'); + expect(childProcess.execSync).toHaveBeenCalledWith('bower uninstall --save-dev thing'); expect(appLibUtils.fail).toHaveBeenCalledWith(jasmine.any(String), 'remove'); }); }); From afc2a21f86b99aec572aca78622ce232885b4a4d Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 3 Aug 2016 12:22:16 -0500 Subject: [PATCH 1019/1100] chore(): Updated changelog and package.json versioning. --- CHANGELOG.md | 11 +++++++++++ package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d5557d55b..ae94bf6050 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ + +# [2.0.0-beta.36](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.35...v2.0.0-beta.36) (2016-08-03) + + +### Bug Fixes + +* **stats:** Updated cli to only track on valid commands. ([9308577](https://github.com/driftyco/ionic-cli/commit/9308577)) +* change all exec commands within add, remove, and service to use sync. ([cb01e1d](https://github.com/driftyco/ionic-cli/commit/cb01e1d)) + + + # [2.0.0-beta.35](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.34...v2.0.0-beta.35) (2016-07-27) diff --git a/package.json b/package.json index 8a09e2868e..00feddd0fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.0.0-beta.35", + "version": "2.0.0-beta.36", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 2bd7aeea83fe86eafe69e5f83e1ac766c58bbb39 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 11 Aug 2016 09:59:40 -0500 Subject: [PATCH 1020/1100] fix(setup): add setup sass back as a command for v1 projects. --- lib/config/commands.js | 1 + lib/ionic/setup.js | 44 +++++++++++++++++ spec/tasks/setup.spec.js | 103 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 lib/ionic/setup.js create mode 100644 spec/tasks/setup.spec.js diff --git a/lib/config/commands.js b/lib/config/commands.js index b0e132aa35..e63c4ee280 100644 --- a/lib/config/commands.js +++ b/lib/config/commands.js @@ -32,6 +32,7 @@ var affordances = { var orderedListOfCommands = [ 'start', 'serve', + 'setup', 'generate', 'platform', 'prepare', diff --git a/lib/ionic/setup.js b/lib/ionic/setup.js new file mode 100644 index 0000000000..484db3daca --- /dev/null +++ b/lib/ionic/setup.js @@ -0,0 +1,44 @@ +'use strict'; + +require('colors'); + +var extend = require('../utils/extend'); +var Q = require('q'); +var IonicAppLib = require('ionic-app-lib'); +var appLibUtils = IonicAppLib.utils; +var log = IonicAppLib.logging.logger; +var appLibSetup = IonicAppLib.setup; + +var settings = { + title: 'setup', + name: 'setup', + summary: 'Configure the project with a build tool ' + '(beta)'.yellow, + args: { + '[sass]': 'Setup the project to use Sass CSS precompiling' + }, + isProjectTask: true +} + +function run(ionic, argv) { + var taskName = argv._[1]; + if(!taskName) { + appLibUtils.fail('Missing setup task command.', 'setup'); + return Q(); + } + if (taskName !== 'sass') { + appLibUtils.fail('Invalid setup task command: ' + taskName, 'setup'); + return Q(); + } + + return appLibSetup.sassSetup(process.cwd()) + .then(function() { + log.info('Ionic setup complete'.green); + }) + .catch(function(error) { + log.error('Error from setup - ' + error); + }) +}; + +module.exports = extend(settings, { + run: run +}); diff --git a/spec/tasks/setup.spec.js b/spec/tasks/setup.spec.js new file mode 100644 index 0000000000..8fe940ff54 --- /dev/null +++ b/spec/tasks/setup.spec.js @@ -0,0 +1,103 @@ +'use strict'; + +var optimist = require('optimist'); +var setup = require('../../lib/ionic/setup'); +var IonicAppLib = require('ionic-app-lib'); +var appLibUtils = IonicAppLib.utils; +var appLibSetup = IonicAppLib.setup; +var log = IonicAppLib.logging.logger; +var Q = require('q'); + +describe('setup command', function() { + describe('command settings', function() { + it('should have a title', function() { + expect(setup.title).toBeDefined(); + expect(setup.title).not.toBeNull(); + expect(setup.title.length).toBeGreaterThan(0); + }); + + it('should have a summary', function() { + expect(setup.summary).toBeDefined(); + expect(setup.summary).not.toBeNull(); + expect(setup.summary.length).toBeGreaterThan(0); + }); + + it('should have a an option of sass', function() { + expect(setup.args).toEqual(jasmine.any(Object)); + expect(setup.args['[sass]']).toEqual(jasmine.any(String)); + }); + }); + + describe('run function', function() { + it('should fail if a command is not passed', function(done) { + var processArguments = ['node', 'ionic', 'setup']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(appLibUtils, 'fail'); + + // Expect failure + setup.run(null, argv, rawCliArguments).then(function() { + expect(appLibUtils.fail).toHaveBeenCalledWith('Missing setup task command.', 'setup'); + done(); + }); + }); + + it('should fail if a an invalid command passed', function(done) { + var processArguments = ['node', 'ionic', 'setup', 'stuff']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(appLibUtils, 'fail'); + + // Expect failure + setup.run(null, argv, rawCliArguments).then(function() { + expect(appLibUtils.fail).toHaveBeenCalledWith('Invalid setup task command: stuff', 'setup'); + done(); + }); + }); + + it('should succeed if sass was passed and sassSetup returns success', function(done) { + var processArguments = ['node', 'ionic', 'setup', 'sass']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + var dir = '/project/dir/path'; + + spyOn(appLibUtils, 'fail'); + spyOn(log, 'info'); + spyOn(log, 'error'); + spyOn(process, 'cwd').andReturn(dir); + spyOn(appLibSetup, 'sassSetup').andReturn(Q(true)); + + // Expect success + setup.run(null, argv, rawCliArguments).then(function() { + expect(appLibSetup.sassSetup).toHaveBeenCalledWith(dir); + expect(log.info).toHaveBeenCalled(); + expect(log.error).not.toHaveBeenCalled(); + done(); + }); + + }); + + it('should fail if sass was passed and sassSetup returns an error', function(done) { + var processArguments = ['node', 'ionic', 'setup', 'sass']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + var dir = '/project/dir/path'; + + spyOn(appLibUtils, 'fail'); + spyOn(log, 'info'); + spyOn(log, 'error'); + spyOn(process, 'cwd').andReturn(dir); + spyOn(appLibSetup, 'sassSetup').andReturn(Q.reject('error occurred')); + + // Expect success + setup.run(null, argv, rawCliArguments).then(function() { + expect(appLibSetup.sassSetup).toHaveBeenCalledWith(dir); + expect(log.info).not.toHaveBeenCalled(); + expect(log.error).toHaveBeenCalled(); + done(); + }); + }); + }); +}); From cf9b2700de557c7e54649af87cd0c692471ac363 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 11 Aug 2016 10:11:16 -0500 Subject: [PATCH 1021/1100] chore(): update changelog and increment package.json --- CHANGELOG.md | 10 ++++++++++ package.json | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae94bf6050..d9b569932b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ + +# [2.0.0-beta.37](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.36...v2.0.0-beta.37) (2016-08-11) + + +### Bug Fixes + +* **setup:** add setup sass back as a command for v1 projects. ([2bd7aee](https://github.com/driftyco/ionic-cli/commit/2bd7aee)) + + + # [2.0.0-beta.36](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.35...v2.0.0-beta.36) (2016-08-03) diff --git a/package.json b/package.json index 00feddd0fb..6b61cbd28b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.0.0-beta.36", + "version": "2.0.0-beta.37", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 83288696a2a46bcc28fce338ebc84e7307526021 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 11 Aug 2016 10:13:00 -0500 Subject: [PATCH 1022/1100] chore(): increment ionic-app-lib because it was missed. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6b61cbd28b..27a824b35f 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "gulp": "3.8.8", "gulp-util": "3.0.7", "inquirer": "0.11.2", - "ionic-app-lib": "2.0.0-beta.19", + "ionic-app-lib": "2.0.0-beta.20", "moment": "2.11.1", "open": "0.0.5", "optimist": "0.6.0", From 9185bb5828b2ecd85edd4dbce9ab165a6288641e Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 15 Aug 2016 08:37:12 -0500 Subject: [PATCH 1023/1100] Create 2.0 release. --- CHANGELOG.md | 5 +++++ package.json | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9b569932b..ca123cb459 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ + +# [2.0.0](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.37...v2.0.0) (2016-08-15) + +* create 2.0 release using beta.37 source. + # [2.0.0-beta.37](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.36...v2.0.0-beta.37) (2016-08-11) diff --git a/package.json b/package.json index 27a824b35f..99600f4426 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.0.0-beta.37", + "version": "2.0.0", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -67,7 +67,7 @@ "gulp": "3.8.8", "gulp-util": "3.0.7", "inquirer": "0.11.2", - "ionic-app-lib": "2.0.0-beta.20", + "ionic-app-lib": "2.0.0", "moment": "2.11.1", "open": "0.0.5", "optimist": "0.6.0", From 7db1850e7e340a95eebec1c95279686c37f95120 Mon Sep 17 00:00:00 2001 From: Dan Bucholtz Date: Tue, 13 Sep 2016 23:27:17 -0500 Subject: [PATCH 1024/1100] added in generators using @ionic/app-generators --- lib/ionic/generate.js | 47 +++++++++++++++++++++++++++---------------- package.json | 1 + 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/lib/ionic/generate.js b/lib/ionic/generate.js index 7d3c7906e2..6aa1095877 100644 --- a/lib/ionic/generate.js +++ b/lib/ionic/generate.js @@ -3,6 +3,7 @@ var extend = require('../utils/extend'); var path = require('path'); var ionicAppLib = require('ionic-app-lib'); +var appGenerator = require('@ionic/app-generators'); var log = ionicAppLib.logging.logger; var Project = ionicAppLib.project; var fail = ionicAppLib.utils.fail; @@ -16,10 +17,6 @@ var settings = { '--list': { title: 'List available generators', boolean: true - }, - '--typescript|--ts': { - boolean: true, - title: '(with --v2 only) Use TypeScript in generation' } }, isProjectTask: true @@ -48,28 +45,44 @@ function run(ionic, argv) { var name = argv._[2]; // TODO support multiple names var isTS = project.get('typescript') || argv.ts; - var ionicModule = loadToolingModule(); - if (argv.list) { - ionicModule.Generate.printAvailableGenerators(); + appGenerator.printAvailableGenerators(); return; } - var promise; - try { - promise = ionicModule.generate({ appDirectory: process.cwd(), generator: generator, name: name, isTS: isTS }); - } catch (err) { - if (err.message.indexOf('no generator available') !== -1) { + let includeSpec = argv.includeSpec ? true : false; + let includeSass = argv.skipScss ? false : true; + + var generatorOptions = { + generatorType: generator, + suppliedName: name, + includeSpec: includeSpec, + includeSass: includeSass + }; + + var projectStructureOptions = { + absoluteComponentDirPath: process.cwd() + '/src/components', + absoluteDirectiveDirPath: process.cwd() + '/src/components', + absolutePagesDirPath: process.cwd() + '/src/pages', + absolutePipeDirPath: process.cwd() + '/src/pipes', + absoluteProviderDirPath: process.cwd() + '/src/providers', + absolutePagesDirPath: process.cwd() + '/src/pages', + absolutePathTemplateBaseDir: process.cwd() + '/node_modules/ionic-angular/templates' + }; + + return appGenerator.generate(generatorOptions, projectStructureOptions).catch(function(err) { + if (err.message === 'Unknown Generator Type') { log.error(err.message); - ionicModule.Generate.printAvailableGenerators(); + appGenerator.printAvailableGenerators(); return; + } else { + return fail(err); } - return fail(err); - } - return promise; + }); + } catch (err) { - fail('There was an error generating your item:', err); + return fail('There was an error generating your item:', err); } } diff --git a/package.json b/package.json index 99600f4426..87e2b14e88 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ ], "license": "MIT", "dependencies": { + "@ionic/app-generators": "0.0.3", "cli-table": "0.3.1", "colors": "0.6.2", "expand-tilde": "1.2.0", From 2c6822d911864936aefb5d25774d1bdbd4bd6384 Mon Sep 17 00:00:00 2001 From: Dan Bucholtz Date: Tue, 13 Sep 2016 23:36:53 -0500 Subject: [PATCH 1025/1100] remove duplicate pages path --- lib/ionic/generate.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ionic/generate.js b/lib/ionic/generate.js index 6aa1095877..764e6faec3 100644 --- a/lib/ionic/generate.js +++ b/lib/ionic/generate.js @@ -66,7 +66,6 @@ function run(ionic, argv) { absolutePagesDirPath: process.cwd() + '/src/pages', absolutePipeDirPath: process.cwd() + '/src/pipes', absoluteProviderDirPath: process.cwd() + '/src/providers', - absolutePagesDirPath: process.cwd() + '/src/pages', absolutePathTemplateBaseDir: process.cwd() + '/node_modules/ionic-angular/templates' }; From 9a49df9ae419683d08d75d65c29dde55c94c9b11 Mon Sep 17 00:00:00 2001 From: Dan Bucholtz Date: Tue, 13 Sep 2016 23:42:04 -0500 Subject: [PATCH 1026/1100] let to var --- lib/ionic/generate.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ionic/generate.js b/lib/ionic/generate.js index 764e6faec3..8a1f28be46 100644 --- a/lib/ionic/generate.js +++ b/lib/ionic/generate.js @@ -50,8 +50,8 @@ function run(ionic, argv) { return; } - let includeSpec = argv.includeSpec ? true : false; - let includeSass = argv.skipScss ? false : true; + var includeSpec = argv.includeSpec ? true : false; + var includeSass = argv.skipScss ? false : true; var generatorOptions = { generatorType: generator, From 2bc79bf728ef64a9bf66dbbd1224929e1523c1d4 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Wed, 14 Sep 2016 21:03:15 -0400 Subject: [PATCH 1027/1100] feat(): add build step for npm scripts if they exists --- lib/cli.js | 101 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 93 insertions(+), 8 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index ca1461b0f1..2ace110d78 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -160,6 +160,9 @@ Cli.run = function run(processArgv) { return Q.fcall(task.run.bind(task), Cli, argv, rawCliArguments); } + // Check if there are npm scripts in the package.json + var npmScripts = Cli.loadNpmScripts(); + try { var gulpLoaded = Cli.loadGulpfile(); } catch (e) { @@ -174,20 +177,28 @@ Cli.run = function run(processArgv) { process.exit(1); } - if (!gulpLoaded) { + log.debug('\nNpm scripts:', npmScripts); + log.debug('Gulpfile found:', gulpLoaded, '\n'); + + if (npmScripts) { + return Cli.runWithNpmScripts(argv, task, rawCliArguments); + } else if (gulpLoaded) { + return Cli.runWithGulp(argv, task, rawCliArguments); + } - // warn if no gulpfile and it's a command that requires a build step + if (!gulpLoaded) { + // warn if no build file and it's a command that requires a build step // but still run the command with no hooks - if (Cli.isBuildCommand(taskName) && argv.v2) { - log.warn('WARN: No gulpfile found!'); + if (Cli.isBuildCommand(taskName) && argv.v2 && !npmScripts) { + log.warn('WARN: No build file found!'); log.warn('If your app requires a build step, you may want to ensure it runs before ' + taskName + '.\n'); } - log.debug('No gulpfile found, not running gulp hooks'); - - return Q.fcall(task.run.bind(task), Cli, argv, rawCliArguments); + if (!npmScripts) { + log.debug('No gulpfile found, not running gulp hooks'); + } } - return Cli.runWithGulp(argv, task, rawCliArguments); + return Q.fcall(task.run.bind(task), Cli, argv, rawCliArguments); } catch (ex) { return appLibUtils.fail(ex); @@ -272,6 +283,80 @@ Cli.loadGulpfile = function loadGulpfile() { return false; }; +Cli.runWithNpmScripts = function runWithNpmScripts(argv, taskInstance, rawCliArguments) { + var cmdName = argv._[0]; + var beforeHook = cmdName + ':before'; + var afterHook = cmdName + ':after'; + + var packageFile = require(path.resolve(process.cwd() + '/package.json')); + var scripts = packageFile.scripts; + + var beforeHookPromise = Q(true); + if (scripts[beforeHook]) { + log.info('\nRunning \'' + beforeHook + '\' npm script before ' + cmdName); + beforeHookPromise = Cli.runNpmHook(scripts[beforeHook]); + } + + // run beforeHook + return beforeHookPromise + + // run cmd + .then(function() { + return Q.fcall(taskInstance.run.bind(taskInstance), Cli, argv, rawCliArguments); + }) + + // run afterHook + .then(function() { + if (scripts[afterHook]) { + log.info('\nRunning \'' + afterHook + '\' npm script before ' + cmdName); + return Cli.runNpmHook(scripts[afterHook]); + } + }); +} + +Cli.runNpmHook = function runNpmHook(hook) { + var cmd = 'npm'; + var args = ['run', hook]; + var command = cmd + ' ' + args; + + var q = Q.defer(); + var spawn = require('cross-spawn-async'); + + var spawned = spawn('npm', args); + spawned.on('error', function(err) { + log.error('Unable to run spawn command ' + err); + }); + spawned.stdout.on('data', function(data) { + log.info(data.toString()); + }); + spawned.stderr.on('data', function(data) { + log.info(data.toString()); + }); + spawned.on('exit', function(code) { + log.debug('Spawn command', command, 'completed'); + if (code !== 0) { + return q.reject('There was an error with the spawned command: ' + command); + } + return q.resolve(); + }); + + return q.promise; +} + +Cli.loadNpmScripts = function loadNpmScripts() { + var fileName = 'package.json'; + + try { + var packageFile = require(path.resolve(process.cwd() + '/' + fileName)); + log.verbose('Package.json found scripts:', packageFile.scripts); + return packageFile.scripts; + } catch (e) { + throw e; + } + + return undefined; +}; + Cli.logEvents = function logEvents(gulpInst, finalTaskNames) { var prettyTime = require('pretty-hrtime'); var gutil = require('gulp-util'); From fac1c4d7b49e7c102269f3d5e0acef660d762bf5 Mon Sep 17 00:00:00 2001 From: perry Date: Thu, 15 Sep 2016 16:41:46 -0500 Subject: [PATCH 1028/1100] =?UTF-8?q?=E2=80=9CThis=20command=20has=20been?= =?UTF-8?q?=20deprecate=E2=80=9D=20typo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/ionic/add.js | 4 ++-- lib/ionic/lib.js | 4 ++-- lib/ionic/list.js | 4 ++-- lib/ionic/remove.js | 4 ++-- lib/ionic/service.js | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/ionic/add.js b/lib/ionic/add.js index 50f81105b5..8fffd7ecef 100644 --- a/lib/ionic/add.js +++ b/lib/ionic/add.js @@ -44,8 +44,8 @@ function installBowerComponent(componentName) { */ function run(ionic, argv) { - // This command will be deprecated in the future. - var deprecationMsg = 'This command has been ' + 'deprecate'.red + '. All ' + + // This command will be depreciated in the future. + var deprecationMsg = 'This command has been ' + 'depreciated'.red + '. All ' + 'resources are currently available in NPM and we recommend that you use NPM to manage these.\n' + 'More information is available here: https://github.com/driftyco/ionic-cli/wiki/Migrating-to-NPM-from-bower\n'; log.info(deprecationMsg.bold); diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index bb3791659c..74fe976384 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -32,8 +32,8 @@ var settings = { function run(ionic, argv) { - // This command will be deprecated in the future. - var deprecationMsg = 'This command has been ' + 'deprecate'.red + '. All ' + + // This command will be depreciated in the future. + var deprecationMsg = 'This command has been ' + 'depreciated'.red + '. All ' + 'resources are currently available in NPM and we recommend that you use NPM to manage these.\n' + 'More information is available here: https://github.com/driftyco/ionic-cli/wiki/Migrating-to-NPM-from-bower\n'; log.info(deprecationMsg.bold); diff --git a/lib/ionic/list.js b/lib/ionic/list.js index ab264cbb2e..6874b96bd5 100644 --- a/lib/ionic/list.js +++ b/lib/ionic/list.js @@ -37,8 +37,8 @@ function listComponents() { */ function run() { - // This command will be deprecated in the future. - var deprecationMsg = 'This command has been ' + 'deprecate'.red + '. All ' + + // This command will be depreciated in the future. + var deprecationMsg = 'This command has been ' + 'depreciated'.red + '. All ' + 'resources are currently available in NPM and we recommend that you use NPM to manage these.\n' + 'More information is available here: https://github.com/driftyco/ionic-cli/wiki/Migrating-to-NPM-from-bower\n'; log.info(deprecationMsg.bold); diff --git a/lib/ionic/remove.js b/lib/ionic/remove.js index 2feccf90a3..e64ea6d2c0 100644 --- a/lib/ionic/remove.js +++ b/lib/ionic/remove.js @@ -44,8 +44,8 @@ function uninstallBowerComponent(componentName) { */ function run(ionic, argv) { - // This command will be deprecated in the future. - var deprecationMsg = 'This command has been ' + 'deprecate'.red + '. All ' + + // This command will be depreciated in the future. + var deprecationMsg = 'This command has been ' + 'depreciated'.red + '. All ' + 'resources are currently available in NPM and we recommend that you use NPM to manage these.\n' + 'More information is available here: https://github.com/driftyco/ionic-cli/wiki/Migrating-to-NPM-from-bower\n'; log.info(deprecationMsg.bold); diff --git a/lib/ionic/service.js b/lib/ionic/service.js index 0cdc5878f0..aa00615429 100644 --- a/lib/ionic/service.js +++ b/lib/ionic/service.js @@ -182,8 +182,8 @@ function removeService(serviceName) { // For each plugins - call 'ionic add plugin ' function run(ionic, argv) { - // This command will be deprecated in the future. - var deprecationMsg = 'This command has been ' + 'deprecate'.red + '. All ' + + // This command will be depreciated in the future. + var deprecationMsg = 'This command has been ' + 'depreciated'.red + '. All ' + 'resources are currently available in NPM and we recommend that you use NPM to manage these.\n' + 'More information is available here: https://github.com/driftyco/ionic-cli/wiki/Migrating-to-NPM-from-bower\n'; log.info(deprecationMsg.bold); From f41a26409f68825c2b052ed968284161f1eff03e Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Fri, 16 Sep 2016 13:48:45 -0400 Subject: [PATCH 1029/1100] chore: update changelog for 2.1.0 release --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca123cb459..7f39283fbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ + +# [2.1.0](https://github.com/driftyco/ionic-cli/compare/v2.0.0...v2.1.0) (2016-09-16) + +* **refactor:** updated generate functionality to call @ionic/app-generators instead of ionic. ([45e9959](https://github.com/driftyco/ionic-cli/commit/45e99597b0f0afa81b992f4fb60388675679b595)) +* **feat:** add build step for npm scripts if they exist. ([2bc79bf](https://github.com/driftyco/ionic-cli/commit/2bc79bf728ef64a9bf66dbbd1224929e1523c1d4)) + # [2.0.0](https://github.com/driftyco/ionic-cli/compare/v2.0.0-beta.37...v2.0.0) (2016-08-15) From a6df88c7aa047468af9cae25e3c45eee627ac252 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Fri, 16 Sep 2016 13:05:40 -0500 Subject: [PATCH 1030/1100] Update io.js --- lib/ionic/io.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/io.js b/lib/ionic/io.js index 13d18453f5..7070392feb 100644 --- a/lib/ionic/io.js +++ b/lib/ionic/io.js @@ -12,7 +12,7 @@ var LoginTask = require('./login'); var settings = { title: 'io', name: 'io', - summary: 'Integrate your app with the ionic.io platform services ' + '(alpha)'.red, + summary: 'Integrate your app with the ionic.io platform services', args: { '': 'init'.yellow }, From 9476e3fb672f5f8a156b9171c8910e8bd0bafba9 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Fri, 16 Sep 2016 13:06:11 -0500 Subject: [PATCH 1031/1100] Update config.js --- lib/ionic/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/config.js b/lib/ionic/config.js index 0b46b1d9d6..18efd17be4 100644 --- a/lib/ionic/config.js +++ b/lib/ionic/config.js @@ -9,7 +9,7 @@ var appLibUtils = IonicAppLib.utils; var settings = { title: 'config', name: 'config', - summary: 'Set configuration variables for your ionic app ' + '(alpha)'.red, + summary: 'Set configuration variables for your ionic app', args: { '': 'set'.yellow + ', ' + 'unset'.yellow + ', ' + 'build'.yellow + ', or ' + 'info'.yellow, '[key]': 'The key to set', From c1b12062a7158eb8257730e6ea44e9aa334031bb Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Fri, 16 Sep 2016 13:06:42 -0500 Subject: [PATCH 1032/1100] Update package.js --- lib/ionic/package.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 7283be0d7b..96510e3a45 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -20,7 +20,7 @@ var Project = IonicAppLib.project; var settings = { title: 'package', name: 'package', - summary: 'Use Ionic Package to build your app ' + '(alpha)'.red, + summary: 'Use Ionic Package to build your app', args: { '': 'build android'.yellow + ', ' + 'build ios'.yellow + ', ' + 'list'.yellow + ', ' + 'info'.yellow + ', or ' + 'download'.yellow, From a6e17628f5ed92861a327ccb43f3a8529e49023d Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Fri, 16 Sep 2016 13:07:01 -0500 Subject: [PATCH 1033/1100] Update security.js --- lib/ionic/security.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/security.js b/lib/ionic/security.js index 728b08e416..d97ec68113 100644 --- a/lib/ionic/security.js +++ b/lib/ionic/security.js @@ -20,7 +20,7 @@ var Project = IonicAppLib.project; var settings = { title: 'security', name: 'security', - summary: 'Store your app\'s credentials for the Ionic Platform ' + '(alpha)'.red, + summary: 'Store your app\'s credentials for the Ionic Cloud', args: { '': 'profiles list'.yellow + ', ' + 'profiles add ""'.yellow + ', ' + 'credentials android'.yellow + ', or ' + 'credentials ios'.yellow, From 72b9fff6701727f619cc28619917a4e898388740 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Fri, 16 Sep 2016 13:08:22 -0500 Subject: [PATCH 1034/1100] Update io.js --- lib/ionic/io.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/io.js b/lib/ionic/io.js index 7070392feb..b5f51613fb 100644 --- a/lib/ionic/io.js +++ b/lib/ionic/io.js @@ -12,7 +12,7 @@ var LoginTask = require('./login'); var settings = { title: 'io', name: 'io', - summary: 'Integrate your app with the ionic.io platform services', + summary: 'Integrate your app with Ionic Cloud services', args: { '': 'init'.yellow }, From c3307fa41bb81b0923de2276e58050a7dccbe4a6 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Fri, 16 Sep 2016 13:10:34 -0500 Subject: [PATCH 1035/1100] Update push.js --- lib/ionic/push.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/push.js b/lib/ionic/push.js index c9e927c7d4..2382a4c4f3 100644 --- a/lib/ionic/push.js +++ b/lib/ionic/push.js @@ -18,7 +18,7 @@ var LoginTask = require('./login'); var settings = { title: 'push', name: 'push', - summary: 'Upload APNS and GCM credentials to Ionic Push ' + '(alpha)'.red, + summary: 'Upload APNS and GCM credentials to Ionic Push', options: { '--ios-dev-cert': 'Upload your development .p12 file to Ionic Push', '--ios-prod-cert': 'Upload your production .p12 file to Ionic Push', From 4c844540fdd8ed001e0cd90a1b25fe8048140ed6 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Fri, 16 Sep 2016 13:10:59 -0500 Subject: [PATCH 1036/1100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c922812ee6..160c55a729 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Ionic-Cli ========= -The Ionic Framework command line utility makes it easy to start, build, run, and emulate [Ionic](http://ionicframework.com/) apps. In addition, it comes with (optional!) integration with the [Ionic Platform](http://ionic.io/), a set of mobile services perfect for Ionic apps. +The Ionic Framework command line utility makes it easy to start, build, run, and emulate [Ionic](http://ionicframework.com/) apps. In addition, it comes with (optional!) integration with the [Ionic Cloud](http://ionic.io/), a set of mobile backend services perfect for Ionic apps. Use the `ionic --help` command for more detailed task information. From 6b35a9818c02cde5c92e107381b500ddc8a8d821 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Fri, 16 Sep 2016 13:13:41 -0500 Subject: [PATCH 1037/1100] chore(docs): readme ionic cloud --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 160c55a729..b2b55908e9 100644 --- a/README.md +++ b/README.md @@ -426,9 +426,9 @@ By default, starter projects are hooked up to Ionic's precompiled CSS file, whic 5. In the `ionic.project` file, add the JavaScript property `"gulpStartupTasks": ["sass", "watch"]` (this can also be customized to whatever gulp tasks you'd like). -# Ionic.io services +# Ionic Cloud services -The CLI supports operations to use backend services for your Ionic app. To get started, [visit the ionic.io homepage](http://ionic.io) and [sign up there](https://apps.ionic.io/signup). +The CLI integrates with Ionic Cloud, a set of backend services that integrate perfectly with Ionic apps. To get started, [visit the Ionic Cloud homepage](http://ionic.io) and [sign up](https://apps.ionic.io/signup). There are a few things you can utilize the CLI for to support ease of development. @@ -465,7 +465,7 @@ Successfully uploaded (f23j9fjs) This indicates you uploaded the application correctly, and the App ID is set to `f23j9fjs`. -You can then view that App ID from the View app or the application listing on ionic.io. +You can then view that App ID from the View app or the application listing on your [Ionic Cloud dashboard](http://apps.ionic.io/). ### Adding a note with your upload @@ -479,7 +479,7 @@ Use the `ionic link ` command to set your Ionic App ID to continue workin ## Share the application with another user -Use the `ionic share ` command to have an email sent to another person to have them view the Ionic application you are using. Note: You must have an ionic.io account as well as the user you are sharing with. +Use the `ionic share ` command to have an email sent to another person to have them view the Ionic application you are using. Note: You must have an [Ionic Cloud](http://ionic.io/) account as well as the user you are sharing with. # Ionic Docs From 5f2be6fc44dd5ef10936099b388264c0d41be5ab Mon Sep 17 00:00:00 2001 From: perry Date: Fri, 16 Sep 2016 13:18:09 -0500 Subject: [PATCH 1038/1100] error message grammar: deprecated not depreciated http://english.stackexchange.com/questions/45295/why-is-there-confusion-between-depreciated-and-deprecated --- lib/ionic/add.js | 4 ++-- lib/ionic/lib.js | 4 ++-- lib/ionic/list.js | 4 ++-- lib/ionic/remove.js | 4 ++-- lib/ionic/service.js | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/ionic/add.js b/lib/ionic/add.js index 8fffd7ecef..a447f5f952 100644 --- a/lib/ionic/add.js +++ b/lib/ionic/add.js @@ -44,8 +44,8 @@ function installBowerComponent(componentName) { */ function run(ionic, argv) { - // This command will be depreciated in the future. - var deprecationMsg = 'This command has been ' + 'depreciated'.red + '. All ' + + // This command will be deprecated in the future. + var deprecationMsg = 'This command has been ' + 'deprecated'.red + '. All ' + 'resources are currently available in NPM and we recommend that you use NPM to manage these.\n' + 'More information is available here: https://github.com/driftyco/ionic-cli/wiki/Migrating-to-NPM-from-bower\n'; log.info(deprecationMsg.bold); diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index 74fe976384..a85cc8128c 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -32,8 +32,8 @@ var settings = { function run(ionic, argv) { - // This command will be depreciated in the future. - var deprecationMsg = 'This command has been ' + 'depreciated'.red + '. All ' + + // This command will be deprecated in the future. + var deprecationMsg = 'This command has been ' + 'deprecated'.red + '. All ' + 'resources are currently available in NPM and we recommend that you use NPM to manage these.\n' + 'More information is available here: https://github.com/driftyco/ionic-cli/wiki/Migrating-to-NPM-from-bower\n'; log.info(deprecationMsg.bold); diff --git a/lib/ionic/list.js b/lib/ionic/list.js index 6874b96bd5..75a57f59af 100644 --- a/lib/ionic/list.js +++ b/lib/ionic/list.js @@ -37,8 +37,8 @@ function listComponents() { */ function run() { - // This command will be depreciated in the future. - var deprecationMsg = 'This command has been ' + 'depreciated'.red + '. All ' + + // This command will be deprecated in the future. + var deprecationMsg = 'This command has been ' + 'deprecated'.red + '. All ' + 'resources are currently available in NPM and we recommend that you use NPM to manage these.\n' + 'More information is available here: https://github.com/driftyco/ionic-cli/wiki/Migrating-to-NPM-from-bower\n'; log.info(deprecationMsg.bold); diff --git a/lib/ionic/remove.js b/lib/ionic/remove.js index e64ea6d2c0..248ab3038e 100644 --- a/lib/ionic/remove.js +++ b/lib/ionic/remove.js @@ -44,8 +44,8 @@ function uninstallBowerComponent(componentName) { */ function run(ionic, argv) { - // This command will be depreciated in the future. - var deprecationMsg = 'This command has been ' + 'depreciated'.red + '. All ' + + // This command will be deprecated in the future. + var deprecationMsg = 'This command has been ' + 'deprecated'.red + '. All ' + 'resources are currently available in NPM and we recommend that you use NPM to manage these.\n' + 'More information is available here: https://github.com/driftyco/ionic-cli/wiki/Migrating-to-NPM-from-bower\n'; log.info(deprecationMsg.bold); diff --git a/lib/ionic/service.js b/lib/ionic/service.js index aa00615429..ae489ce24d 100644 --- a/lib/ionic/service.js +++ b/lib/ionic/service.js @@ -182,8 +182,8 @@ function removeService(serviceName) { // For each plugins - call 'ionic add plugin ' function run(ionic, argv) { - // This command will be depreciated in the future. - var deprecationMsg = 'This command has been ' + 'depreciated'.red + '. All ' + + // This command will be deprecated in the future. + var deprecationMsg = 'This command has been ' + 'deprecated'.red + '. All ' + 'resources are currently available in NPM and we recommend that you use NPM to manage these.\n' + 'More information is available here: https://github.com/driftyco/ionic-cli/wiki/Migrating-to-NPM-from-bower\n'; log.info(deprecationMsg.bold); From 0c3ceb5dc5f6aa3e6ab7fff2e01cc1f8f7f6324d Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Fri, 16 Sep 2016 13:31:55 -0500 Subject: [PATCH 1039/1100] chore(error): friendly package error message --- lib/ionic/package.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 96510e3a45..39b808e8a7 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -59,7 +59,7 @@ function run(ionic, argv) { appId = project.get('app_id'); if (!appId) { - throw new Error('Missing Ionic App ID.'); + throw new Error('Missing Ionic App ID. Please make sure to upload your app or set your app_id in the ionic.config.json file.'); } } catch (ex) { return fail(ex, 'package'); From 271f2f984861d4faf17c9d5a91e382f061f60697 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Fri, 16 Sep 2016 13:35:46 -0500 Subject: [PATCH 1040/1100] chore(error): friendly(-er) package error message --- lib/ionic/package.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 39b808e8a7..0dbdd74da3 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -59,7 +59,7 @@ function run(ionic, argv) { appId = project.get('app_id'); if (!appId) { - throw new Error('Missing Ionic App ID. Please make sure to upload your app or set your app_id in the ionic.config.json file.'); + throw new Error('Missing Ionic App ID. Make sure to run "ionic upload" first or set your app_id in the ionic.config.json file.'); } } catch (ex) { return fail(ex, 'package'); From 4c8cdc65c252c19c78d0ebd9e42e283991a52c82 Mon Sep 17 00:00:00 2001 From: Tim Lancina Date: Fri, 16 Sep 2016 14:07:46 -0500 Subject: [PATCH 1041/1100] fix(): add missing cross-spawn-async dependency Looks like cross-spawn-async prints a warning on install, we should look into using cross-spawn instead. --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index 87e2b14e88..e6036fac5b 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "@ionic/app-generators": "0.0.3", "cli-table": "0.3.1", "colors": "0.6.2", + "cross-spawn-async": "2.1.9", "expand-tilde": "1.2.0", "form-data": "0.1.4", "gulp": "3.8.8", @@ -96,6 +97,7 @@ "bundledDependencies": [ "cli-table", "colors", + "cross-spawn-async", "expand-tilde", "form-data", "gulp", From bf52436a9d447856e87acb283949d947ee0dda2d Mon Sep 17 00:00:00 2001 From: Dan Bucholtz Date: Wed, 14 Sep 2016 10:09:12 -0500 Subject: [PATCH 1042/1100] tests passing --- spec/config/commands.spec.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spec/config/commands.spec.js b/spec/config/commands.spec.js index 27418821f3..bdfb901a80 100644 --- a/spec/config/commands.spec.js +++ b/spec/config/commands.spec.js @@ -14,10 +14,15 @@ describe('orderedListOfCommands', function() { var stat = fs.statSync(path.join(__dirname, '../../lib/ionic', file)); return !stat.isDirectory(); }) + .filter(function(file) { + // stats is private to us, so ignore it + return file !== 'stats.js'; + }) .map(function(file) { return file.replace('.js', ''); }) .sort(); + expect(taskFileList).toEqual(listOfCommands.sort()); }); }); From c1c55eff2add94d1bf8c4c73eceaca992ed52855 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Fri, 16 Sep 2016 16:10:20 -0400 Subject: [PATCH 1043/1100] test(cli): fix tests with gulpfile, add tests for npm scripts --- spec/cli.spec.js | 69 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/spec/cli.spec.js b/spec/cli.spec.js index e430a477ec..6b247ff47f 100644 --- a/spec/cli.spec.js +++ b/spec/cli.spec.js @@ -320,11 +320,12 @@ describe('Cli', function() { spyOn(FakeTask, 'run').andReturn(Q(true)); spyOn(fs, 'existsSync').andReturn(true); spyOn(IonicCli, 'loadGulpfile').andReturn(false); + spyOn(IonicCli, 'loadNpmScripts').andReturn(false); spyOn(log, 'warn'); IonicCli.run(['node', 'bin/ionic', 'build']) .then(function() { - expect(log.warn).toHaveBeenCalledWith('WARN: No gulpfile found!'); + expect(log.warn).toHaveBeenCalledWith('WARN: No build file found!'); done(); }); }); @@ -341,6 +342,29 @@ describe('Cli', function() { spyOn(FakeTask, 'run').andReturn(Q(true)); spyOn(fs, 'existsSync').andReturn(true); spyOn(IonicCli, 'loadGulpfile').andReturn(false); + spyOn(IonicCli, 'loadNpmScripts').andReturn(false); + + IonicCli.run(['node', 'bin/ionic', 'fake']) + .then(function() { + expect(IonicCli.loadGulpfile).toHaveBeenCalled(); + expect(IonicCli.runWithGulp).not.toHaveBeenCalled(); + done(); + }); + }); + + it('should not runWithGulp if npmScripts exist', function(done) { + var FakeTask = { + name: 'fake', + title: 'fake', + run: function() {}, + isProjectTask: true + }; + spyOn(IonicCli, 'getTaskSettingsByName').andReturn(FakeTask); + spyOn(IonicCli, 'runWithGulp'); + spyOn(FakeTask, 'run').andReturn(Q(true)); + spyOn(fs, 'existsSync').andReturn(true); + spyOn(IonicCli, 'loadGulpfile').andReturn(false); + spyOn(IonicCli, 'loadNpmScripts').andReturn(true); IonicCli.run(['node', 'bin/ionic', 'fake']) .then(function() { @@ -357,12 +381,55 @@ describe('Cli', function() { }; spyOn(IonicCli, 'getTaskSettingsByName').andReturn(FakeTask); spyOn(IonicCli, 'runWithGulp').andReturn(Q(true)); + spyOn(IonicCli, 'runWithNpmScripts').andReturn(Q(true)); spyOn(fs, 'existsSync').andReturn(true); spyOn(IonicCli, 'loadGulpfile').andReturn(true); + spyOn(IonicCli, 'loadNpmScripts').andReturn(false); IonicCli.run(['node', 'bin/ionic', 'fake']) .then(function() { expect(IonicCli.runWithGulp).toHaveBeenCalled(); + expect(IonicCli.runWithNpmScripts).not.toHaveBeenCalled(); + done(); + }); + }); + + it('should runWithNpmScripts without Gulpfile', function(done) { + var FakeTask = { + name: 'fake', + isProjectTask: true + }; + spyOn(IonicCli, 'getTaskSettingsByName').andReturn(FakeTask); + spyOn(IonicCli, 'runWithGulp').andReturn(Q(true)); + spyOn(IonicCli, 'runWithNpmScripts').andReturn(Q(true)); + spyOn(fs, 'existsSync').andReturn(true); + spyOn(IonicCli, 'loadGulpfile').andReturn(false); + spyOn(IonicCli, 'loadNpmScripts').andReturn(true); + + IonicCli.run(['node', 'bin/ionic', 'fake']) + .then(function() { + expect(IonicCli.runWithGulp).not.toHaveBeenCalled(); + expect(IonicCli.runWithNpmScripts).toHaveBeenCalled(); + done(); + }); + }); + + it('should runWithNpmScripts even with Gulpfile', function(done) { + var FakeTask = { + name: 'fake', + isProjectTask: true + }; + spyOn(IonicCli, 'getTaskSettingsByName').andReturn(FakeTask); + spyOn(IonicCli, 'runWithGulp').andReturn(Q(true)); + spyOn(IonicCli, 'runWithNpmScripts').andReturn(Q(true)); + spyOn(fs, 'existsSync').andReturn(true); + spyOn(IonicCli, 'loadGulpfile').andReturn(true); + spyOn(IonicCli, 'loadNpmScripts').andReturn(true); + + IonicCli.run(['node', 'bin/ionic', 'fake']) + .then(function() { + expect(IonicCli.runWithGulp).not.toHaveBeenCalled(); + expect(IonicCli.runWithNpmScripts).toHaveBeenCalled(); done(); }); }); From 2772a4c18f7e36038b35b913471fc1c7ed5cce9e Mon Sep 17 00:00:00 2001 From: Tim Lancina Date: Tue, 20 Sep 2016 10:43:16 -0500 Subject: [PATCH 1044/1100] chore(): bump version --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index e6036fac5b..09bf471cd3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.0.0", + "version": "2.1.0-beta.1", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -69,7 +69,7 @@ "gulp": "3.8.8", "gulp-util": "3.0.7", "inquirer": "0.11.2", - "ionic-app-lib": "2.0.0", + "ionic-app-lib": "2.1.0-beta.0", "moment": "2.11.1", "open": "0.0.5", "optimist": "0.6.0", From 734639caa37f0947fe27142ec3dde39606088fa8 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Tue, 20 Sep 2016 11:46:06 -0500 Subject: [PATCH 1045/1100] chore(serve): continue command after watch ready hook --- lib/cli.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/cli.js b/lib/cli.js index 2ace110d78..c4fb1e8106 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -327,7 +327,15 @@ Cli.runNpmHook = function runNpmHook(hook) { log.error('Unable to run spawn command ' + err); }); spawned.stdout.on('data', function(data) { - log.info(data.toString()); + var dataLines = data.toString().split('\n'); + for (var i = 0; i < dataLines.length; i++) { + if (dataLines[i].length) { + log.info(dataLines[i]); + if (dataLines[i].indexOf('watch ready') > -1) { + return q.resolve(); + } + } + } }); spawned.stderr.on('data', function(data) { log.info(data.toString()); From 3b7dd96fd1cf655d185bcdf89883ec49802f466c Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 20 Sep 2016 20:01:22 -0400 Subject: [PATCH 1046/1100] chore: bump version and release --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 09bf471cd3..8b53dd0263 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.1.0-beta.1", + "version": "2.1.0-beta.2", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -69,7 +69,7 @@ "gulp": "3.8.8", "gulp-util": "3.0.7", "inquirer": "0.11.2", - "ionic-app-lib": "2.1.0-beta.0", + "ionic-app-lib": "2.1.0-beta.1", "moment": "2.11.1", "open": "0.0.5", "optimist": "0.6.0", From 74ad1ea30f75ecddc1bf8c4dfbeaa48460f33602 Mon Sep 17 00:00:00 2001 From: Dan Bucholtz Date: Sun, 25 Sep 2016 11:49:08 -0500 Subject: [PATCH 1047/1100] fix(generators): allow configurable directory to be passed in --- lib/ionic/generate.js | 44 +++++++++++++++++++++++++++++++++++++------ package.json | 2 +- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/lib/ionic/generate.js b/lib/ionic/generate.js index 8a1f28be46..dc288d8789 100644 --- a/lib/ionic/generate.js +++ b/lib/ionic/generate.js @@ -60,13 +60,45 @@ function run(ionic, argv) { includeSass: includeSass }; + var componentsDir = path.join(process.cwd(), 'src', 'components'); + var directivesDir = path.join(process.cwd(), 'src', 'components'); + var pagesDir = path.join(process.cwd(), 'src', 'pages'); + var pipesDir = path.join(process.cwd(), 'src', 'pipes'); + var providersDir = path.join(process.cwd(), 'src', 'providers'); + var templateDir = path.join(process.cwd(), 'node_modules', 'ionic-angular', 'templates'); + + if (argv.componentsDir && argv.componentsDir.length > 0) { + componentsDir = path.resolve(argv.componentsDir); + } + + if (argv.directivesDir && argv.directivesDir.length > 0) { + directivesDir = path.resolve(argv.directivesDir); + } + + if (argv.pagesDir && argv.pagesDir.length > 0) { + pagesDir = path.resolve(argv.pagesDir); + } + + if (argv.pipesDir && argv.pipesDir.length > 0) { + pipesDir = path.resolve(argv.pipesDir); + } + + if (argv.providersDir && argv.providersDir.length > 0) { + providersDir = path.resolve(argv.providersDir); + } + + if (argv.templateDir && argv.templateDir.length > 0) { + templateDir = path.resolve(argv.templateDir); + } + + var projectStructureOptions = { - absoluteComponentDirPath: process.cwd() + '/src/components', - absoluteDirectiveDirPath: process.cwd() + '/src/components', - absolutePagesDirPath: process.cwd() + '/src/pages', - absolutePipeDirPath: process.cwd() + '/src/pipes', - absoluteProviderDirPath: process.cwd() + '/src/providers', - absolutePathTemplateBaseDir: process.cwd() + '/node_modules/ionic-angular/templates' + absoluteComponentDirPath: componentsDir, + absoluteDirectiveDirPath: directivesDir, + absolutePagesDirPath: pagesDir, + absolutePipeDirPath: pipesDir, + absoluteProviderDirPath: providersDir, + absolutePathTemplateBaseDir: templateDir }; return appGenerator.generate(generatorOptions, projectStructureOptions).catch(function(err) { diff --git a/package.json b/package.json index 8b53dd0263..bcb1b39a98 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.1.0-beta.2", + "version": "2.1.0-beta.3", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From d61763458f5c9b903bc03e42ad1353396c86cf25 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Tue, 27 Sep 2016 10:29:17 -0500 Subject: [PATCH 1048/1100] chore(): updated package.json version to 2.1.0. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bcb1b39a98..02fa111ce2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.1.0-beta.3", + "version": "2.1.0", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 184aa593c100b69ea7ef33ec4e1a178cfa904326 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 28 Sep 2016 14:52:00 -0500 Subject: [PATCH 1049/1100] Update circle.yml Ensure node 4 is used when running coveralls on CI server. --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index a0d097163d..5717fb5c42 100644 --- a/circle.yml +++ b/circle.yml @@ -3,4 +3,4 @@ test: - nvm use 0.12 && npm test - nvm use 4.0 && npm test post: - - npm run coveralls + - nvm use 4.0 && npm run coveralls From 59e2d795834ed340f2423bf1f8db7d6a29819975 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 29 Sep 2016 16:01:21 -0500 Subject: [PATCH 1050/1100] Update README.md Added disclaimer about using npm 2.7 because of our dependency now on a name spaced module. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b2b55908e9..0bae1517b6 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ $ npm install -g ionic *Note: For a global install of `-g ionic`, OSX/Linux users may need to prefix the command with `sudo` or can setup [proper file permissions on OSX for npm](http://www.johnpapa.net/how-to-use-npm-global-without-sudo-on-osx/) to install without `sudo`. * +*Please ensure that you have npm 2.7+ installed. We are now depending on scoped npm packages which where not introduced until this version. This occurred around the time of NodeJS 0.12.2* + ## Starting an Ionic App From 8df45a29018474843ec46265dcf006615eb29de9 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 3 Oct 2016 10:53:03 -0500 Subject: [PATCH 1051/1100] Update README.md Fixed a typo. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0bae1517b6..55f6beaaaf 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ $ npm install -g ionic *Note: For a global install of `-g ionic`, OSX/Linux users may need to prefix the command with `sudo` or can setup [proper file permissions on OSX for npm](http://www.johnpapa.net/how-to-use-npm-global-without-sudo-on-osx/) to install without `sudo`. * -*Please ensure that you have npm 2.7+ installed. We are now depending on scoped npm packages which where not introduced until this version. This occurred around the time of NodeJS 0.12.2* +*Please ensure that you have npm 2.7+ installed. We are now depending on scoped npm packages which were not introduced until this version. This occurred around the time of NodeJS 0.12.2* ## Starting an Ionic App From d22c78ed258b76361eb4ec67f1dd42eee9031bf5 Mon Sep 17 00:00:00 2001 From: Matt Lewis Date: Tue, 4 Oct 2016 19:11:34 +0200 Subject: [PATCH 1052/1100] docs(readme): update docs to reflect CLI 2.0 (#1366) Looks good. Thank you! --- README.md | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 55f6beaaaf..790fc060d1 100644 --- a/README.md +++ b/README.md @@ -217,7 +217,7 @@ __LiveReload__ By default, LiveReload will watch for changes in your `www/` directory, excluding `www/lib/`. To change this, you can specify a `watchPatterns` -property in the `ionic.project` file located in your project root to watch +property in the `ionic.config.json` file located in your project root to watch (or not watch) for specific changes. ```json @@ -237,30 +237,26 @@ the Grunt website. __Gulp Integration__ -When running `ionic serve`, you can have Ionic run any Gulp tasks you specify by putting them into your `ionic.project` as a `gulpStartupTasks` property as follows: - -```json -{ - "name": "SmoothRiders", - "gulpStartupTasks": [ - "watch" - ] -} +When running `ionic serve`, you can have Ionic run any Gulp tasks you specify by putting them into a gulp task called `serve:before`: +```js +gulp.task('serve:before', ['sass', 'watch']); ``` Now, when you run `ionic serve`, it will run the `watch` task while starting the Ionic server. If you would like to disable gulp from running during serve, pass the `--nogulp` option. +Your gulpfile must be named gulpfile.js or Gulpfile.js, there is currently no support for typescript, babel or coffeescript gulp files in the 2.0 CLI + NOTE: ```bash $ ionic setup sass ``` -will add a `watchPatterns` propery with the default values to your `ionic.project` -file that you can then edit, in addition to the `gulpStartupTasks` property +will add a `watchPatterns` propery with the default values to your `ionic.config.json` +file that you can then edit, in addition to the `serve:before` gulp task described in the [Using Sass](https://github.com/driftyco/ionic-cli/blob/master/README.md#using-sass) section. @@ -268,7 +264,7 @@ __Service Proxies:__ The `serve` command can add some proxies to the http server. These proxies are useful if you are developing in the browser and you need to make calls to an external API. With this feature you can proxy request to the external api through the ionic http server preventing the `No 'Access-Control-Allow-Origin' header is present on the requested resource` error. -In the `ionic.project` file you can add a property with an array of proxies you want to add. The proxies are object with the following properties: +In the `ionic.config.json` file you can add a property with an array of proxies you want to add. The proxies are object with the following properties: * `path`: string that will be matched against the beginning of the incoming request URL. * `proxyUrl`: a string with the url of where the proxied request should go. @@ -341,7 +337,7 @@ And of course, it supports Live Reload and all the other goodies we've added ove ## Serving an alternate document root -If you'd like to test your app in the browser and you use a folder other than the default of `www`, you can specify this folder in your `ionic.project` file. +If you'd like to test your app in the browser and you use a folder other than the default of `www`, you can specify this folder in your `ionic.config.json` file. You might also want to have the document root be created if you use some sort of build system, we suggest using `createDocumentRoot` for that so that `ionic serve` will create that folder for you. @@ -350,9 +346,6 @@ It is also advised you specify the watch patterns for this document root as well ```json { "name": "SmoothRiders", - "gulpStartupTasks": [ - "watch" - ], "documentRoot": "app", "createDocumentRoot": "app", "watchPatterns": [ @@ -425,7 +418,7 @@ By default, starter projects are hooked up to Ionic's precompiled CSS file, whic 2. Remove `` from the `` of the root `index.html` file. 3. Remove `` from the `` of the root `index.html` file. 4. Add `` to the `` of the root `index.html` file. -5. In the `ionic.project` file, add the JavaScript property `"gulpStartupTasks": ["sass", "watch"]` (this can also be customized to whatever gulp tasks you'd like). +5. In your `serve:before` gulp task, add the `sass` task `gulp.task('serve:before', ['sass', 'watch']);` (this can also be customized to whatever gulp tasks you'd like). # Ionic Cloud services From 0b458e4a7f4e4b681376957493ec770611b988e3 Mon Sep 17 00:00:00 2001 From: Mike Hartington Date: Fri, 7 Oct 2016 21:01:39 -0400 Subject: [PATCH 1053/1100] docs(): update readme (#1481) Update readme with more accurate information for recent Ionic V1 and V2 projects --- README.md | 195 +++++++++++++----------------------------------------- 1 file changed, 47 insertions(+), 148 deletions(-) diff --git a/README.md b/README.md index 790fc060d1..1b362459e3 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,9 @@ [![Build status][appveyor-badge]][appveyor-badge-url] [![npm][npm-badge]][npm-badge-url] -Ionic-Cli -========= +# Ionic-Cli -The Ionic Framework command line utility makes it easy to start, build, run, and emulate [Ionic](http://ionicframework.com/) apps. In addition, it comes with (optional!) integration with the [Ionic Cloud](http://ionic.io/), a set of mobile backend services perfect for Ionic apps. +The Ionic Framework command line utility makes it easy to start, build, run, and emulate [Ionic](http://ionicframework.com/) apps. In addition, it comes with (optional!) integration with the [Ionic Cloud](http://ionic.io/), a set of mobile backend services perfect for Ionic apps. Use the `ionic --help` command for more detailed task information. @@ -15,28 +14,47 @@ Use the `ionic --help` command for more detailed task information. $ npm install -g ionic ``` -*Note: For a global install of `-g ionic`, OSX/Linux users may need to prefix the command with `sudo` or can setup [proper file permissions on OSX for npm](http://www.johnpapa.net/how-to-use-npm-global-without-sudo-on-osx/) to install without `sudo`. * +*Note: For a global install of `-g ionic`, OSX/Linux users may need to prefix the command with `sudo` or can setup [proper file permissions on OSX for npm](http://www.johnpapa.net/how-to-use-npm-global-without-sudo-on-osx/) to install without `sudo`.* + + +Minimal node requirements: + +- NodeLTS or greater +- NPM 3x -*Please ensure that you have npm 2.7+ installed. We are now depending on scoped npm packages which were not introduced until this version. This occurred around the time of NodeJS 0.12.2* ## Starting an Ionic App +To start a Ionic 1x app, run: + ```bash $ ionic start myapp [template] ``` -Starter templates can either come from a named template, a Github repo, a Codepen, or a local directory. A starter template is what becomes the `www` directory within the Cordova project. +To start a Ionic 2 app, run: + +```bash +$ ionic start myapp [template] --v2 +``` + +Starter templates can either come from a named template, a Github repo, a Codepen, or a local directory. A starter template is what becomes the `www` directory for a V1 project, and a `src` directory for a V2 project -__Named template starters:__ +__Named V1 template starters:__ * [tabs](https://github.com/driftyco/ionic-starter-tabs) (Default) * [sidemenu](https://github.com/driftyco/ionic-starter-sidemenu) * [maps](https://github.com/driftyco/ionic-starter-maps) * [salesforce](https://github.com/driftyco/ionic-starter-salesforce) -* [complex-list](https://github.com/driftyco/ionic-starter-complex-list) * [blank](https://github.com/driftyco/ionic-starter-blank) +__Named V2 template starters:__ + +* [tabs](https://github.com/driftyco/ionic2-starter-tabs) (Default) +* [sidemenu](https://github.com/driftyco/ionic2-starter-sidemenu) +* [blank](https://github.com/driftyco/ionic2-starter-blank) + + __Github Repo starters:__ * Any Github repo url, ex: [https://github.com/driftyco/ionic-starter-tabs](https://github.com/driftyco/ionic-starter-tabs) @@ -62,7 +80,7 @@ __Command-line flags/options:__ [--id|-i] ............ Package name set in the config ex: com.mycompany.myapp [--no-cordova|-w] .... Do not create an app targeted for Cordova - [--sass|-s] ........... Setup the project to use Sass CSS precompiling + [--sass|-s] ........... Setup the project to use Sass CSS precompiling (V1 only) [--list|-l] .......... List starter templates available [--io-app-id] ......... The Ionic.io app ID to use @@ -75,7 +93,7 @@ $ ionic platform ios android ## Testing in a Browser -Use `ionic serve` to start a local development server for app dev and testing. This is useful for both desktop browser testing, and to test within a device browser which is connected to the same network. Additionally, this command starts LiveReload which is used to monitor changes in the file system. As soon as you save a file the browser is refreshed automatically. View [Using Sass](https://github.com/driftyco/ionic-cli/blob/master/README.md#using-sass) if you would also like to have `ionic serve` watch the project's Sass files. +Use `ionic serve` to start a local development server for app dev and testing. Additionally, this command starts LiveReload which is used to monitor changes in the file system. As soon as you save a file the browser is refreshed automatically. View [Using Sass](https://github.com/driftyco/ionic-cli/blob/master/README.md#using-sass) if you would also like to have `ionic serve` watch the project's Sass files. ```bash $ ionic serve [options] @@ -87,33 +105,6 @@ $ ionic serve [options] $ ionic build ios ``` -## Live Reload App During Development (beta) - -The `run` or `emulate` command will deploy the app to the specified platform devices/emulators. You can also run __live reload__ on the specified platform device by adding the `--livereload` option. The live reload functionality is similar to `ionic serve`, but instead of developing and debugging an app using a standard browser, the compiled hybrid app itself is watching for any changes to its files and reloading the app when needed. This reduces the requirement to constantly rebuild the app for small changes. However, any changes to plugins will still require a full rebuild. For live reload to work, the dev machine and device must be on the same local network, and the device must support [web sockets](http://caniuse.com/websockets). - -With live reload enabled, an app's console logs can also be printed to the terminal/command prompt by including the `--consolelogs` or `-c` option. Additionally, the development server's request logs can be printed out using `--serverlogs` or `-s` options. - -__Command-line flags/options for `run` and `emulate`:__ - - [--livereload|-l] ....... Live Reload app dev files from the device (beta) - [--consolelogs|-c] ...... Print app console logs to Ionic CLI (live reload req.) - [--serverlogs|-s] ....... Print dev server logs to Ionic CLI (live reload req.) - [--port|-p] ............. Dev server HTTP port (8100 default, live reload req.) - [--livereload-port|-i] .. Live Reload port (35729 default, live reload req.) - [--all|-a] .............. Specify to run on all addresses, 0.0.0.0, so you can view externally - [--browser|-w] .......... Specifies the browser to use (safari, firefox, chrome) - [--browseroption|-o] .... Specifies a path to open to (/#/tab/dash) - [--debug|--release] - -While the server is running for live reload, you can use the following commands within the CLI: - - restart or r to restart the client app from the root - goto or g and a url to have the app navigate to the given url - consolelogs or c to enable/disable console log output - serverlogs or s to enable/disable server log output - quit or q to shutdown the server and exit - - ## Emulating your app Deploys the Ionic app on specified platform emulator. This is simply an alias for `run --emulator`. @@ -196,26 +187,19 @@ When starting a new app and adding a platform `ionic platform add ios` - the def ## Crosswalk for Android -In v1.3.0 and later, you can now specify which browser to use in your Cordova Android projects. Currently we only support [Crosswalk](https://crosswalk-project.org/) and have plans to support more browsers later. - -Execute `ionic browser add crosswalk` to add the Crosswalk browser to your Android project. By default, this will install the `12.41.296.5` version of Crosswalk. +To install [Crosswalk](https://crosswalk-project.org/) for Android run: -If you'd like to specify a different version of Crosswalk, run `ionic browser list` to see which browsers are available and what versions. Then run `ionic browser add crosswalk@10.39.235.15`. +```bash +ionic plugin add cordova-plugin-crosswalk-webview --save +``` All that is left is to run the project as normal - `ionic run android`. -If you'd like to build without Crosswalk for Android SDK 21 or later, do the following: - -``` -ionic browser revert android -ionic build android --release -- --minSdkVersion 21 -``` - ## Advanced serve options __LiveReload__ -By default, LiveReload will watch for changes in your `www/` directory, +By default, for Ionic 1 projects, LiveReload will watch for changes in your `www/` directory, excluding `www/lib/`. To change this, you can specify a `watchPatterns` property in the `ionic.config.json` file located in your project root to watch (or not watch) for specific changes. @@ -249,7 +233,7 @@ If you would like to disable gulp from running during serve, pass the `--nogulp` Your gulpfile must be named gulpfile.js or Gulpfile.js, there is currently no support for typescript, babel or coffeescript gulp files in the 2.0 CLI -NOTE: +NOTE For V1: ```bash $ ionic setup sass @@ -325,16 +309,18 @@ __Command-line flags/options:__ ## Using Ionic Labs -We've extended the serve command to open the new Lab UI that features iOS and Android side-by-side. +We've extended the serve command to open the new Lab UI that features iOS, Android, and Windows side-by-side. ```bash $ ionic serve --lab ``` -If you've used the serve command before, you'll feel right at home with this one. Just like serve, it opens your app in a browser, but now it shows you what your app will look like on a phone, with both iOS and Android side by side. +If you've used the serve command before, you'll feel right at home with this one. Just like serve, it opens your app in a browser, but now it shows you what your app will look like on a phone, with both iOS, Android, Windows side by side. And of course, it supports Live Reload and all the other goodies we've added over the last couple of months. +NOTE: This does **not** emulate cordova or cordova plugins. So while the UI may feel like a native app, you'll still want to deploy to a device to test plugins. + ## Serving an alternate document root If you'd like to test your app in the browser and you use a folder other than the default of `www`, you can specify this folder in your `ionic.config.json` file. @@ -356,17 +342,6 @@ It is also advised you specify the watch patterns for this document root as well ``` -## Update Ionic lib - -Update Ionic library files, which are found in the `www/lib/ionic` directory. If bower is being used -by the project, this command will automatically run `bower update ionic`, otherwise this command updates -the local static files from Ionic's CDN. - -```bash -$ ionic lib update -``` -*Note: Using bower? This command does not update Ionic's dependencies. Run `bower update` to update Ionic and all of it's dependencies defined in `bower.json`.* - ## Packaging an app (beta) Using Ionic's service, you can compile and package your project into an app-store ready app without @@ -403,16 +378,18 @@ PROXY=internal.proxy.com ionic start my_app ``` -## Using Sass +## Using Sass (V1 Only) -By default, starter projects are hooked up to Ionic's precompiled CSS file, which is found in the project's `www/lib/ionic/css` directory, and is linked to the app in the head of the root `index.html` file. However, Ionic projects can also be customized using [Sass](http://sass-lang.com/), which gives developers and designers "superpowers" in terms of creating and maintaining CSS. Below are two ways to setup Sass for your Ionic project (the `ionic setup sass` command simply does the manual steps for you). Once Sass has been setup for your Ionic project, then the `ionic serve` command will also watch for Sass changes. +By default, V1 starter projects are hooked up to Ionic's precompiled CSS file, which is found in the project's `www/lib/ionic/css` directory, and is linked to the app in the head of the root `index.html` file. However, projects can also be customized using [Sass](http://sass-lang.com/), which gives developers and designers "superpowers" in terms of creating and maintaining CSS. Below are two ways to setup Sass for your Ionic project (the `ionic setup sass` command simply does the manual steps for you). Once Sass has been setup for your Ionic project, then the `ionic serve` command will also watch for Sass changes. -#### Setup Sass Automatically +For V2 projects, there's nothing you need to do! Ionic 2 projects by default are setup with sass and come with all the build process enabled. + +#### Setup Sass Automatically (V1) ionic setup sass -#### Setup Sass Manually +#### Setup Sass Manually (V1) 1. Run `npm install` from the working directory of an Ionic project. This will install [gulp.js](http://gulpjs.com/) and a few handy tasks, such as [gulp-sass](https://www.npmjs.org/package/gulp-sass) and [gulp-minify-css](https://www.npmjs.org/package/gulp-minify-css). 2. Remove `` from the `` of the root `index.html` file. @@ -476,16 +453,7 @@ Use the `ionic link ` command to set your Ionic App ID to continue workin Use the `ionic share ` command to have an email sent to another person to have them view the Ionic application you are using. Note: You must have an [Ionic Cloud](http://ionic.io/) account as well as the user you are sharing with. -# Ionic Docs - -To get Ionic documentation from the Ionic CLI, try using the `ionic docs` command. The command will look up your currently used Ionic version and open the docs specific for that version. Ex: RC0, RC1, etc. - -To view all docs, `ionic docs ls`. - -To get help with a doc you may not remember, just type the name close enough: `ionic docs list` and you will be prompted for suggestions that may match. - - -# Ionic Hooks +# Ionic Hooks (V1) Ionic provides some default hooks for you to use in your Cordova application. In versions prior to 1.3.18, these hooks were automatically installed via the `ionic platform` command. @@ -496,82 +464,13 @@ If you were a user who would still like to use those hooks, you can re-install t If you would like to remove these hooks yourself, use `ionic hooks remove` to get rid of them. -# Ionic State - -Ionic now provides a command to help you manage the state of your Ionic application. Previously Cordova hooks were used to save platforms and plugins to your `package.json` file. - -Now when using `ionic platform add`, `ionic platform rm`, `ionic plugin add`, or `ionic plugin rm`, the state command will automatically be used to save the platform or plugin state to the `package.json` file. - -If you would like to avoid saving plugins or platforms to the `package.json` file - pass in the `--nosave` option. (`ionic plugin add org.apache.cordova.file --nosave`). - -Your package.json file might look something like the following: - -```json -"cordovaPlatforms": [ - "ios", - { - "android": { - "id": "android", - "locator": "https://github.com/apache/cordova-android.git" - } - } -], -"cordovaPlugins": [ - "org.apache.cordova.device", - "org.apache.cordova.console", - "com.ionic.keyboard", - "org.apache.cordova.splashscreen", - { - "locator": "https://github.com/MobileChromeApps/cordova-crosswalk-engine.git", - "id": "org.cordova.croswalk" - }, - { - "locator": "/path/to/cloned/phonegap-facebook-plugin", - "id": "", - "variables": { - "APP_ID": "some_id", - "APP_NAME": "some_name" - } - } -] -``` - -## ionic state save - -The `ionic state save` command does some lookup in your platforms and plugins to save the current state of your cordova application. - -First it looks in your platforms folder to see which platforms are installed, and saves the name and version in your `package.json` file under the `cordovaPlatforms` attribute. - -Second it looks at your plugins `fetch.json` file to save the plugins installed both from the Cordova registry, locally installed plugins, as well as remote plugins from github or a remote HTTP url. - -## ionic state restore - -The `ionic state restore` command looks at the `cordovaPlugins` and `cordovaPlatforms` attributes in your `package.json` file to restore your application with those platforms and plugins. - -In the package.json file, `cordovaPlugins` will be stored as just their `ID` if they are installed from the Cordova registry. However, if they are local or remote, they will be stored as an object with those properties. Also to note, variables are stored for plugins with variables like the Facebook connect plugin. - -The `cordovaPlatforms` follow the same convention of either an `ID` for the platform name, if they are local or remote, they will be stored as an object with those properties. - -If you'd like, you can populate the `cordovaPlugins` and `cordovaPlatforms` by hand, then use the `ionic state restore` command to get your app ready to go. - -## ionic state clear - -The `ionic state clear` method will clear out your platforms and plugins directories, as well as removing the `cordovaPlugins` and `cordovaPlaforms` attributes in your `package.json` file. - -## ionic state reset - -The `ionic state reset` method will first remove your platforms and plugins folders. Then it will look at your `package.json` file to re-install the platforms and plugins as specified there. - -This command can be helpful for you to reinstall your plugins and platforms to get a fresh start. - - # Ionic CLI 2.0 ## Ionic Generators -First class support has come to the Ionic CLI to scaffold and generate Ionic and Angular 2 components. To use this feature, first install the V2 Ionic CLI: `npm install ionic@alpha` and start an app. +First class support has come to the Ionic CLI to scaffold and generate Ionic and Angular 2 components. -Once in the app folder, use the `generate` command (alias: `g`). +From your app directory, use the `generate` command (alias: `g`). Usage: * `ionic generate page About` - Generate a page named About with HTML, JavaScript, and Sass named `about`. From 951fbc867503fc420cda53d0df3f9595f67b4997 Mon Sep 17 00:00:00 2001 From: Justin Willis Date: Wed, 12 Oct 2016 13:52:25 -0500 Subject: [PATCH 1054/1100] chore(): add issue template (#1525) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 👍 --- .github/ISSUE_TEMPLATE.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..3887def3a2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,29 @@ +**Note: for support questions, please use one of these channels:** + +https://forum.ionicframework.com/ +http://ionicworldwide.herokuapp.com/ + +**Note: for build related issues you can open up an issue on the ionic-app-scripts repo** + +https://github.com/driftyco/ionic-app-scripts + + +#### Short description of the problem: + + +#### What behavior are you expecting? + + +**Steps to reproduce:** +1. +2. +3. + +``` +insert any relevant code between the above and below backticks +``` + +**Post the output of `ionic info` below please** + + +**Other information:** (e.g. stacktraces, related issues, suggestions how to fix, stackoverflow links, forum links, etc) \ No newline at end of file From 157aefd98d096f1dc6d26f195ed0cf6ea50d55e4 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Fri, 14 Oct 2016 12:48:55 -0500 Subject: [PATCH 1055/1100] fix(): ensure all child spawned npm process exit when the parent process ends. fixes #1544 --- lib/cli.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index c4fb1e8106..e40b686d12 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -312,7 +312,7 @@ Cli.runWithNpmScripts = function runWithNpmScripts(argv, taskInstance, rawCliArg return Cli.runNpmHook(scripts[afterHook]); } }); -} +}; Cli.runNpmHook = function runNpmHook(hook) { var cmd = 'npm'; @@ -347,9 +347,14 @@ Cli.runNpmHook = function runNpmHook(hook) { } return q.resolve(); }); + + // If this process ends ensure that we killed the spawned child + process.on('exit', function() { + spawned.kill(); + }); return q.promise; -} +}; Cli.loadNpmScripts = function loadNpmScripts() { var fileName = 'package.json'; From f74e3c3754af204b272a3bc5a7811bc09462760f Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Fri, 14 Oct 2016 14:22:47 -0500 Subject: [PATCH 1056/1100] feat(): ensure child spawned processes for npm run print colors. --- lib/cli.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index e40b686d12..83a173e863 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -316,30 +316,27 @@ Cli.runWithNpmScripts = function runWithNpmScripts(argv, taskInstance, rawCliArg Cli.runNpmHook = function runNpmHook(hook) { var cmd = 'npm'; - var args = ['run', hook]; + var args = ['run', hook, '--color']; var command = cmd + ' ' + args; var q = Q.defer(); var spawn = require('cross-spawn-async'); - var spawned = spawn('npm', args); + var spawned = spawn('npm', args, { stdio: ['pipe', 'pipe', process.stderr] }); spawned.on('error', function(err) { log.error('Unable to run spawn command ' + err); }); + spawned.stdout.pipe(process.stdout); spawned.stdout.on('data', function(data) { var dataLines = data.toString().split('\n'); for (var i = 0; i < dataLines.length; i++) { if (dataLines[i].length) { - log.info(dataLines[i]); if (dataLines[i].indexOf('watch ready') > -1) { return q.resolve(); } } } }); - spawned.stderr.on('data', function(data) { - log.info(data.toString()); - }); spawned.on('exit', function(code) { log.debug('Spawn command', command, 'completed'); if (code !== 0) { From 7e2dd6c6b627b1e3a16c5f0bd3290dd73658df17 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Fri, 14 Oct 2016 15:05:14 -0500 Subject: [PATCH 1057/1100] feat(): update to print ionic app-scripts version. --- lib/ionic/info.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ionic/info.js b/lib/ionic/info.js index 3953c0a003..8827b22b5e 100644 --- a/lib/ionic/info.js +++ b/lib/ionic/info.js @@ -20,6 +20,7 @@ function run() { Info.getIonicVersion(info, process.cwd()); Info.getIonicCliVersion(info, path.join(__dirname, '../../')); + Info.getIonicAppScriptsVersion(info, process.cwd()); Info.printInfo(info); From b8e9023022eab85b06e317e6f04a3bd26a7071c9 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Fri, 14 Oct 2016 15:10:17 -0500 Subject: [PATCH 1058/1100] chore(): increment package.json and update changelog. --- CHANGELOG.md | 16 ++++++++++++++++ package.json | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f39283fbd..04c31f108f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ + +## [2.1.1](https://github.com/driftyco/ionic-cli/compare/v2.1.0...v2.1.1) (2016-10-14) + + +### Bug Fixes + +* ensure all child spawned npm process exit when the parent process ends. fixes [#1544](https://github.com/driftyco/ionic-cli/issues/1544) ([157aefd](https://github.com/driftyco/ionic-cli/commit/157aefd)) + + +### Features + +* ensure child spawned processes for npm run print colors. ([f74e3c3](https://github.com/driftyco/ionic-cli/commit/f74e3c3)) +* update `ionic info` to print ionic app-scripts version. ([7e2dd6c](https://github.com/driftyco/ionic-cli/commit/7e2dd6c)) + + + # [2.1.0](https://github.com/driftyco/ionic-cli/compare/v2.0.0...v2.1.0) (2016-09-16) diff --git a/package.json b/package.json index 02fa111ce2..4459402861 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.1.0", + "version": "2.1.1", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -69,7 +69,7 @@ "gulp": "3.8.8", "gulp-util": "3.0.7", "inquirer": "0.11.2", - "ionic-app-lib": "2.1.0-beta.1", + "ionic-app-lib": "2.1.1", "moment": "2.11.1", "open": "0.0.5", "optimist": "0.6.0", From 5ee6657a5878c3c292793780c67dca268e98985a Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Tue, 18 Oct 2016 09:49:15 -0500 Subject: [PATCH 1059/1100] chore(): update info test to also spyOn getIonicAppScriptsVersion. --- spec/tasks/info.spec.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/tasks/info.spec.js b/spec/tasks/info.spec.js index 0c95995d97..5246bfbf2e 100644 --- a/spec/tasks/info.spec.js +++ b/spec/tasks/info.spec.js @@ -47,13 +47,14 @@ describe('info command', function() { expect(appLibInfo.checkRuntime).not.toHaveBeenCalled(); }); - it('should fail if any Info task throws', function() { + it('should gather info', function() { var gatheredInfo = { info: 'hi' }; spyOn(appLibUtils, 'fail'); spyOn(process, 'cwd').andReturn('/hello/how/areyou'); spyOn(appLibInfo, 'gatherInfo').andReturn(gatheredInfo); spyOn(appLibInfo, 'getIonicVersion'); spyOn(appLibInfo, 'getIonicCliVersion'); + spyOn(appLibInfo, 'getIonicAppScriptsVersion'); spyOn(appLibInfo, 'printInfo'); spyOn(appLibInfo, 'checkRuntime'); @@ -62,6 +63,7 @@ describe('info command', function() { expect(appLibInfo.gatherInfo).toHaveBeenCalled(); expect(appLibInfo.getIonicVersion).toHaveBeenCalledWith(gatheredInfo, '/hello/how/areyou'); expect(appLibInfo.getIonicCliVersion).toHaveBeenCalledWith(gatheredInfo, jasmine.any(String)); + expect(appLibInfo.getIonicAppScriptsVersion).toHaveBeenCalledWith(gatheredInfo, '/hello/how/areyou'); expect(appLibInfo.printInfo).toHaveBeenCalledWith(gatheredInfo); expect(appLibInfo.checkRuntime).toHaveBeenCalled(); From cdbeef6ee4702b6ac8790342b54d6bdb8688bbca Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Tue, 18 Oct 2016 10:17:47 -0500 Subject: [PATCH 1060/1100] fix(): use an environment variable to force colors instead of using the --colors flag. (#1557) --- lib/cli.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/cli.js b/lib/cli.js index 83a173e863..93d97c3241 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -316,9 +316,12 @@ Cli.runWithNpmScripts = function runWithNpmScripts(argv, taskInstance, rawCliArg Cli.runNpmHook = function runNpmHook(hook) { var cmd = 'npm'; - var args = ['run', hook, '--color']; + var args = ['run', hook]; var command = cmd + ' ' + args; + // Force colors for all spawned child processes + process.env['FORCE_COLOR'] = true; + var q = Q.defer(); var spawn = require('cross-spawn-async'); From 7285d5198cec81bbfcd4d6e6afc10b37d4548b32 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Tue, 18 Oct 2016 10:41:18 -0500 Subject: [PATCH 1061/1100] feat(): introduce new api for build and watch tasks for app-scripts. --- lib/ionic/build.js | 10 ++++ lib/ionic/emulate.js | 10 ++++ lib/ionic/run.js | 10 ++++ lib/ionic/serve.js | 96 ++++++++++++++++++++------------------ lib/utils/npmScripts.js | 73 +++++++++++++++++++++++++++++ spec/tasks/build.spec.js | 47 +++++++++++++++++++ spec/tasks/emulate.spec.js | 48 +++++++++++++++++++ spec/tasks/run.spec.js | 48 +++++++++++++++++++ spec/tasks/serve.spec.js | 67 ++++++++++++++++++++------ 9 files changed, 349 insertions(+), 60 deletions(-) create mode 100644 lib/utils/npmScripts.js diff --git a/lib/ionic/build.js b/lib/ionic/build.js index cade8f6daa..723ff7f47d 100644 --- a/lib/ionic/build.js +++ b/lib/ionic/build.js @@ -1,6 +1,7 @@ 'use strict'; var extend = require('../utils/extend'); +var npmScripts = require('../utils/npmScripts'); var os = require('os'); var Q = require('q'); var IonicAppLib = require('ionic-app-lib'); @@ -51,6 +52,15 @@ function run(ionic, argv, rawCliArguments) { []); return Q.all(promiseList).then(function() { + return npmScripts.hasIonicScript('build'); + }) + .then(function(hasBuildCommand) { + if (hasBuildCommand) { + return npmScripts.runIonicScript('build'); + } + return Q.resolve(); + }) + .then(function() { // ensure the content node was set back to its original return ConfigXml.setConfigXml(appDirectory, { diff --git a/lib/ionic/emulate.js b/lib/ionic/emulate.js index 22b7472a69..6c11474fce 100644 --- a/lib/ionic/emulate.js +++ b/lib/ionic/emulate.js @@ -1,6 +1,7 @@ 'use strict'; var extend = require('../utils/extend'); +var npmScripts = require('../utils/npmScripts'); var os = require('os'); var Q = require('q'); var IonicAppLib = require('ionic-app-lib'); @@ -68,6 +69,15 @@ function run(ionic, argv, rawCliArguments) { []); return Q.all(promiseList).then(function() { + return npmScripts.hasIonicScript('build'); + }) + .then(function(hasBuildCommand) { + if (hasBuildCommand) { + return npmScripts.runIonicScript('build'); + } + return Q.resolve(); + }) + .then(function() { if (isLiveReload) { return cordovaUtils.setupLiveReload(argv, appDirectory); } diff --git a/lib/ionic/run.js b/lib/ionic/run.js index dc47b13b9b..9188e3b844 100644 --- a/lib/ionic/run.js +++ b/lib/ionic/run.js @@ -1,6 +1,7 @@ 'use strict'; var extend = require('../utils/extend'); +var npmScripts = require('../utils/npmScripts'); var os = require('os'); var Q = require('q'); var IonicAppLib = require('ionic-app-lib'); @@ -68,6 +69,15 @@ function run(ionic, argv, rawCliArguments) { []); return Q.all(promiseList).then(function() { + return npmScripts.hasIonicScript('build'); + }) + .then(function(hasBuildCommand) { + if (hasBuildCommand) { + return npmScripts.runIonicScript('build'); + } + return Q.resolve(); + }) + .then(function() { if (isLiveReload) { return cordovaUtils.setupLiveReload(argv, appDirectory); } diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index cfd6cef21f..636e019b8e 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -1,6 +1,7 @@ 'use strict'; var extend = require('../utils/extend'); +var npmScripts = require('../utils/npmScripts'); var IonicAppLib = require('ionic-app-lib'); var appLibUtils = IonicAppLib.utils; var events = IonicAppLib.events; @@ -60,65 +61,68 @@ var settings = { }; function run(ionic, argv) { + + /** + * Before running the internal server check to see if npmscripts has + * a ionic specific serve command. If so then run it instead. + */ + return npmScripts.hasIonicScript('serve') + .then(function(hasServeCommand) { + if (hasServeCommand) { + return npmScripts.runIonicScript('serve', argv); + } else { + return runServer(argv); + } + }) + .catch(function(error) { + log.error('There was an error serving your Ionic application:', error); + }); +} + +function runServer(argv) { var project = null; var cwd = process.cwd(); + var options = {}; + var promise = Q(); - try { - project = IonicProject.load(cwd); - } catch (ex) { - log.error('Error occured', ex); - return appLibUtils.fail(ex.message); - } - - var options = Serve.loadSettings(argv, project); + project = IonicProject.load(cwd); + options = Serve.loadSettings(argv, project); options.appDirectory = cwd; // called from cli - ionic serve - cwd is this - options.nogulp = argv.nogulp; // check if CONNECT_LIVE_RELOAD_PORT is defined and use that instead if (process.env.CONNECT_LIVE_RELOAD_PORT) { options.liveReloadPort = process.env.CONNECT_LIVE_RELOAD_PORT; } - var promise; - - try { - if (argv.all || argv.a) { - options.address = '0.0.0.0'; - promise = Q(); - } else if (argv.address) { - options.address = argv.address; - promise = Q(); - } else { - promise = Serve.getAddress(options); - } - return promise - .then(function() { - return Serve.checkPorts(true, options.port, options.address, options); - }) - .then(function() { - if (options.runLivereload) { - return Serve.checkPorts(false, options.liveReloadPort, options.address, options); - } - }) - .then(function() { - return Serve.start(options); - }) - .then(function() { - return Serve.showFinishedServeMessage(options); - }) - .then(function() { - events.on('serverlog', log.info); - events.on('consolelog', log.info); - }) - .catch(function(error) { - log.info('There was an error serving your Ionic application:', error); - throw error; - }); - } catch (ex) { - appLibUtils.fail('Error with serve- ' + ex); + if (argv.all || argv.a) { + options.address = '0.0.0.0'; + } else if (argv.address) { + options.address = argv.address; + } else { + promise = Serve.getAddress(options); } + + return promise + .then(function() { + return Serve.checkPorts(true, options.port, options.address, options); + }) + .then(function() { + if (options.runLivereload) { + return Serve.checkPorts(false, options.liveReloadPort, options.address, options); + } + }) + .then(function() { + return Serve.start(options); + }) + .then(function() { + return Serve.showFinishedServeMessage(options); + }) + .then(function() { + events.on('serverlog', log.info); + events.on('consolelog', log.info); + }); } module.exports = extend(settings, { diff --git a/lib/utils/npmScripts.js b/lib/utils/npmScripts.js new file mode 100644 index 0000000000..4315c673fe --- /dev/null +++ b/lib/utils/npmScripts.js @@ -0,0 +1,73 @@ +'use strict'; + +var Q = require('q'); +var path = require('path'); +var fs = require('fs'); +var IonicAppLib = require('ionic-app-lib'); +var log = IonicAppLib.logging.logger; + + +function hasIonicScript(name) { + return getPackageJsonContents().then(function(packageJsonContents) { + return packageJsonContents.hasOwnProperty('scripts') && + packageJsonContents.scripts.hasOwnProperty('ionic:' + name); + }); +} + +function runIonicScript(name, argv) { + var spawn = require('cross-spawn-async'); + var scriptName = 'ionic:' + name; + var q = Q.defer(); + + var scriptSpawn = spawn('npm', ['run', scriptName].concat(argv || []), { stdio: 'inherit' }) + .on('error', function(err) { + log.debug('Spawn command', scriptName, 'failed'); + q.reject('Unable to run spawn command ' + err); + }); + + scriptSpawn.on('exit', function(code) { + log.debug('Spawn command', scriptName, 'completed'); + if (code !== 0) { + return q.reject('There was an error with the spawned command: ' + name); + } + return q.resolve(); + }); + + return q.promise; +} + +/** + * Function is memoized so that it should only access the filesystem one time. + * Everytime after that it just returns the saved packageJson wrapped in a resolved promise. + */ +var getPackageJsonContents = (function() { + var packageJson; + + return function f() { + var packageJsonPath = path.resolve(process.cwd() + '/package.json'); + var q = Q.defer(); + + if (packageJson) { + Q.resolve(packageJson); + } + + try { + fs.readFile(packageJsonPath, 'utf8', function(err, dataString) { + if (!err) { + packageJson = JSON.parse(dataString); + } + q.resolve(packageJson); + }); + } catch (e) { + q.resolve(packageJson); + } + + return q.promise; + }; +})(); + +module.exports = { + getPackageJsonContents: getPackageJsonContents, + hasIonicScript: hasIonicScript, + runIonicScript: runIonicScript +}; diff --git a/spec/tasks/build.spec.js b/spec/tasks/build.spec.js index 1a563782b0..add186aa2a 100644 --- a/spec/tasks/build.spec.js +++ b/spec/tasks/build.spec.js @@ -8,6 +8,7 @@ var IonicAppLib = require('ionic-app-lib'); var ConfigXml = IonicAppLib.configXml; var log = IonicAppLib.logging.logger; var build = require('../../lib/ionic/build'); +var npmScripts = require('../../lib/utils/npmScripts'); describe('build command', function() { beforeEach(function() { @@ -54,6 +55,7 @@ describe('build command', function() { spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(false)); spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); }); @@ -90,6 +92,7 @@ describe('build command', function() { spyOn(cordovaUtils, 'installPlatform').andReturn(Q(true)); spyOn(cordovaUtils, 'installPlugins').andReturn(Q(true)); + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(false)); }); it('should try to install the cordova platform if it is not installed', function(done) { @@ -161,4 +164,48 @@ describe('build command', function() { }); }); }); + + describe('npmScripts check', function() { + var appDirectory = '/ionic/app/path'; + var processArguments = ['node', 'ionic', 'build', 'ios', '-n']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + beforeEach(function() { + spyOn(os, 'platform').andReturn('darwin'); + spyOn(process, 'cwd').andReturn(appDirectory); + + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(0)); + }); + + it('should not call runIonicScript if hasIonicScript is false', function(done) { + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(false)); + spyOn(npmScripts, 'runIonicScript'); + + build.run(null, argv, rawCliArguments).then(function() { + expect(npmScripts.hasIonicScript).toHaveBeenCalledWith('build'); + expect(npmScripts.runIonicScript).not.toHaveBeenCalled(); + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['build', 'ios', '-n']); + done(); + }).catch(function(e) { + console.log(e); + }); + }); + + it('should call runIonicScript if hasIonicScript is true', function(done) { + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(true)); + spyOn(npmScripts, 'runIonicScript').andReturn(Q(true)); + + build.run(null, argv, rawCliArguments).then(function() { + expect(npmScripts.hasIonicScript).toHaveBeenCalledWith('build'); + expect(npmScripts.runIonicScript).toHaveBeenCalledWith('build'); + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['build', 'ios', '-n']); + done(); + }).catch(function(e) { + console.log(e); + }); + }); + }); }); diff --git a/spec/tasks/emulate.spec.js b/spec/tasks/emulate.spec.js index 9529948092..46ea4fefc5 100644 --- a/spec/tasks/emulate.spec.js +++ b/spec/tasks/emulate.spec.js @@ -8,6 +8,7 @@ var IonicAppLib = require('ionic-app-lib'); var ConfigXml = IonicAppLib.configXml; var log = IonicAppLib.logging.logger; var emulate = require('../../lib/ionic/emulate'); +var npmScripts = require('../../lib/utils/npmScripts'); describe('emulate command', function() { beforeEach(function() { @@ -52,6 +53,7 @@ describe('emulate command', function() { spyOn(process, 'cwd').andReturn(appDirectory); spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(false)); }); it('should default to iOS for the platform', function(done) { @@ -86,6 +88,7 @@ describe('emulate command', function() { spyOn(process, 'cwd').andReturn(appDirectory); spyOn(cordovaUtils, 'installPlatform').andReturn(Q(true)); spyOn(cordovaUtils, 'installPlugins').andReturn(Q(true)); + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(false)); spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); }); @@ -138,6 +141,7 @@ describe('emulate command', function() { beforeEach(function() { spyOn(process, 'cwd').andReturn(appDirectory); spyOn(os, 'platform').andReturn('darwin'); + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(false)); }); it('should fail if any functions throw', function(done) { @@ -222,4 +226,48 @@ describe('emulate command', function() { }); }); }); + + describe('npmScripts check', function() { + var processArguments = ['node', 'ionic', 'emulate', 'android']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + beforeEach(function() { + var appDirectory = '/ionic/app/path'; + spyOn(process, 'cwd').andReturn(appDirectory); + spyOn(os, 'platform').andReturn('darwin'); + + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); + }); + + it('should not call runIonicScript if hasIonicScript is false', function(done) { + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(false)); + spyOn(npmScripts, 'runIonicScript'); + + emulate.run(null, argv, rawCliArguments).then(function() { + expect(npmScripts.hasIonicScript).toHaveBeenCalledWith('build'); + expect(npmScripts.runIonicScript).not.toHaveBeenCalled(); + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['emulate', 'android'], false, true); + done(); + }).catch(function(e) { + console.log(e); + }); + }); + + it('should call runIonicScript if hasIonicScript is true', function(done) { + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(true)); + spyOn(npmScripts, 'runIonicScript').andReturn(Q(true)); + + emulate.run(null, argv, rawCliArguments).then(function() { + expect(npmScripts.hasIonicScript).toHaveBeenCalledWith('build'); + expect(npmScripts.runIonicScript).toHaveBeenCalledWith('build'); + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['emulate', 'android'], false, true); + done(); + }).catch(function(e) { + console.log(e); + }); + }); + }); }); diff --git a/spec/tasks/run.spec.js b/spec/tasks/run.spec.js index a2ee1c1190..08240aa99a 100644 --- a/spec/tasks/run.spec.js +++ b/spec/tasks/run.spec.js @@ -8,6 +8,7 @@ var IonicAppLib = require('ionic-app-lib'); var ConfigXml = IonicAppLib.configXml; var log = IonicAppLib.logging.logger; var run = require('../../lib/ionic/run'); +var npmScripts = require('../../lib/utils/npmScripts'); describe('run command', function() { beforeEach(function() { @@ -54,6 +55,7 @@ describe('run command', function() { spyOn(process, 'cwd').andReturn(appDirectory); spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(false)); spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); }); @@ -90,6 +92,7 @@ describe('run command', function() { spyOn(cordovaUtils, 'installPlatform').andReturn(Q(true)); spyOn(cordovaUtils, 'installPlugins').andReturn(Q(true)); + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(false)); spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); }); @@ -143,6 +146,7 @@ describe('run command', function() { spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(false)); }); it('should execute the command against the cordova util', function(done) { @@ -217,4 +221,48 @@ describe('run command', function() { }); }); }); + + describe('npmScripts check', function() { + var processArguments = ['node', 'ionic', 'run', 'android']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + beforeEach(function() { + var appDirectory = '/ionic/app/path'; + spyOn(process, 'cwd').andReturn(appDirectory); + spyOn(os, 'platform').andReturn('darwin'); + + spyOn(cordovaUtils, 'isPlatformInstalled').andReturn(true); + spyOn(cordovaUtils, 'arePluginsInstalled').andReturn(true); + spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(0)); + }); + + it('should not call runIonicScript if hasIonicScript is false', function(done) { + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(false)); + spyOn(npmScripts, 'runIonicScript'); + + run.run(null, argv, rawCliArguments).then(function() { + expect(npmScripts.hasIonicScript).toHaveBeenCalledWith('build'); + expect(npmScripts.runIonicScript).not.toHaveBeenCalled(); + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['run', 'android'], false, true); + done(); + }).catch(function(e) { + console.log(e); + }); + }); + + it('should call runIonicScript if hasIonicScript is true', function(done) { + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(true)); + spyOn(npmScripts, 'runIonicScript').andReturn(Q(true)); + + run.run(null, argv, rawCliArguments).then(function() { + expect(npmScripts.hasIonicScript).toHaveBeenCalledWith('build'); + expect(npmScripts.runIonicScript).toHaveBeenCalledWith('build'); + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['run', 'android'], false, true); + done(); + }).catch(function(e) { + console.log(e); + }); + }); + }); }); diff --git a/spec/tasks/serve.spec.js b/spec/tasks/serve.spec.js index ebbb91a980..e3f53220eb 100644 --- a/spec/tasks/serve.spec.js +++ b/spec/tasks/serve.spec.js @@ -9,6 +9,7 @@ var IonicProject = IonicAppLib.project; var Serve = IonicAppLib.serve; var log = IonicAppLib.logging.logger; var appLibUtils = IonicAppLib.utils; +var npmScripts = require('../../lib/utils/npmScripts'); describe('Serve', function() { @@ -66,15 +67,17 @@ describe('Serve', function() { var error = new Error('some error'); + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(false)); + spyOn(npmScripts, 'runIonicScript'); spyOn(IonicProject, 'load').andCallFake(function() { throw error; }); - spyOn(appLibUtils, 'fail'); - serveTask.run({}, argv); - expect(log.error).toHaveBeenCalledWith(jasmine.any(String), error); - expect(appLibUtils.fail).toHaveBeenCalledWith(error.message); - done(); + serveTask.run({}, argv).then(function() { + expect(log.error).toHaveBeenCalledWith(jasmine.any(String), error); + expect(npmScripts.runIonicScript).not.toHaveBeenCalled(); + done(); + }); }); it('should fail if loading the ionic project fails', function(done) { @@ -83,6 +86,9 @@ describe('Serve', function() { var argv = optimist(rawCliArguments).argv; var error = new Error('some error'); + + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(false)); + spyOn(npmScripts, 'runIonicScript'); spyOn(IonicProject, 'load').andReturn({ get: function() { return false; @@ -93,19 +99,22 @@ describe('Serve', function() { throw error; }); - spyOn(appLibUtils, 'fail'); - - serveTask.run({}, argv); - expect(appLibUtils.fail).toHaveBeenCalledWith('Error with serve- ' + error); - done(); + serveTask.run({}, argv).then(function() { + expect(log.error).toHaveBeenCalledWith(jasmine.any(String), error); + expect(npmScripts.runIonicScript).not.toHaveBeenCalled(); + done(); + }); }); - it('should return a rejected promise when chain fails', function(done) { + it('should call appLibUtils when chain fails', function(done) { var processArguments = ['node', 'ionic', 'serve', '-a']; var rawCliArguments = processArguments.slice(2); var argv = optimist(rawCliArguments).argv; var error = new Error('some error'); + + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(false)); + spyOn(npmScripts, 'runIonicScript'); spyOn(IonicProject, 'load').andReturn({ get: function() { return false; @@ -118,13 +127,13 @@ describe('Serve', function() { spyOn(Serve, 'start'); spyOn(Serve, 'showFinishedServeMessage'); - serveTask.run({}, argv).catch(function(errCaught) { + serveTask.run({}, argv).then(function() { expect(Serve.checkPorts).toHaveBeenCalled(); expect(Serve.start).not.toHaveBeenCalled(); expect(Serve.showFinishedServeMessage).not.toHaveBeenCalled(); + expect(npmScripts.runIonicScript).not.toHaveBeenCalled(); - expect(log.info).toHaveBeenCalledWith('There was an error serving your Ionic application:', error); - expect(errCaught).toEqual(error); + expect(log.error).toHaveBeenCalledWith(jasmine.any(String), error); done(); }); }); @@ -134,6 +143,8 @@ describe('Serve', function() { var rawCliArguments = processArguments.slice(2); var argv = optimist(rawCliArguments).argv; + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(false)); + spyOn(npmScripts, 'runIonicScript'); spyOn(IonicProject, 'load').andReturn({ get: function() { return false; @@ -147,6 +158,7 @@ describe('Serve', function() { spyOn(Serve, 'showFinishedServeMessage'); serveTask.run({}, argv).then(function() { + expect(npmScripts.runIonicScript).not.toHaveBeenCalled(); expect(Serve.getAddress).not.toHaveBeenCalled(); expect(Serve.start.calls[0].args[0].address).toBe('0.0.0.0'); done(); @@ -158,6 +170,8 @@ describe('Serve', function() { var rawCliArguments = processArguments.slice(2); var argv = optimist(rawCliArguments).argv; + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(false)); + spyOn(npmScripts, 'runIonicScript'); spyOn(IonicProject, 'load').andReturn({ get: function() { return false; @@ -171,6 +185,7 @@ describe('Serve', function() { spyOn(Serve, 'showFinishedServeMessage'); serveTask.run({}, argv).then(function() { + expect(npmScripts.runIonicScript).not.toHaveBeenCalled(); expect(Serve.getAddress).not.toHaveBeenCalled(); expect(Serve.start.calls[0].args[0].address).toBe('0.0.0.0'); done(); @@ -182,6 +197,8 @@ describe('Serve', function() { var rawCliArguments = processArguments.slice(2); var argv = optimist(rawCliArguments).argv; + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(false)); + spyOn(npmScripts, 'runIonicScript'); spyOn(IonicProject, 'load').andReturn({ get: function() { return false; @@ -195,6 +212,7 @@ describe('Serve', function() { spyOn(Serve, 'showFinishedServeMessage'); serveTask.run({}, argv).then(function() { + expect(npmScripts.runIonicScript).not.toHaveBeenCalled(); expect(Serve.getAddress).not.toHaveBeenCalled(); expect(Serve.start.calls[0].args[0].address).toBe('192.168.1.10'); done(); @@ -206,6 +224,8 @@ describe('Serve', function() { var rawCliArguments = processArguments.slice(2); var argv = optimist(rawCliArguments).argv; + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(false)); + spyOn(npmScripts, 'runIonicScript'); spyOn(IonicProject, 'load').andReturn({}); spyOn(Serve, 'loadSettings').andReturn({}); spyOn(Serve, 'getAddress').andReturn(Q()); @@ -223,6 +243,7 @@ describe('Serve', function() { }; serveTask.run({}, argv).then(function() { + expect(npmScripts.runIonicScript).not.toHaveBeenCalled(); expect(Serve.start).toHaveBeenCalledWith(options); done(); }); @@ -233,6 +254,8 @@ describe('Serve', function() { var rawCliArguments = processArguments.slice(2); var argv = optimist(rawCliArguments).argv; + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(false)); + spyOn(npmScripts, 'runIonicScript'); spyOn(IonicProject, 'load').andReturn({ get: function() { return false; @@ -246,9 +269,25 @@ describe('Serve', function() { spyOn(Serve, 'showFinishedServeMessage'); serveTask.run({}, argv).then(function() { + expect(npmScripts.runIonicScript).not.toHaveBeenCalled(); expect(Serve.start.calls[0].args[0].liveReloadPort).toBe(35729); done(); }); }); + + it('should call runIonicScript if hasIonicScript is true', function(done) { + var processArguments = ['node', 'ionic', 'serve', '--livereload', '--nogulp']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + + spyOn(npmScripts, 'hasIonicScript').andReturn(Q(true)); + spyOn(npmScripts, 'runIonicScript').andReturn(Q(true)); + + serveTask.run({}, argv).then(function() { + expect(npmScripts.hasIonicScript).toHaveBeenCalledWith('serve'); + expect(npmScripts.runIonicScript).toHaveBeenCalledWith('serve', argv); + done(); + }); + }); }); }); From 5544cc8f286a9331f4440eee0b5aff73e5ed4a78 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Tue, 18 Oct 2016 10:50:19 -0500 Subject: [PATCH 1062/1100] chore(): increment package.json version. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4459402861..e48a39b347 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.1.1", + "version": "2.1.2", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 9c128e0b49261969e6881d9146cfaaeab2c0c305 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Fri, 21 Oct 2016 08:33:24 -0500 Subject: [PATCH 1063/1100] chore(): create tests for npmscripts util. --- lib/utils/npmScripts.js | 10 +++-- spec/fixtures/package.json | 53 +++++++++++++++++++++++++ spec/utils/npmScripts.spec.js | 73 +++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 spec/fixtures/package.json create mode 100644 spec/utils/npmScripts.spec.js diff --git a/lib/utils/npmScripts.js b/lib/utils/npmScripts.js index 4315c673fe..e538e5dd7c 100644 --- a/lib/utils/npmScripts.js +++ b/lib/utils/npmScripts.js @@ -6,17 +6,21 @@ var fs = require('fs'); var IonicAppLib = require('ionic-app-lib'); var log = IonicAppLib.logging.logger; +function getScriptName(name) { + return 'ionic:' + name; +} + function hasIonicScript(name) { return getPackageJsonContents().then(function(packageJsonContents) { return packageJsonContents.hasOwnProperty('scripts') && - packageJsonContents.scripts.hasOwnProperty('ionic:' + name); + packageJsonContents.scripts.hasOwnProperty(getScriptName(name)); }); } function runIonicScript(name, argv) { var spawn = require('cross-spawn-async'); - var scriptName = 'ionic:' + name; + var scriptName = getScriptName(name); var q = Q.defer(); var scriptSpawn = spawn('npm', ['run', scriptName].concat(argv || []), { stdio: 'inherit' }) @@ -48,7 +52,7 @@ var getPackageJsonContents = (function() { var q = Q.defer(); if (packageJson) { - Q.resolve(packageJson); + return Q.resolve(packageJson); } try { diff --git a/spec/fixtures/package.json b/spec/fixtures/package.json new file mode 100644 index 0000000000..6ddf931184 --- /dev/null +++ b/spec/fixtures/package.json @@ -0,0 +1,53 @@ +{ + "name": "ionic-hello-world", + "author": "Ionic Framework", + "homepage": "http://ionicframework.com/", + "scripts": { + "ionic:build": "ionic-app-scripts build", + "watch": "ionic-app-scripts watch", + "ionic:test": "dir", + "serve:before": "watch", + "emulate:before": "build", + "deploy:before": "build", + "build:before": "build", + "run:before": "build" + }, + "dependencies": { + "@angular/common": "^2.0.0", + "@angular/compiler": "^2.0.0", + "@angular/compiler-cli": "0.6.2", + "@angular/core": "^2.0.0", + "@angular/forms": "^2.0.0", + "@angular/http": "^2.0.0", + "@angular/platform-browser": "^2.0.0", + "@angular/platform-browser-dynamic": "^2.0.0", + "@angular/platform-server": "^2.0.0", + "@ionic/storage": "^1.0.3", + "ionic-angular": "^2.0.0-rc.1", + "ionic-native": "^2.2.3", + "ionicons": "^3.0.0", + "rxjs": "5.0.0-beta.12", + "zone.js": "^0.6.21" + }, + "devDependencies": { + "@ionic/app-scripts": "^0.0.36", + "typescript": "^2.0.3" + }, + "cordovaPlugins": [ + "cordova-plugin-whitelist", + "cordova-plugin-statusbar", + "cordova-plugin-console", + "cordova-plugin-device", + "cordova-plugin-splashscreen", + "ionic-plugin-keyboard" + ], + "cordovaPlatforms": [ + "ios", + { + "platform": "ios", + "version": "", + "locator": "ios" + } + ], + "description": "hi: An Ionic project" +} \ No newline at end of file diff --git a/spec/utils/npmScripts.spec.js b/spec/utils/npmScripts.spec.js new file mode 100644 index 0000000000..ca1a84fec0 --- /dev/null +++ b/spec/utils/npmScripts.spec.js @@ -0,0 +1,73 @@ +'use strict'; + +var rewire = require('rewire'); +var path = require('path'); +var fs = require('fs'); +var npmScripts = rewire('../../lib/utils/npmScripts'); +var EventEmitter = require('events'); +var Q = require('q'); +var spawn = require('cross-spawn-async'); + +describe('hasIonicScript function', function() { + + it('should return false if script does not exist', function(done) { + var jsonContent = require(path.join(__dirname, '../', 'fixtures/', 'package.json')); + var getPackageJsonContentsSpy = jasmine.createSpy('getPackageJsonContentsSpy').andReturn(Q(jsonContent)); + var revert = npmScripts.__set__('getPackageJsonContents', getPackageJsonContentsSpy); + spyOn(npmScripts, 'getPackageJsonContents').andReturn(Q(jsonContent)); + + npmScripts.hasIonicScript('stuff').then(function(results) { + expect(results).toEqual(false); + done(); + revert(); + }); + }); + it('should return true if script does not exist', function(done) { + var jsonContent = require(path.join(__dirname, '../', 'fixtures/', 'package.json')); + var getPackageJsonContentsSpy = jasmine.createSpy('getPackageJsonContentsSpy').andReturn(Q(jsonContent)); + var revert = npmScripts.__set__('getPackageJsonContents', getPackageJsonContentsSpy); + spyOn(npmScripts, 'getPackageJsonContents').andReturn(Q(jsonContent)); + + npmScripts.hasIonicScript('build').then(function(results) { + expect(results).toEqual(true); + done(); + revert(); + }); + }); +}); +/* +describe('runIonicScript function', function() { + it('should call spawn', function(done) { + //'npm', ['run', scriptName].concat(argv || []), { stdio: 'inherit' } + var emitter = new EventEmitter(); + var error = new Error(); + + spawn = jasmine.createSpy('spawnSpy', spawn).andCallFake(function() { + return emitter; + }); + + npmScripts.runIonicScript('test').catch(function(err) { + expect(err).toEqual(error); + done(); + }); + emitter.emit('error', error); + }); +}); +*/ +describe('getPackageJsonContents method', function() { + it('getPackageJsonContents should return json contents of package.json file and should memoize', function(done) { + var dapath = path.join(__dirname, '../', 'fixtures/package.json'); + spyOn(path, 'resolve').andReturn(dapath); + spyOn(fs, 'readFile').andCallThrough(); + + npmScripts.getPackageJsonContents().then(function(contents) { + expect(contents).toEqual(require(dapath)); + + npmScripts.getPackageJsonContents().then(function(secondContents) { + expect(secondContents).toEqual(require(dapath)); + expect(fs.readFile.calls.length).toEqual(1); + done(); + }); + }); + }); +}); \ No newline at end of file From 5565e5dc2bf20f90e5be128f347a0ef285e9ee84 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Fri, 21 Oct 2016 10:35:04 -0500 Subject: [PATCH 1064/1100] chore(): increment ionic-app-lib dependency to 2.1.2 to get latests fixes. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index e48a39b347..83cb61a550 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.1.2", + "version": "2.1.3", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -69,7 +69,7 @@ "gulp": "3.8.8", "gulp-util": "3.0.7", "inquirer": "0.11.2", - "ionic-app-lib": "2.1.1", + "ionic-app-lib": "2.1.2", "moment": "2.11.1", "open": "0.0.5", "optimist": "0.6.0", From a1f7bb55442ca1402d5901de45eb737aea7aa726 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Fri, 21 Oct 2016 10:37:16 -0500 Subject: [PATCH 1065/1100] chore(): update changelog. --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04c31f108f..8f42663408 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ + +## [2.1.2](https://github.com/driftyco/ionic-cli/compare/v2.1.1...v2.1.2) (2016-10-21) + +### Bug Fixes + +chore(): increment ionic-app-lib dependency to 2.1.2 to get latests fixes. ([5565e5d](https://github.com/driftyco/ionic-cli/commit/5565e5d)) + + ## [2.1.1](https://github.com/driftyco/ionic-cli/compare/v2.1.0...v2.1.1) (2016-10-14) From a56d792d797e8d73d9e3646d91c35a4473bdf2f5 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Fri, 21 Oct 2016 10:39:15 -0500 Subject: [PATCH 1066/1100] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f42663408..6117fd04ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ### Bug Fixes -chore(): increment ionic-app-lib dependency to 2.1.2 to get latests fixes. ([5565e5d](https://github.com/driftyco/ionic-cli/commit/5565e5d)) +* increment ionic-app-lib dependency to 2.1.2 to get latests fixes. ([5565e5d](https://github.com/driftyco/ionic-cli/commit/5565e5d)) From 4b4b77f8ffe6b8e6ffb315e09f9d8590f61cfbae Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Fri, 21 Oct 2016 11:11:00 -0500 Subject: [PATCH 1067/1100] chore(): update changelog for 2.1.2 and 2.1.3. --- CHANGELOG.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6117fd04ba..31bec396d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,21 @@ - -## [2.1.2](https://github.com/driftyco/ionic-cli/compare/v2.1.1...v2.1.2) (2016-10-21) + +## [2.1.3](https://github.com/driftyco/ionic-cli/compare/v2.1.2...v2.1.3) (2016-10-21) + ### Bug Fixes * increment ionic-app-lib dependency to 2.1.2 to get latests fixes. ([5565e5d](https://github.com/driftyco/ionic-cli/commit/5565e5d)) + +## [2.1.2](https://github.com/driftyco/ionic-cli/compare/v2.1.1...v2.1.2) (2016-10-18) + + +### Bug Fixes + +* use an environment variable to force colors instead of using the --colors flag. ([#1557](https://github.com/driftyco/ionic-cli/issues/1557)) ([cdbeef6](https://github.com/driftyco/ionic-cli/commit/cdbeef6)) + + ## [2.1.1](https://github.com/driftyco/ionic-cli/compare/v2.1.0...v2.1.1) (2016-10-14) From 2a50608ada56fab24cfe611ca50a4b6b0ac71f5e Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Fri, 21 Oct 2016 11:26:07 -0500 Subject: [PATCH 1068/1100] fix(): rerelease to npm because ionic-app-lib bundled dependency was not updated. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 83cb61a550..3eeda02abb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.1.3", + "version": "2.1.4", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 58cfd8a5c091665be51fcdc5d4b0c8a5acbb39f6 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Tue, 25 Oct 2016 13:39:20 -0500 Subject: [PATCH 1069/1100] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31bec396d5..1eb17d536f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ - -## [2.1.3](https://github.com/driftyco/ionic-cli/compare/v2.1.2...v2.1.3) (2016-10-21) + +## [2.1.4](https://github.com/driftyco/ionic-cli/compare/v2.1.2...v2.1.4) (2016-10-21) ### Bug Fixes From 26486d2449740aa4d6083f74b1ae364290c72fd6 Mon Sep 17 00:00:00 2001 From: Vyacheslav Chub Date: Fri, 28 Oct 2016 04:23:44 +0300 Subject: [PATCH 1070/1100] fix(cli): npm install under windows (#1539) Closes #1445 --- lib/cli.js | 7 ++++--- lib/utils/help.js | 7 ++++--- spec/utils/templates.spec.js | 3 ++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index 93d97c3241..10873abdca 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -18,6 +18,7 @@ var Logging = IonicAppLib.logging; var log = Logging.logger; var Q = require('q'); var helpUtil = require('./utils/help'); +var EOL = require('os').EOL; Cli.ALL_TASKS = Tasks; Cli.IONIC_DASH = 'https://apps.ionic.io'; @@ -268,7 +269,7 @@ Cli.loadGulpfile = function loadGulpfile() { var names = ['gulpfile.js', 'Gulpfile.js']; for (var i = 0, ii = names.length; i < ii; i += 1) { try { - require(path.resolve(process.cwd() + '/' + names[i])); + require(path.resolve(process.cwd(), names[i])); log.verbose('Gulpfile found'); return true; } catch (e) { @@ -331,7 +332,7 @@ Cli.runNpmHook = function runNpmHook(hook) { }); spawned.stdout.pipe(process.stdout); spawned.stdout.on('data', function(data) { - var dataLines = data.toString().split('\n'); + var dataLines = data.toString().split(EOL); for (var i = 0; i < dataLines.length; i++) { if (dataLines[i].length) { if (dataLines[i].indexOf('watch ready') > -1) { @@ -360,7 +361,7 @@ Cli.loadNpmScripts = function loadNpmScripts() { var fileName = 'package.json'; try { - var packageFile = require(path.resolve(process.cwd() + '/' + fileName)); + var packageFile = require(path.resolve(process.cwd(), fileName)); log.verbose('Package.json found scripts:', packageFile.scripts); return packageFile.scripts; } catch (e) { diff --git a/lib/utils/help.js b/lib/utils/help.js index 144adbdbc2..2aebb576f7 100644 --- a/lib/utils/help.js +++ b/lib/utils/help.js @@ -2,6 +2,7 @@ var IonicAppLib = require('ionic-app-lib'); var log = IonicAppLib.logging.logger; +var EOL = require('os').EOL; require('colors'); /** @@ -173,7 +174,7 @@ function printTaskDetails(d) { } w((indent + ' ' + arg + ' ').bold); - var argDescs = d.args[arg].split('\n'); + var argDescs = d.args[arg].split(EOL); var argIndent = indent + ' '; for (x = 0; x < arg.length + 1; x += 1) { @@ -218,9 +219,9 @@ function printTaskDetails(d) { var optDescs; if (typeof taskOpt == 'string') { - optDescs = taskOpt.split('\n'); + optDescs = taskOpt.split(EOL); } else { - optDescs = taskOpt.title.split('\n'); + optDescs = taskOpt.title.split(EOL); } for (x = 0; x < optDescs.length; x += 1) { if (x === 0) { diff --git a/spec/utils/templates.spec.js b/spec/utils/templates.spec.js index 0e75efd953..ae37dbb929 100644 --- a/spec/utils/templates.spec.js +++ b/spec/utils/templates.spec.js @@ -5,6 +5,7 @@ var rewire = require('rewire'); var templateUtils = rewire('../../lib/utils/templates'); var IonicAppLib = require('ionic-app-lib'); var log = IonicAppLib.logging.logger; +var EOL = require('os').EOL; describe('listTemplates method', function() { it('should should call fetchStarterTemplates pull out templates/sort and send to list', function(done) { @@ -148,7 +149,7 @@ describe('list function', function() { var list = templateUtils.__get__('list'); list(templates); - expect(log.info.calls[0].args).toEqual(['\n']); + expect(log.info.calls[0].args).toEqual([EOL]); expect(log.info.calls[1].args[0]).toMatch('a-template'); // Use match because of colors expect(log.info.calls[1].args[1]).toEqual('...........'); expect(log.info.calls[1].args[2]).toEqual(templates[0].description); From 690a447fbf87a592cae53551c25f23e2d9dd21c9 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Fri, 28 Oct 2016 13:42:10 -0500 Subject: [PATCH 1071/1100] chore(): replace colors module with chalk and update all usages. (#1605) --- .eslintrc.js | 4 +++ lib/cli.js | 70 ++++++++++++++++++------------------------ lib/ionic/add.js | 11 +++---- lib/ionic/config.js | 4 ++- lib/ionic/docs.js | 7 +++-- lib/ionic/emulate.js | 3 +- lib/ionic/generate.js | 48 ----------------------------- lib/ionic/help.js | 3 +- lib/ionic/io.js | 3 +- lib/ionic/lib.js | 33 ++++++++++---------- lib/ionic/link.js | 3 +- lib/ionic/list.js | 11 +++---- lib/ionic/login.js | 11 ++++--- lib/ionic/package.js | 40 ++++++++++++------------ lib/ionic/push.js | 37 +++++++++++----------- lib/ionic/remove.js | 11 ++++--- lib/ionic/resources.js | 3 +- lib/ionic/run.js | 3 +- lib/ionic/security.js | 27 ++++++++-------- lib/ionic/serve.js | 1 - lib/ionic/service.js | 17 +++++----- lib/ionic/setup.js | 17 +++++----- lib/ionic/share.js | 4 ++- lib/ionic/start.js | 4 +-- lib/utils/bower.js | 5 ++- lib/utils/cordova.js | 9 +++--- lib/utils/help.js | 28 ++++++++--------- lib/utils/ionitron.js | 4 +-- lib/utils/templates.js | 10 +++--- package.json | 7 +++-- 30 files changed, 197 insertions(+), 241 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 0905e44435..ffa1706b6f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -3,8 +3,12 @@ module.exports = { node: true, commonjs: true }, + plugins: [ + 'no-use-extend-native' + ], extends: 'eslint:recommended', rules: { + 'no-use-extend-native/no-use-extend-native': 2, eqeqeq: [ "error", "smart" diff --git a/lib/cli.js b/lib/cli.js index 10873abdca..1dbf2dcfea 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -1,5 +1,3 @@ -require('colors'); - var Cli = {}; var IonicAppLib = require('ionic-app-lib'); var IonicStats = require('./utils/stats'); @@ -19,6 +17,7 @@ var log = Logging.logger; var Q = require('q'); var helpUtil = require('./utils/help'); var EOL = require('os').EOL; +var chalk = require('chalk'); Cli.ALL_TASKS = Tasks; Cli.IONIC_DASH = 'https://apps.ionic.io'; @@ -154,7 +153,7 @@ Cli.run = function run(processArgv) { // For v2, print a warning as this is most likely not what they want if (!fs.existsSync('node_modules')) { if (argv.v2) { - log.warn('WARN: No node_modules directory found, do you need to run npm install?'.yellow); + log.warn(chalk.yellow('WARN: No node_modules directory found, do you need to run npm install?')); } log.debug('node_modules directory not found, not running gulp hooks'); @@ -173,7 +172,7 @@ Cli.run = function run(processArgv) { log.info('\nDo you need to run `npm install`?\n'); process.exit(1); } - log.error('\nThere is an error in your gulpfile: '.red); + log.error(chalk.red('\nThere is an error in your gulpfile: ')); log.error(e.stack + '\n'); process.exit(1); } @@ -188,6 +187,7 @@ Cli.run = function run(processArgv) { } if (!gulpLoaded) { + // warn if no build file and it's a command that requires a build step // but still run the command with no hooks if (Cli.isBuildCommand(taskName) && argv.v2 && !npmScripts) { @@ -216,8 +216,8 @@ Cli.runWithGulp = function runWithGulp(argv, taskInstance, rawCliArguments) { } catch (e) { // Empty gulpfile (or one that doesn't require gulp?), and no gulp - log.error('\nGulpfile detected, but gulp is not installed'.red); - log.error('Do you need to run `npm install`?\n'.red); + log.error(chalk.red('\nGulpfile detected, but gulp is not installed')); + log.error(chalk.red('Do you need to run `npm install`?\n')); return process.exit(1); } @@ -231,9 +231,9 @@ Cli.runWithGulp = function runWithGulp(argv, taskInstance, rawCliArguments) { beforeHookPromise = Cli.runGulpHook(gulp, beforeHook); } else if (Cli.isBuildCommand(cmdName) && argv.v2) { - log.warn(('WARN: No \'' + beforeHook + '\' gulp task found!').yellow); - log.warn(('If your app requires a build step, you may want to ensure it runs before ' + - cmdName + '.\n').yellow); + log.warn(chalk.yellow('WARN: No \'' + beforeHook + '\' gulp task found!')); + log.warn(chalk.yellow('If your app requires a build step, you may want to ensure it runs before ' + + cmdName + '.\n')); } // run beforeHook @@ -333,7 +333,7 @@ Cli.runNpmHook = function runNpmHook(hook) { spawned.stdout.pipe(process.stdout); spawned.stdout.on('data', function(data) { var dataLines = data.toString().split(EOL); - for (var i = 0; i < dataLines.length; i++) { + for (var i = 0; i < dataLines.length; i += 1) { if (dataLines[i].length) { if (dataLines[i].indexOf('watch ready') > -1) { return q.resolve(); @@ -348,7 +348,7 @@ Cli.runNpmHook = function runNpmHook(hook) { } return q.resolve(); }); - + // If this process ends ensure that we killed the spawned child process.on('exit', function() { spawned.kill(); @@ -367,8 +367,6 @@ Cli.loadNpmScripts = function loadNpmScripts() { } catch (e) { throw e; } - - return undefined; }; Cli.logEvents = function logEvents(gulpInst, finalTaskNames) { @@ -379,14 +377,14 @@ Cli.logEvents = function logEvents(gulpInst, finalTaskNames) { // TODO: batch these // so when 5 tasks start at once it only logs one time with all 5 - gutil.log('Starting', '\'' + e.task.cyan + '\'...'); + gutil.log('Starting', '\'' + chalk.cyan(e.task) + '\'...'); }); gulpInst.on('task_stop', function(e) { var time = prettyTime(e.hrDuration); gutil.log( - 'Finished', '\'' + e.task.cyan + '\'', - 'after', time.magenta + 'Finished', '\'' + chalk.cyan(e.task) + '\'', + 'after', chalk.magenta(time) ); if (finalTaskNames.indexOf(e.task) > -1) { log.info(); @@ -397,20 +395,12 @@ Cli.logEvents = function logEvents(gulpInst, finalTaskNames) { var msg = Cli.formatGulpError(e); var time = prettyTime(e.hrDuration); gutil.log( - '\'' + e.task.cyan + '\'', - 'errored after'.red, - time.magenta + '\'' + chalk.cyan(e.task) + '\'', + chalk.red('errored after'), + chalk.magenta(time) ); gutil.log(msg); }); - - // gulpInst.on('task_not_found', function(err) { - // gutil.log( - // ('Task \'' + err.task + '\' is not in your gulpfile').red - // ); - // gutil.log('Please check the documentation for proper gulpfile formatting'); - // process.exit(1); - // }); }; // Format orchestrator errors @@ -472,13 +462,13 @@ Cli.printVersionWarning = function printVersionWarning(version, npmVersion) { } if (npmVersion && npmVersion !== version.trim()) { - log.warn('\n------------------------------------\n'.red); - log.warn('Ionic CLI is out of date:\n'.bold.yellow); - log.warn((' * Locally installed version: ' + version + '\n').yellow); - log.warn((' * Latest version: ' + npmVersion + '\n').yellow); - log.warn((' * https://github.com/driftyco/ionic-cli/blob/master/CHANGELOG.md\n').yellow); - log.warn(' * Run '.yellow + 'npm install -g ionic'.bold + ' to update\n'.yellow); - log.warn('------------------------------------\n\n'.red); + log.warn(chalk.red('\n------------------------------------\n')); + log.warn(chalk.yellow.bold('Ionic CLI is out of date:\n')); + log.warn(chalk.yellow(' * Locally installed version: ' + version + '\n')); + log.warn(chalk.yellow(' * Latest version: ' + npmVersion + '\n')); + log.warn(chalk.yellow(' * https://github.com/driftyco/ionic-cli/blob/master/CHANGELOG.md\n')); + log.warn(chalk.yellow(' * Run ') + chalk.bold('npm install -g ionic') + chalk.yellow(' to update\n')); + log.warn(chalk.red('------------------------------------\n\n')); } }; @@ -614,10 +604,10 @@ Cli.printNewsUpdates = function printNewsUpdates(skipNewsCheck) { for (var i = 0, j = messagesJson.list.length; i < j; i += 1) { var entry = messagesJson.list[i]; - var entryMessage = ['+ ', entry.name, '\n', '+ ', entry.action.blue.bold, '\n+\n'].join(''); + var entryMessage = ['+ ', entry.name, '\n', '+ ', chalk.blue.bold(entry.action), '\n+\n'].join(''); log.info(entryMessage); } - log.info('+---------------------------------------------------------+\n'.green); + log.info(chalk.green('+---------------------------------------------------------+\n')); } catch (ex) { q.reject('Error occurred in downloading the CLI messages:', ex); appLibUtils.fail(ex); @@ -639,7 +629,7 @@ Cli.gatherInfo = function gatherInfo() { }; Cli.handleUncaughtExceptions = function handleUncaughtExceptions(err) { - log.error('An uncaught exception occurred and has been reported to Ionic'.red.bold); + log.error(chalk.red.bold('An uncaught exception occurred and has been reported to Ionic')); var errorMessage = typeof err === 'string' ? err : err.message; appLibUtils.errorHandler(errorMessage); process.exit(1); @@ -655,10 +645,10 @@ Cli.attachErrorHandling = function attachErrorHandling() { var info = Cli.gatherInfo(); var ionicCliVersion = info.ionic_cli; if (stack && stack.length > 0) { - process.stderr.write('\n' + stack.bold + '\n\n'); + process.stderr.write('\n' + chalk.bold(stack) + '\n\n'); } - process.stderr.write('\n' + errorMessage.bold); - process.stderr.write((' (CLI v' + ionicCliVersion + ')').bold + '\n'); + process.stderr.write('\n' + chalk.bold(errorMessage)); + process.stderr.write(chalk.bold(' (CLI v' + ionicCliVersion + ')') + '\n'); Info.printInfo(info); } diff --git a/lib/ionic/add.js b/lib/ionic/add.js index a447f5f952..d05168a978 100644 --- a/lib/ionic/add.js +++ b/lib/ionic/add.js @@ -1,7 +1,6 @@ 'use strict'; -require('colors'); - +var chalk = require('chalk'); var extend = require('../utils/extend'); var childProcess = require('child_process'); var IonicAppLib = require('ionic-app-lib'); @@ -31,8 +30,8 @@ function installBowerComponent(componentName) { } catch (e) {} // eslint-disable-line no-empty // Error happened, report it. - var errorMessage = 'Bower error, check that "'.red.bold + componentName + '"'.red.bold + - ' exists,'.red.bold + '\nor try running "'.red.bold + bowerInstallCommand + '" for more info.'.red.bold; + var errorMessage = chalk.red.bold('Bower error, check that "') + componentName + + chalk.red.bold('" exists,\nor try running "') + bowerInstallCommand + chalk.red.bold('" for more info.'); appLibUtils.fail(errorMessage, 'add'); } @@ -45,10 +44,10 @@ function installBowerComponent(componentName) { function run(ionic, argv) { // This command will be deprecated in the future. - var deprecationMsg = 'This command has been ' + 'deprecated'.red + '. All ' + + var deprecationMsg = 'This command has been ' + chalk.red('deprecated') + '. All ' + 'resources are currently available in NPM and we recommend that you use NPM to manage these.\n' + 'More information is available here: https://github.com/driftyco/ionic-cli/wiki/Migrating-to-NPM-from-bower\n'; - log.info(deprecationMsg.bold); + log.info(chalk.bold(deprecationMsg)); if (!bower.checkForBower()) { appLibUtils.fail(bower.installMessage, 'add'); diff --git a/lib/ionic/config.js b/lib/ionic/config.js index 18efd17be4..a39d750b67 100644 --- a/lib/ionic/config.js +++ b/lib/ionic/config.js @@ -1,5 +1,6 @@ 'use strict'; +var chalk = require('chalk'); var extend = require('../utils/extend'); var IonicAppLib = require('ionic-app-lib'); var ioLib = IonicAppLib.ioConfig; @@ -11,7 +12,8 @@ var settings = { name: 'config', summary: 'Set configuration variables for your ionic app', args: { - '': 'set'.yellow + ', ' + 'unset'.yellow + ', ' + 'build'.yellow + ', or ' + 'info'.yellow, + '': chalk.yellow('set') + ', ' + + chalk.yellow('unset') + ', ' + chalk.yellow('build') + ', or ' + chalk.yellow('info'), '[key]': 'The key to set', '[value]': 'The value to set' }, diff --git a/lib/ionic/docs.js b/lib/ionic/docs.js index 374401ed02..4885020cf0 100644 --- a/lib/ionic/docs.js +++ b/lib/ionic/docs.js @@ -1,5 +1,6 @@ 'use strict'; +var chalk = require('chalk'); var prompt = require('prompt'); var _ = require('underscore'); var extend = require('../utils/extend'); @@ -28,7 +29,7 @@ function list() { var docsList = IonicDocs.api; // var i = 0; - log.info('Help Topics'.yellow); + log.info(chalk.yellow('Help Topics')); _.each(docsList, function(doc) { var table = new Table({ head: [doc.id] }); var count = 0; @@ -50,7 +51,7 @@ function list() { } catch (ex) { log.error('Error listing docs:', ex); } - log.info('Type "ionic docs " to open the help document for that doc.'.blue.bold); + log.info(chalk.blue.bold('Type "ionic docs " to open the help document for that doc.')); } function openDefault() { @@ -150,7 +151,7 @@ function lookUpCommand(helpDoc) { function openDoc(topic, doc) { var url = sanitizeUrl('http://ionicframework.com/docs/api/' + topic + '/' + doc); - log.info('Opening Ionic document:'.green.bold, url.green.bold); + log.info(chalk.green.bold('Opening Ionic document:'), chalk.green.bold(url)); return require('open')(url); } } diff --git a/lib/ionic/emulate.js b/lib/ionic/emulate.js index 6c11474fce..714a824854 100644 --- a/lib/ionic/emulate.js +++ b/lib/ionic/emulate.js @@ -1,5 +1,6 @@ 'use strict'; +var chalk = require('chalk'); var extend = require('../utils/extend'); var npmScripts = require('../utils/npmScripts'); var os = require('os'); @@ -10,7 +11,7 @@ var log = IonicAppLib.logging.logger; var cordovaUtils = require('../utils/cordova'); var cordovaRunEmulateOptions = { - '--livereload|-l': 'Live reload app dev files from the device' + ' (beta)'.yellow, + '--livereload|-l': 'Live reload app dev files from the device' + chalk.yellow(' (beta)'), '--address': 'Use specific address (livereload req.)', '--port|-p': 'Dev server HTTP port (8100 default, livereload req.)', '--livereload-port|-r': 'Live Reload port (35729 default, livereload req.)', diff --git a/lib/ionic/generate.js b/lib/ionic/generate.js index dc288d8789..f7e616b671 100644 --- a/lib/ionic/generate.js +++ b/lib/ionic/generate.js @@ -5,7 +5,6 @@ var path = require('path'); var ionicAppLib = require('ionic-app-lib'); var appGenerator = require('@ionic/app-generators'); var log = ionicAppLib.logging.logger; -var Project = ionicAppLib.project; var fail = ionicAppLib.utils.fail; var settings = { @@ -34,16 +33,8 @@ function run(ionic, argv) { return fail('Generators are only available for Ionic 2 projects'); } - var project; - try { - project = Project.load(process.cwd()); - } catch (err) { - return fail(err); - } - var generator = argv._[1]; var name = argv._[2]; // TODO support multiple names - var isTS = project.get('typescript') || argv.ts; if (argv.list) { appGenerator.printAvailableGenerators(); @@ -117,45 +108,6 @@ function run(ionic, argv) { } } -function loadToolingModule() { - - // First try node_modules/ionic-angular/tooling - var toolingPath; - var ionicModule; - - try { - toolingPath = path.join(process.cwd(), 'node_modules', 'ionic-angular', 'tooling'); - ionicModule = require(toolingPath); - } catch (err) { - - // if this isn't found, that's fine, check for ionic-framework - if (err.code !== 'MODULE_NOT_FOUND') { - fail('Error when requiring ' + toolingPath + ':\n ' + err); - } - } - - // Then try node_modules/ionic-framework/tooling - if (!ionicModule) { - try { - ionicModule = require(path.join(process.cwd(), 'node_modules', 'ionic-framework', 'tooling')); - } catch (err) { - if (err.code === 'MODULE_NOT_FOUND') { - fail('No ionic-angular package found, do you have Ionic installed?'); - } - } - } - - // Last, try node_modules/ionic-framework (beta.1 and below) - if (!ionicModule) { - try { - ionicModule = require(path.join(process.cwd(), 'node_modules', 'ionic-framework')); - } catch (err) { - fail(err); - } - } - return ionicModule; -} - module.exports = extend(settings, { run: run }); diff --git a/lib/ionic/help.js b/lib/ionic/help.js index 1005ed78cd..d714a011d0 100644 --- a/lib/ionic/help.js +++ b/lib/ionic/help.js @@ -1,5 +1,6 @@ 'use strict'; +var chalk = require('chalk'); var extend = require('../utils/extend'); var helpUtil = require('../utils/help'); @@ -28,7 +29,7 @@ function run(ionic, argv) { return helpUtil.printTaskUsage(task, ionic.VERSION); } - return console.log('Command not found.'.red.bold); + return console.log(chalk.red.bold('Command not found.')); } module.exports = extend(settings, { diff --git a/lib/ionic/io.js b/lib/ionic/io.js index b5f51613fb..f5714b024c 100644 --- a/lib/ionic/io.js +++ b/lib/ionic/io.js @@ -1,5 +1,6 @@ 'use strict'; +var chalk = require('chalk'); var extend = require('../utils/extend'); var IonicAppLib = require('ionic-app-lib'); var ioLib = IonicAppLib.ioConfig; @@ -14,7 +15,7 @@ var settings = { name: 'io', summary: 'Integrate your app with Ionic Cloud services', args: { - '': 'init'.yellow + '': chalk.yellow('init') }, isProjectTask: true }; diff --git a/lib/ionic/lib.js b/lib/ionic/lib.js index a85cc8128c..1a4cdb0ffd 100644 --- a/lib/ionic/lib.js +++ b/lib/ionic/lib.js @@ -1,7 +1,6 @@ 'use strict'; -require('colors'); - +var chalk = require('chalk'); var extend = require('../utils/extend'); var fs = require('fs'); var path = require('path'); @@ -33,10 +32,10 @@ var settings = { function run(ionic, argv) { // This command will be deprecated in the future. - var deprecationMsg = 'This command has been ' + 'deprecated'.red + '. All ' + + var deprecationMsg = 'This command has been ' + chalk.red('deprecated') + '. All ' + 'resources are currently available in NPM and we recommend that you use NPM to manage these.\n' + 'More information is available here: https://github.com/driftyco/ionic-cli/wiki/Migrating-to-NPM-from-bower\n'; - log.info(deprecationMsg.bold); + log.info(chalk.bold(deprecationMsg)); if (!fs.existsSync(path.resolve('www'))) { return appLibUtils.fail('"www" directory cannot be found. Make sure the working directory ' + @@ -84,16 +83,16 @@ function loadVersionData() { function printLibVersions(data) { - log.info('Local Ionic version: '.bold.green + data.local.version + ' (' + data.versionFilePath + ')'); + log.info(chalk.bold.green('Local Ionic version: ') + data.local.version + ' (' + data.versionFilePath + ')'); getVersionData('latest').then(function(versionData) { - log.info('Latest Ionic version: '.bold.green + versionData.version_number + + log.info(chalk.bold.green('Latest Ionic version: ') + versionData.version_number + ' (released ' + versionData.release_date + ')'); if (data.local.version !== versionData.version_number) { - log.info(' * Local version is out of date'.yellow); + log.info(chalk.yellow(' * Local version is out of date')); } else { - log.info(' * Local version up to date'.green); + log.info(chalk.green(' * Local version up to date')); } }); } @@ -134,7 +133,7 @@ function getVersionData(data, version) { q.resolve(versionData); } catch (e) { - log.error('Error loading ' + version.bold + ' version information'); + log.error('Error loading ' + chalk.bold(version) + ' version information'); q.reject(); } }); @@ -154,7 +153,7 @@ function updateLibVersion(data) { bowerCmd.stderr.on('data', function(data) { if (data) { - process.stderr.write(data.toString().red.bold); + process.stderr.write(chalk.red.bold(data.toString())); } }); @@ -167,13 +166,13 @@ function updateLibVersion(data) { var libPath = path.resolve('www/lib/ionic/'); - log.info('Are you sure you want to replace '.green.bold + libPath.bold + - ' with an updated version of Ionic?'.green.bold); + log.info(chalk.green.bold('Are you sure you want to replace ') + chalk.bold(libPath) + + chalk.green.bold(' with an updated version of Ionic?')); var promptProperties = { areYouSure: { name: 'areYouSure', - description: '(yes/no):'.yellow.bold, + description: chalk.yellow.bold('(yes/no):'), required: true } }; @@ -209,10 +208,10 @@ function getLatest() { getVersionData(version).then(function(versionData) { if (version === 'latest') { - log.info('Latest version: '.bold.green + versionData.version_number + + log.info(chalk.bold.green('Latest version: ') + versionData.version_number + ' (released ' + versionData.release_date + ')'); } else { - log.info('Version: '.bold.green + versionData.version_number + + log.info(chalk.bold.green('Version: ') + versionData.version_number + ' (released ' + versionData.release_date + ')'); } downloadZip(versionData.version_number); @@ -230,7 +229,7 @@ function downloadZip(data, version) { fs.mkdirSync(data.tmpExtractPath); } - log.info('Downloading: '.green.bold + archivePath); + log.info(chalk.bold.green('Downloading: ') + archivePath); var proxy = process.env.PROXY || null; request({ url: archivePath, rejectUnauthorized: false, encoding: null, proxy: proxy }, function(err, res, body) { @@ -331,7 +330,7 @@ function updateFiles(data, tmpZipPath, tmpExtractPath) { log.error(e); } - log.info('Ionic version updated to: '.bold.green + data.versionData.version_number.bold); + log.info(chalk.bold.green('Ionic version updated to: ') + chalk.bold(data.versionData.version_number)); writeVersionData(data); diff --git a/lib/ionic/link.js b/lib/ionic/link.js index debf0637ef..fcfa7302e2 100644 --- a/lib/ionic/link.js +++ b/lib/ionic/link.js @@ -1,5 +1,6 @@ 'use strict'; +var chalk = require('chalk'); var extend = require('../utils/extend'); var fs = require('fs'); var path = require('path'); @@ -43,7 +44,7 @@ function run(ionic, argv) { setAppId(ionic, argv._[1]); } - log.info('Your Ionic App ID was set'.green); + log.info(chalk.green('Your Ionic App ID was set')); } function setAppId(ionic, appId) { diff --git a/lib/ionic/list.js b/lib/ionic/list.js index 75a57f59af..a8d686491f 100644 --- a/lib/ionic/list.js +++ b/lib/ionic/list.js @@ -1,7 +1,6 @@ 'use strict'; -require('colors'); - +var chalk = require('chalk'); var extend = require('../utils/extend'); var path = require('path'); var IonicAppLib = require('ionic-app-lib'); @@ -22,10 +21,10 @@ function listComponents() { log.info('Ions, bower components, or addons installed:'); Object.keys(bowerJson.devDependencies).forEach(function(bowerComponentName) { - log.info(bowerComponentName.green); + log.info(chalk.green(bowerComponentName)); }); } catch (ex) { - log.error('This command can only be used when in an Ionic project directory'.red.bold); + log.error(chalk.red.bold('This command can only be used when in an Ionic project directory')); } } @@ -38,10 +37,10 @@ function listComponents() { function run() { // This command will be deprecated in the future. - var deprecationMsg = 'This command has been ' + 'deprecated'.red + '. All ' + + var deprecationMsg = 'This command has been ' + chalk.red('deprecated') + '. All ' + 'resources are currently available in NPM and we recommend that you use NPM to manage these.\n' + 'More information is available here: https://github.com/driftyco/ionic-cli/wiki/Migrating-to-NPM-from-bower\n'; - log.info(deprecationMsg.bold); + log.info(chalk.bold(deprecationMsg)); try { listComponents(); diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 3a42709af4..2ee35a5e95 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -1,5 +1,6 @@ 'use strict'; +var chalk = require('chalk'); var extend = require('../utils/extend'); var IonicAppLib = require('ionic-app-lib'); var Login = IonicAppLib.login; @@ -41,7 +42,7 @@ function login(argv) { return Login.requestLogIn(loginInfo.email, loginInfo.password, true); }) .then(function(cookieJar) { - log.info('Logged in! :)'.green); + log.info(chalk.green('Logged in! :)')); return cookieJar; }) .catch(function(ex) { @@ -55,18 +56,18 @@ function promptForLogin(argv) { var schema = [{ name: 'email', pattern: /^[A-z0-9!#$%&'*+\/=?\^_{|}~\-]+(?:\.[A-z0-9!#$%&'*+\/=?\^_{|}~\-]+)*@(?:[A-z0-9](?:[A-z0-9\-]*[A-z0-9])?\.)+[A-z0-9](?:[A-z0-9\-]*[A-z0-9])?$/, // eslint-disable-line max-len - description: 'Email:'.yellow.bold, + description: chalk.yellow.bold('Email:'), required: true }, { name: 'password', - description: 'Password:'.yellow.bold, + description: chalk.yellow.bold('Password:'), hidden: true, required: true }]; // prompt for log - log.info('\nTo continue, please login to your Ionic account.'.bold.green); - log.info('Don\'t have one? Create one at: '.bold + (appLibSettings.IONIC_DASH + '/signup').bold + '\n'); + log.info(chalk.bold.green('\nTo continue, please login to your Ionic account.')); + log.info(chalk.bold('Don\'t have one? Create one at: ') + chalk.bold(appLibSettings.IONIC_DASH + '/signup') + '\n'); prompt.override = argv; prompt.message = ''; diff --git a/lib/ionic/package.js b/lib/ionic/package.js index 0dbdd74da3..25d126f962 100644 --- a/lib/ionic/package.js +++ b/lib/ionic/package.js @@ -1,5 +1,6 @@ 'use strict'; +var chalk = require('chalk'); var path = require('path'); var _ = require('underscore'); var extend = require('../utils/extend'); @@ -22,18 +23,18 @@ var settings = { name: 'package', summary: 'Use Ionic Package to build your app', args: { - '': 'build android'.yellow + ', ' + 'build ios'.yellow + - ', ' + 'list'.yellow + ', ' + 'info'.yellow + ', or ' + 'download'.yellow, + '': chalk.yellow('build android') + ', ' + chalk.yellow('build ios') + + ', ' + chalk.yellow('list') + ', ' + chalk.yellow('info') + ', or ' + chalk.yellow('download'), '[options]': '' }, options: { - '--release': '(' + 'build '.yellow + + '--release': '(' + chalk.yellow('build ') + ') Mark this build as a release', - '--profile|-p ': '(' + 'build '.yellow + + '--profile|-p ': '(' + chalk.yellow('build ') + ') Specify the Security Profile to use with this build', - '--noresources': '(' + 'build '.yellow + + '--noresources': '(' + chalk.yellow('build ') + ') Do not generate icon and splash screen resources during this build', - '--destination|-d ': '(' + 'download'.yellow + + '--destination|-d ': '(' + chalk.yellow('download') + ') Specify the destination directory to download your packaged app.' }, module: './ionic/package', @@ -59,7 +60,8 @@ function run(ionic, argv) { appId = project.get('app_id'); if (!appId) { - throw new Error('Missing Ionic App ID. Make sure to run "ionic upload" first or set your app_id in the ionic.config.json file.'); + throw new Error('Missing Ionic App ID. Make sure to run "ionic upload" first or set ' + + 'your app_id in the ionic.config.json file.'); } } catch (ex) { return fail(ex, 'package'); @@ -141,9 +143,9 @@ function packageBuild(ionic, argv, dir, project, appId) { function formatStatus(status) { switch (status) { case 'SUCCESS': - return status.green; + return chalk.green(status); case 'FAILED': - return status.red; + return chalk.red(status); } return status; @@ -173,7 +175,7 @@ function packageList(ionic, argv, dir, project, appId) { .then(function(body) { if (body.data.length === 0) { log.info('You don\'t have any builds yet!'); - log.info('Type ' + 'ionic help package'.yellow + ' to learn how to use Ionic Package.'); + log.info('Type ' + chalk.yellow('ionic help package') + ' to learn how to use Ionic Package.'); } else { var count = 0; var headers = ['id', 'status', 'platform', 'mode']; @@ -213,7 +215,7 @@ function packageList(ionic, argv, dir, project, appId) { log.info(''); log.info(table.toString()); - log.info('\nShowing', String(count).yellow, 'of your latest builds.'); + log.info('\nShowing', chalk.yellow(String(count)), 'of your latest builds.'); log.info(''); } }) @@ -252,15 +254,15 @@ function packageInfo(ionic, argv, dir, project, appId) { var build = body.data; table.push( - ['id'.yellow, build.id], - ['status'.yellow, formatStatus(build.status)], - ['platform'.yellow, build.platform], - ['mode'.yellow, build.mode], - ['started'.yellow, formatDate(build.created)] + [chalk.yellow('id'), build.id], + [chalk.yellow('status'), formatStatus(build.status)], + [chalk.yellow('platform'), build.platform], + [chalk.yellow('mode'), build.mode], + [chalk.yellow('started'), formatDate(build.created)] ); if (build.completed) { - table.push(['completed'.yellow, formatDate(build.completed)]); + table.push([chalk.yellow('completed'), formatDate(build.completed)]); } log.info(''); @@ -268,7 +270,7 @@ function packageInfo(ionic, argv, dir, project, appId) { log.info(''); if (build.output) { - log.info('output'.yellow + ':'); + log.info(chalk.yellow('output') + ':'); log.info(''); log.info(build.output); log.info(''); @@ -322,7 +324,7 @@ function packageDownload(ionic, argv, dir, project, appId) { } log.info('Wrote:', filename); - log.info('Done!'.green); + log.info(chalk.green('Done!')); }, null, function(state) { if (typeof bar === 'undefined') { bar = new ProgressBar('Downloading... [:bar] :percent :etas', { diff --git a/lib/ionic/push.js b/lib/ionic/push.js index 2382a4c4f3..720c903221 100644 --- a/lib/ionic/push.js +++ b/lib/ionic/push.js @@ -1,5 +1,6 @@ 'use strict'; +var chalk = require('chalk'); var extend = require('../utils/extend'); var fs = require('fs'); var path = require('path'); @@ -53,7 +54,7 @@ function run(ionic, argv) { if (argv._[2]) { set_webhook_url(task, argv._[2].trim()); } else { - log.error('You need to specify a webhook url!'.bold.red); + log.error(chalk.bold.red('You need to specify a webhook url!')); } } else if (argv['google-api-key']) { set_google_api_key(task, argv['google-api-key'].trim()); @@ -82,7 +83,7 @@ function set_webhook_url(task, webhook_url) { // eslint-disable-line camelcase var project = IonicProject.load(); if (!project.get('app_id')) { - log.error('You need to upload your app first!'.bold.red); + log.error(chalk.bold.red('You need to upload your app first!')); return false; } @@ -126,7 +127,7 @@ function set_google_api_key(task, google_api_key) { // eslint-disable-line camel var project = IonicProject.load(); if (!project.get('app_id')) { - log.error('You need to upload your app first!'.bold.red); + log.error(chalk.bold.red('You need to upload your app first!')); return false; } @@ -155,7 +156,7 @@ function set_google_api_key(task, google_api_key) { // eslint-disable-line camel } if (parseInt(response.statusCode, 10) === '200') { - log.info('Google API Key Saved'.green); + log.info(chalk.green('Google API Key Saved')); } else { return fail('App not found'); } @@ -170,7 +171,7 @@ function send_push() { // eslint-disable-line camelcase var project = IonicProject.load(); if (!project.get('app_id')) { - log.error('You need to upload your app first!'.bold.red); + log.error(chalk.bold.red('You need to upload your app first!')); return false; } @@ -179,35 +180,35 @@ function send_push() { // eslint-disable-line camelcase promptProperties['push-api-key'] = { name: 'push-api-key', - description: 'Your private API key'.yellow.bold, + description: chalk.yellow.bold('Your private API key'), required: true, isFile: false }; promptProperties['device-token'] = { name: 'device-token', - description: 'Device token'.yellow.bold, + description: chalk.yellow.bold('Device token'), required: true, isFile: false }; promptProperties['alert'] = { name: 'alert', - description: 'Notification alert message'.yellow.bold, + description: chalk.yellow.bold('Notification alert message'), required: false, isFile: false }; promptProperties['badge'] = { name: 'badge', - description: 'Badge count'.yellow.bold, + description: chalk.yellow.bold('Badge count'), required: false, isFile: false }; promptProperties['sound'] = { name: 'sound', - description: 'Sound file name'.yellow.bold, + description: chalk.yellow.bold('Sound file name'), required: false, isFile: false }; @@ -258,19 +259,19 @@ function set_production_mode(task) { // eslint-disable-line camelcase var project = IonicProject.load(); if (!project.get('app_id')) { - log.error('You need to upload your app first!'.bold.red); + log.error(chalk.bold.red('You need to upload your app first!')); return false; } if (task.inputValues['production-mode'] === true) { - log.error('You need to specify a value [y/n] to set the production mode!'.bold.red); + log.error(chalk.bold.red('You need to specify a value [y/n] to set the production mode!')); return false; } task.inputValues['production-mode'] = task.inputValues['production-mode'].toLowerCase(); if (task.inputValues['production-mode'] !== 'y' && task.inputValues['production-mode'] !== 'n') { - log.error('You need to specify a value [y/n] to set the production mode!'.bold.red); + log.error(chalk.bold.red('You need to specify a value [y/n] to set the production mode!')); return false; } @@ -307,7 +308,7 @@ function set_production_mode(task) { // eslint-disable-line camelcase var d = JSON.parse(data); if (d.errors && d.errors.length) { for (var j = 0; j < d.errors.length; j += 1) { - log.error((d.errors[j]).bold.red); + log.error(chalk.bold.red(d.errors[j])); } return fail('Unable to set mode'); } @@ -329,7 +330,7 @@ function upload_cert(task) { // eslint-disable-line camelcase var project = IonicProject.load(); if (!project.get('app_id')) { - log.error('You need to upload your app first!'.bold.red); + log.error(chalk.bold.red('You need to upload your app first!')); return false; } @@ -340,7 +341,7 @@ function upload_cert(task) { // eslint-disable-line camelcase if (argv['ios-dev-cert']) { promptProperties['ios-push-cert'] = { name: 'ios-push-cert', - description: 'iOS Dev Push Certificate File (.p12)'.yellow.bold, + description: chalk.yellow.bold('iOS Dev Push Certificate File (.p12)'), required: true, conform: fileExists, isFile: true @@ -348,7 +349,7 @@ function upload_cert(task) { // eslint-disable-line camelcase } else if (argv['ios-prod-cert']) { promptProperties['ios-push-cert'] = { name: 'ios-push-cert', - description: 'iOS Prod Push Certificate File (.p12)'.yellow.bold, + description: chalk.yellow.bold('iOS Prod Push Certificate File (.p12)'), required: true, conform: fileExists, isFile: true @@ -408,7 +409,7 @@ function upload_cert(task) { // eslint-disable-line camelcase var d = JSON.parse(data); if (d.errors && d.errors.length) { for (var j = 0; j < d.errors.length; j += 1) { - log.error((d.errors[j]).bold.red); + log.error(chalk.bold.red(d.errors[j])); } return fail('Unable to upload certificate'); } diff --git a/lib/ionic/remove.js b/lib/ionic/remove.js index 248ab3038e..68ea7a4c19 100644 --- a/lib/ionic/remove.js +++ b/lib/ionic/remove.js @@ -7,6 +7,7 @@ var appLibUtils = IonicAppLib.utils; var ioLib = IonicAppLib.ioConfig; var log = IonicAppLib.logging.logger; var bower = require('../utils/bower'); +var chalk = require('chalk'); var settings = { title: 'remove', @@ -26,12 +27,12 @@ function uninstallBowerComponent(componentName) { if (result.code === 0) { var message = 'Bower component removed - ' + componentName; - return log.info(message.red); + return log.info(chalk.red(message)); } } catch (e) {} // eslint-disable-line no-empty - var errorMessage = 'Failed to find the bower component "'.red.bold + componentName + - '"'.red.bold + '.\nAre you sure it exists?'.red.bold; + var errorMessage = chalk.red.bold('Failed to find the bower component "') + componentName + + chalk.red.bold('"') + chalk.red.bold('.\nAre you sure it exists?'); appLibUtils.fail(errorMessage, 'remove'); } @@ -45,10 +46,10 @@ function uninstallBowerComponent(componentName) { function run(ionic, argv) { // This command will be deprecated in the future. - var deprecationMsg = 'This command has been ' + 'deprecated'.red + '. All ' + + var deprecationMsg = 'This command has been ' + chalk.red('deprecated') + '. All ' + 'resources are currently available in NPM and we recommend that you use NPM to manage these.\n' + 'More information is available here: https://github.com/driftyco/ionic-cli/wiki/Migrating-to-NPM-from-bower\n'; - log.info(deprecationMsg.bold); + log.info(chalk.bold(deprecationMsg)); if (!bower.checkForBower()) { appLibUtils.fail(bower.installMessage, 'remove'); diff --git a/lib/ionic/resources.js b/lib/ionic/resources.js index 6497d161cb..fe0bb48440 100644 --- a/lib/ionic/resources.js +++ b/lib/ionic/resources.js @@ -1,5 +1,6 @@ 'use strict'; +var chalk = require('chalk'); var extend = require('../utils/extend'); var IonicAppLib = require('ionic-app-lib'); var IonicResources = IonicAppLib.resources; @@ -7,7 +8,7 @@ var appLibUtils = IonicAppLib.utils; var Project = IonicAppLib.project; var resourcesSummary = [ - 'Automatically create icon and splash screen resources' + ' (beta)'.yellow, + 'Automatically create icon and splash screen resources' + chalk.yellow(' (beta)'), 'Put your images in the ./resources directory, named splash or icon.', 'Accepted file types are .png, .ai, and .psd.', 'Icons should be 192x192 px without rounded corners.', diff --git a/lib/ionic/run.js b/lib/ionic/run.js index 9188e3b844..385273cc58 100644 --- a/lib/ionic/run.js +++ b/lib/ionic/run.js @@ -1,5 +1,6 @@ 'use strict'; +var chalk = require('chalk'); var extend = require('../utils/extend'); var npmScripts = require('../utils/npmScripts'); var os = require('os'); @@ -10,7 +11,7 @@ var log = IonicAppLib.logging.logger; var cordovaUtils = require('../utils/cordova'); var cordovaRunEmulateOptions = { - '--livereload|-l': 'Live reload app dev files from the device' + ' (beta)'.yellow, + '--livereload|-l': 'Live reload app dev files from the device' + chalk.yellow(' (beta)'), '--address': 'Use specific address (livereload req.)', '--port|-p': 'Dev server HTTP port (8100 default, livereload req.)', '--livereload-port|-r': 'Live Reload port (35729 default, livereload req.)', diff --git a/lib/ionic/security.js b/lib/ionic/security.js index d97ec68113..6d4cfdb9c5 100644 --- a/lib/ionic/security.js +++ b/lib/ionic/security.js @@ -14,6 +14,7 @@ var log = IonicAppLib.logging.logger; var LoginTask = require('./login'); var Prompt = require('../utils/prompt'); var Table = require('../utils/table'); +var chalk = require('chalk'); var Project = IonicAppLib.project; @@ -22,26 +23,26 @@ var settings = { name: 'security', summary: 'Store your app\'s credentials for the Ionic Cloud', args: { - '': 'profiles list'.yellow + ', ' + 'profiles add ""'.yellow + ', ' + - 'credentials android'.yellow + ', or ' + 'credentials ios'.yellow, + '': chalk.yellow('profiles list') + ', ' + chalk.yellow('profiles add ""') + ', ' + + chalk.yellow('credentials android') + ', or ' + chalk.yellow('credentials ios'), '[options]': '' }, options: { - '--profile ': '(' + 'credentials '.yellow + + '--profile ': '(' + chalk.yellow('credentials ') + ') Specify the profile on which these credentials are saved', - '--keystore|-s ': '(' + 'credentials android'.yellow + + '--keystore|-s ': '(' + chalk.yellow('credentials android') + ') Specify the location of your keystore file', - '--keystore-password|-p ': '(' + 'credentials android'.yellow + + '--keystore-password|-p ': '(' + chalk.yellow('credentials android') + ') Specify your keystore password (exclude for prompt)', - '--key-alias|-k ': '(' + 'credentials android'.yellow + + '--key-alias|-k ': '(' + chalk.yellow('credentials android') + ') Specify your key alias for this app', - '--key-password|-w ': '(' + 'credentials android'.yellow + + '--key-password|-w ': '(' + chalk.yellow('credentials android') + ') Specify your key password for this app (exclude for prompt)', - '--cert|-c ': '(' + 'credentials ios'.yellow + + '--cert|-c ': '(' + chalk.yellow('credentials ios') + ') Specify the location of your .p12 file', - '--cert-password|-p ': '(' + 'credentials ios'.yellow + + '--cert-password|-p ': '(' + chalk.yellow('credentials ios') + ') Specify your certificate password (exclude for prompt)', - '--provisioning-profile|-r ': '(' + 'credentials ios'.yellow + + '--provisioning-profile|-r ': '(' + chalk.yellow('credentials ios') + ') Specify the location of your .mobileprovision file' }, isProjectTask: true @@ -157,7 +158,7 @@ function listSecurityProfiles(ionic, argv, dir, appId) { .then(function(body) { if (body.data.length === 0) { log.info('You don\'t have any Security Profiles yet!'); - log.info('Type ' + 'ionic help security'.yellow + ' to learn how to use Security Profiles.'); + log.info('Type ' + chalk.yellow('ionic help security') + ' to learn how to use Security Profiles.'); } else { var table = new Table({ head: ['name', 'tag', 'android', 'ios'] }); @@ -165,8 +166,8 @@ function listSecurityProfiles(ionic, argv, dir, appId) { table.push([ profile.name, profile.tag, - typeof profile.credentials.android === 'undefined' ? '✗'.red.bold : '✓'.green.bold, - typeof profile.credentials.ios === 'undefined' ? '✗'.red.bold : '✓'.green.bold + typeof profile.credentials.android === 'undefined' ? chalk.red.bold('✗') : chalk.green.bold('✓'), + typeof profile.credentials.ios === 'undefined' ? chalk.red.bold('✗') : chalk.green.bold('✓') ]); }); diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 636e019b8e..6febc53ac9 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -3,7 +3,6 @@ var extend = require('../utils/extend'); var npmScripts = require('../utils/npmScripts'); var IonicAppLib = require('ionic-app-lib'); -var appLibUtils = IonicAppLib.utils; var events = IonicAppLib.events; var Q = require('q'); var Serve = IonicAppLib.serve; diff --git a/lib/ionic/service.js b/lib/ionic/service.js index ae489ce24d..fe95cd1d00 100644 --- a/lib/ionic/service.js +++ b/lib/ionic/service.js @@ -1,5 +1,6 @@ 'use strict'; +var chalk = require('chalk'); var fs = require('fs'); var path = require('path'); var execSync = require('child_process').execSync; @@ -69,8 +70,8 @@ function installBowerComponent(serviceName) { } catch (e) {} // eslint-disable-line no-empty // Error happened, report it. - var errorMessage = 'Failed to find the service "'.bold + serviceName.verbose + - '"'.bold + '.\nAre you sure it exists?'.bold; + var errorMessage = chalk.bold('Failed to find the service "') + serviceName.verbose + + chalk.bold('".\nAre you sure it exists?'); fail(errorMessage, 'service'); } @@ -85,8 +86,8 @@ function uninstallBowerComponent(serviceName) { } } catch (e) {} // eslint-disable-line no-empty - var errorMessage = 'Failed to find the service "'.bold + serviceName.verbose + - '"'.bold + '.\nAre you sure it exists?'.bold; + var errorMessage = chalk.bold('Failed to find the service "') + serviceName.verbose + + chalk.bold('".\nAre you sure it exists?'); fail(errorMessage, 'service'); } @@ -132,7 +133,7 @@ function installBowerPlugins(directory, serviceName) { } } catch (e) {} // eslint-disable-line no-empty - var errorMessage = 'Failed to find the plugin "'.bold + plugin.name.verbose + '"'.bold + '.'.bold; + var errorMessage = chalk.bold('Failed to find the plugin "') + plugin.name.verbose + chalk.bold('".'); fail(errorMessage, 'service'); }); } @@ -151,7 +152,7 @@ function uninstallBowerPlugins(bowerJson) { } } catch (e) {} // eslint-disable-line no-empty - var errorMessage = 'Failed to find the plugin to remove "'.bold + plugin.name.verbose + '"'.bold + '.'.bold; + var errorMessage = chalk.bold('Failed to find the plugin to remove "') + plugin.name.verbose + chalk.bold('".'); fail(errorMessage, 'service'); }); } @@ -183,10 +184,10 @@ function removeService(serviceName) { function run(ionic, argv) { // This command will be deprecated in the future. - var deprecationMsg = 'This command has been ' + 'deprecated'.red + '. All ' + + var deprecationMsg = 'This command has been ' + chalk.red('deprecated') + '. All ' + 'resources are currently available in NPM and we recommend that you use NPM to manage these.\n' + 'More information is available here: https://github.com/driftyco/ionic-cli/wiki/Migrating-to-NPM-from-bower\n'; - log.info(deprecationMsg.bold); + log.info(chalk.bold(deprecationMsg)); if (!bower.checkForBower()) { fail(bower.installMessage, 'service'); diff --git a/lib/ionic/setup.js b/lib/ionic/setup.js index 484db3daca..2d15e33078 100644 --- a/lib/ionic/setup.js +++ b/lib/ionic/setup.js @@ -1,7 +1,6 @@ 'use strict'; -require('colors'); - +var chalk = require('chalk'); var extend = require('../utils/extend'); var Q = require('q'); var IonicAppLib = require('ionic-app-lib'); @@ -12,16 +11,16 @@ var appLibSetup = IonicAppLib.setup; var settings = { title: 'setup', name: 'setup', - summary: 'Configure the project with a build tool ' + '(beta)'.yellow, + summary: 'Configure the project with a build tool ' + chalk.yellow('(beta)'), args: { '[sass]': 'Setup the project to use Sass CSS precompiling' }, isProjectTask: true -} +}; function run(ionic, argv) { var taskName = argv._[1]; - if(!taskName) { + if (!taskName) { appLibUtils.fail('Missing setup task command.', 'setup'); return Q(); } @@ -30,14 +29,14 @@ function run(ionic, argv) { return Q(); } - return appLibSetup.sassSetup(process.cwd()) + return appLibSetup.sassSetup(process.cwd()) .then(function() { - log.info('Ionic setup complete'.green); + log.info(chalk.green('Ionic setup complete')); }) .catch(function(error) { log.error('Error from setup - ' + error); - }) -}; + }); +} module.exports = extend(settings, { run: run diff --git a/lib/ionic/share.js b/lib/ionic/share.js index f61be09fee..906b0526d0 100644 --- a/lib/ionic/share.js +++ b/lib/ionic/share.js @@ -1,5 +1,6 @@ 'use strict'; +var chalk = require('chalk'); var extend = require('../utils/extend'); var LoginTask = require('./login'); var IonicAppLib = require('ionic-app-lib'); @@ -43,7 +44,8 @@ function run(ionic, argv) { return appLibUtils.fail('Invalid email address', 'share'); } - log.info(['Sharing app ', project.get('name'), ' (', project.get('app_id'), ') with ', email, '.'].join('').green); + log.info(chalk.green(['Sharing app ', + project.get('name'), ' (', project.get('app_id'), ') with ', email, '.'].join(''))); return Login.retrieveLogin() .then(function(jar) { diff --git a/lib/ionic/start.js b/lib/ionic/start.js index bc891fd7b2..b49598a49e 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -1,6 +1,6 @@ require('shelljs/global'); -require('colors'); +var chalk = require('chalk'); var extend = require('../utils/extend'); var fs = require('fs'); var Q = require('q'); @@ -63,7 +63,7 @@ function run(ionic, argv) { } if (argv._[1] === '.') { - return log.error('Please name your Ionic project something meaningful other than \'.\''.red); + return log.error(chalk.red('Please name your Ionic project something meaningful other than \'.\'')); } // Ensure that the folder name provided is a String diff --git a/lib/utils/bower.js b/lib/utils/bower.js index 9e24f66a71..5cbc10fd56 100644 --- a/lib/utils/bower.js +++ b/lib/utils/bower.js @@ -1,7 +1,6 @@ 'use strict'; -require('colors'); - +var chalk = require('chalk'); var fs = require('fs'); var path = require('path'); var childProcess = require('child_process'); @@ -43,7 +42,7 @@ function saveData(bowerData) { var bowerFilePath = path.resolve('bower.json'); fs.writeFileSync(bowerFilePath, JSON.stringify(bowerData, null, 2)); } catch (e) { - log.error(('Error saving ' + bowerFilePath + ': ' + e).red.bold); + log.error(chalk.red.bold('Error saving ' + bowerFilePath + ': ' + e)); } } diff --git a/lib/utils/cordova.js b/lib/utils/cordova.js index c6b71e2968..8cee6053d2 100644 --- a/lib/utils/cordova.js +++ b/lib/utils/cordova.js @@ -1,7 +1,6 @@ 'use strict'; -require('colors'); - +var chalk = require('chalk'); var Q = require('q'); var _ = require('underscore'); var path = require('path'); @@ -59,7 +58,7 @@ function arePluginsInstalled(baseDir) { * @return {Promise} Promise upon completion */ function installPlatform(platform) { - log.info(('• You\'re trying to build for ' + platform + ' but don\'t have the platform installed yet.').yellow); + log.info(chalk.yellow('• You\'re trying to build for ' + platform + ' but don\'t have the platform installed yet.')); log.info('∆ Installing ' + platform + ' for you.'); return promiseExec('cordova platform add ' + platform).then(function() { @@ -80,7 +79,7 @@ function execCordovaCommand(optionList, isLiveReload, serveOptions) { cordovaProcess.stderr.on('data', function(data) { if (data) { - log.error(data.toString().bold); + log.error(chalk.bold(data.toString())); } }); @@ -210,7 +209,7 @@ function filterArgumentsForCordova(cmdName, argv, rawCliArguments) { * @return {Promise} Promise upon completion */ function setupLiveReload(argv, baseDir) { - log.info(('Setup Live Reload').green.bold); + log.info(chalk.green.bold('Setup Live Reload')); var project = Project.load(baseDir); var options = _.extend(Serve.loadSettings(argv, project), { diff --git a/lib/utils/help.js b/lib/utils/help.js index 2aebb576f7..bc1b69d1cf 100644 --- a/lib/utils/help.js +++ b/lib/utils/help.js @@ -2,8 +2,8 @@ var IonicAppLib = require('ionic-app-lib'); var log = IonicAppLib.logging.logger; +var chalk = require('chalk'); var EOL = require('os').EOL; -require('colors'); /** * @method printIonic @@ -50,7 +50,7 @@ function printTaskListShortUsage(taskList, taskName, ionicVersion) { while ((name + dots).length < 20) { dots += '.'; } - return name.green.bold + dots.grey + ' ' + task.summary.bold; + return chalk.green.bold(name) + chalk.grey(dots) + ' ' + chalk.bold(task.summary); }); var lines = [].concat( @@ -62,9 +62,9 @@ function printTaskListShortUsage(taskList, taskName, ionicVersion) { '', '=======================', (taskName ? - (taskName + ' is not a valid task\n\n').bold.red + chalk.bold.red(taskName + ' is not a valid task\n\n') : null), - 'Available tasks: '.bold, + chalk.bold('Available tasks: '), '(use --help or -h for more info)', '', '' @@ -152,16 +152,16 @@ function printTaskDetails(d) { } } - w(taskArgs.green.bold); + w(chalk.green.bold(taskArgs)); while ((taskArgs + dots).length < rightColumn + 1) { dots += '.'; } - w(' ' + dots.grey + ' '); + w(' ' + chalk.grey(dots) + ' '); if (d.summary) { - w(d.summary.bold); + w(chalk.bold(d.summary)); } for (arg in d.args) { @@ -172,7 +172,7 @@ function printTaskDetails(d) { while (indent.length < rightColumn) { indent += ' '; } - w((indent + ' ' + arg + ' ').bold); + w(chalk.bold(indent + ' ' + arg + ' ')); var argDescs = d.args[arg].split(EOL); var argIndent = indent + ' '; @@ -183,9 +183,9 @@ function printTaskDetails(d) { for (x = 0; x < argDescs.length; x += 1) { if (x === 0) { - w(argDescs[x].bold); + w(chalk.bold(argDescs[x])); } else { - w('\n' + argIndent + argDescs[x].bold); + w('\n' + argIndent + chalk.bold(argDescs[x])); } } } @@ -207,13 +207,13 @@ function printTaskDetails(d) { var optLine = indent + '[' + opt + '] '; - w(optLine.yellow.bold); + w(chalk.yellow.bold(optLine)); if (d.options[opt]) { while ((dots.length + optLine.length - 2) < rightColumn) { dots += '.'; } - w(dots.grey + ' '); + w(chalk.grey(dots) + ' '); var taskOpt = d.options[opt]; var optDescs; @@ -225,9 +225,9 @@ function printTaskDetails(d) { } for (x = 0; x < optDescs.length; x += 1) { if (x === 0) { - w(optDescs[x].bold); + w(chalk.bold(optDescs[x])); } else { - w('\n' + optIndent + optDescs[x].bold); + w('\n' + optIndent + chalk.bold(optDescs[x])); } } } diff --git a/lib/utils/ionitron.js b/lib/utils/ionitron.js index fd630f04e8..2568b62a26 100644 --- a/lib/utils/ionitron.js +++ b/lib/utils/ionitron.js @@ -1,6 +1,6 @@ 'use strict'; -var colors = require('colors'); +var chalk = require('chalk'); var path = require('path'); var fs = require('fs'); var IonicAppLib = require('ionic-app-lib'); @@ -67,7 +67,7 @@ function print(lang) { } } contents = contents.replace(replaceString, messageContent); - log.info(colors.cyan(contents)); + log.info(chalk.cyan(contents)); return; } diff --git a/lib/utils/templates.js b/lib/utils/templates.js index 1f58639602..83ee22b6df 100644 --- a/lib/utils/templates.js +++ b/lib/utils/templates.js @@ -1,5 +1,4 @@ -require('colors'); - +var chalk = require('chalk'); var _ = require('underscore'); var Q = require('q'); var request = require('request'); @@ -11,8 +10,7 @@ function fetchStarterTemplates() { // log.info('About to fetch template'); var downloadUrl = 'http://code.ionicframework.com/content/starter-templates.json'; - // log.info('\nDownloading Starter Templates'.bold, downloadUrl, starterTemplateJsonPath); - log.info('\nDownloading Starter Templates'.bold, '-', downloadUrl); + log.info(chalk.bold('\nDownloading Starter Templates'), '-', downloadUrl); var q = Q.defer(); @@ -47,7 +45,7 @@ function list(templates) { while ((shortName + dots).length < rightColumn + 1) { dots += '.'; } - log.info(shortName.green, dots, template.description); + log.info(chalk.green(shortName), dots, template.description); }); } @@ -56,7 +54,7 @@ function listTemplates() { return fetchStarterTemplates() .then(function(starterTemplates) { var templates = _.sortBy(starterTemplates.items, function(template) { return template.name; }); - log.info('Ionic Starter templates'.green); + log.info(chalk.green('Ionic Starter templates')); return list(templates); }); } diff --git a/package.json b/package.json index 3eeda02abb..be563b5f6f 100644 --- a/package.json +++ b/package.json @@ -61,8 +61,8 @@ "license": "MIT", "dependencies": { "@ionic/app-generators": "0.0.3", + "chalk": "^1.1.3", "cli-table": "0.3.1", - "colors": "0.6.2", "cross-spawn-async": "2.1.9", "expand-tilde": "1.2.0", "form-data": "0.1.4", @@ -87,7 +87,8 @@ "devDependencies": { "conventional-changelog-cli": "1.1.1", "coveralls": "^2.2.0", - "eslint": "^3.0.1", + "eslint": "^3.8.1", + "eslint-plugin-no-use-extend-native": "^0.3.11", "github": "0.2.4", "istanbul": "^0.4.4", "jasmine-node": "1.14.5", @@ -96,7 +97,7 @@ }, "bundledDependencies": [ "cli-table", - "colors", + "chalk", "cross-spawn-async", "expand-tilde", "form-data", From 38cfaaa5a0d5b5a733ccdbdc5edcf6e64538ce8c Mon Sep 17 00:00:00 2001 From: benmag1 Date: Sat, 29 Oct 2016 14:29:28 +0300 Subject: [PATCH 1072/1100] chore(): update request dependency to a more secure version. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index be563b5f6f..4cb4808653 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "progress": "1.1.7", "prompt": "0.2.12", "q": "1.0.1", - "request": "2.51.0", + "request": "2.74.0", "semver": "4.3.6", "serve-static": "1.7.1", "shelljs": "0.2.6", From 2df9b701f49f9f1c1b3dcd42913335e2854da9f3 Mon Sep 17 00:00:00 2001 From: Ramon Henrique Ornelas Date: Sat, 29 Oct 2016 09:31:18 -0200 Subject: [PATCH 1073/1100] docs(generator): add docs help in usage #1411 (#1446) --- lib/ionic/generate.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/ionic/generate.js b/lib/ionic/generate.js index f7e616b671..86faab4f71 100644 --- a/lib/ionic/generate.js +++ b/lib/ionic/generate.js @@ -16,7 +16,21 @@ var settings = { '--list': { title: 'List available generators', boolean: true - } + }, + '--includeSpec': { + title: 'Create test spec basic to pages, components, directives, pipes and providers', + boolean: true + }, + '--skipScss': { + title: 'Not create scss for components and pages', + boolean: true + }, + '--componentsDir': 'Path directory target is created component', + '--directivesDir': 'Path directory target is created directive', + '--pagesDir': 'Path directory target is created page', + '--pipesDir': 'Path directory target is created pipe', + '--providersDir': 'Path directory target is created provider', + '--templateDir': 'Path directory templates custom to pages, components, directives, pipes and providers' }, isProjectTask: true }; From dc4ff0a56a2178599df164801d5d2db8c5041d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20M=C3=A9zino?= Date: Sat, 29 Oct 2016 13:33:41 +0200 Subject: [PATCH 1074/1100] fix(docs): Add summary and options in settings of login --- lib/ionic/login.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/ionic/login.js b/lib/ionic/login.js index 2ee35a5e95..2e9045fdf9 100644 --- a/lib/ionic/login.js +++ b/lib/ionic/login.js @@ -12,6 +12,11 @@ var appLibSettings = IonicAppLib.settings; var settings = { title: 'login', name: 'login', + summary: 'Login to your Ionic account', + options: { + '--email|-e': 'Ionic account email', + '--password|-p': 'Ionic account password' + }, isProjectTask: false }; From 6bd2658e40f31744ade5ac5ee0ca9bc171269a9e Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 9 Nov 2016 12:18:04 -0600 Subject: [PATCH 1075/1100] feat(): allow emulate and run to use serve for livereload. (#1614) --- lib/cli.js | 61 +++++++++------- lib/ionic/emulate.js | 50 +++++++++---- lib/ionic/info.js | 16 +--- lib/ionic/run.js | 53 +++++++++----- lib/ionic/stats.js | 145 ------------------------------------- lib/utils/serve.js | 24 ++++++ lib/utils/stats.js | 11 +-- package.json | 2 +- spec/cli.spec.js | 62 ++++++++-------- spec/tasks/emulate.spec.js | 69 ++++++++++++++++-- spec/tasks/info.spec.js | 43 +++++------ spec/tasks/run.spec.js | 71 ++++++++++++++++-- spec/utils/stats.spec.js | 137 ++++++++++++++++------------------- 13 files changed, 380 insertions(+), 364 deletions(-) delete mode 100644 lib/ionic/stats.js create mode 100644 lib/utils/serve.js diff --git a/lib/cli.js b/lib/cli.js index 1dbf2dcfea..b8d7a2b564 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -42,6 +42,21 @@ log.level = 'info'; * @return {Promise} */ Cli.run = function run(processArgv) { + + /* + * Before taking any more steps, lets check their + * environment and display any upgrade warnings + */ + return Q.all([ + Cli.doRuntimeCheck(), + Cli.checkLatestVersion() + ]).then(function() { + Cli.runr(processArgv); + }).catch(function(ex) { + return appLibUtils.fail(ex); + }); +}; +Cli.runr = function runr(processArgv) { try { /* @@ -54,18 +69,11 @@ Cli.run = function run(processArgv) { var taskList; Cli.attachErrorHandling(); - Cli.latestVersion = Cli.checkLatestVersion(); process.on('exit', function() { Cli.printVersionWarning(settings.version, Cli.npmVersion); }); - /* - * Before taking any more steps, lets check their - * environment and display any upgrade warnings - */ - Cli.doRuntimeCheck(settings.version); - /* * Print version if '--version' or '--v' is an option * TODO: version should probably also be a command @@ -180,7 +188,7 @@ Cli.run = function run(processArgv) { log.debug('\nNpm scripts:', npmScripts); log.debug('Gulpfile found:', gulpLoaded, '\n'); - if (npmScripts) { + if (npmScripts.hasOwnProperty(taskName + ':before') || npmScripts.hasOwnProperty(taskName + ':after')) { return Cli.runWithNpmScripts(argv, task, rawCliArguments); } else if (gulpLoaded) { return Cli.runWithGulp(argv, task, rawCliArguments); @@ -621,13 +629,6 @@ Cli.printNewsUpdates = function printNewsUpdates(skipNewsCheck) { return q.promise; }; -Cli.gatherInfo = function gatherInfo() { - var info = Info.gatherInfo(); - Info.getIonicVersion(info, process.cwd()); - Info.getIonicCliVersion(info, path.join(__dirname, '../')); - return info; -}; - Cli.handleUncaughtExceptions = function handleUncaughtExceptions(err) { log.error(chalk.red.bold('An uncaught exception occurred and has been reported to Ionic')); var errorMessage = typeof err === 'string' ? err : err.message; @@ -637,12 +638,12 @@ Cli.handleUncaughtExceptions = function handleUncaughtExceptions(err) { Cli.attachErrorHandling = function attachErrorHandling() { appLibUtils.errorHandler = function errorHandler(msg) { - try { - log.debug('Cli.appLibUtils.errorHandler msg', msg, typeof msg); - var stack = typeof msg == 'string' ? '' : msg.stack; - var errorMessage = typeof msg == 'string' ? msg : msg.message; - if (msg) { - var info = Cli.gatherInfo(); + log.debug('Cli.appLibUtils.errorHandler msg', msg, typeof msg); + var stack = typeof msg == 'string' ? '' : msg.stack; + var errorMessage = typeof msg == 'string' ? msg : msg.message; + var promise = Q(); + if (msg) { + promise = Info.gatherInfo().then(function(info) { var ionicCliVersion = info.ionic_cli; if (stack && stack.length > 0) { process.stderr.write('\n' + chalk.bold(stack) + '\n\n'); @@ -651,14 +652,16 @@ Cli.attachErrorHandling = function attachErrorHandling() { process.stderr.write(chalk.bold(' (CLI v' + ionicCliVersion + ')') + '\n'); Info.printInfo(info); - } + }); + } + promise.then(function() { process.stderr.write('\n'); process.exit(1); return ''; - } catch (ex) { + }).catch(function(ex) { console.log('errorHandler had an error', ex); console.log(ex.stack); - } + }); }; // TODO Attach error reporter here @@ -686,10 +689,14 @@ Cli.doRuntimeCheck = function doRuntimeCheck(version) { } if (!lastVersionChecked || !versionHasBeenChecked) { - Info.checkRuntime(); - IonicConfig.set('lastVersionChecked', version); - IonicConfig.save(); + return Info.gatherInfo().then(function(info) { + Info.checkRuntime(info); + IonicConfig.set('lastVersionChecked', version); + IonicConfig.save(); + }); } + + return Q(); }; module.exports = Cli; diff --git a/lib/ionic/emulate.js b/lib/ionic/emulate.js index 714a824854..e09025822c 100644 --- a/lib/ionic/emulate.js +++ b/lib/ionic/emulate.js @@ -1,6 +1,7 @@ 'use strict'; var chalk = require('chalk'); +var serveUtil = require('../utils/serve'); var extend = require('../utils/extend'); var npmScripts = require('../utils/npmScripts'); var os = require('os'); @@ -46,6 +47,10 @@ function run(ionic, argv, rawCliArguments) { var appDirectory = process.cwd(); var rawArgs = rawCliArguments.slice(0); var cmdName = argv._[0].toLowerCase(); + var hasBuildCommand = false; + var hasServeCommand = false; + var address = argv.address || serveUtil.DEFAULT_ADDRESS; + var port = argv.port || serveUtil.DEFAULT_HTTP_PORT; var isLiveReload = argv.livereload || argv['live-reload'] || argv.l || false; @@ -64,30 +69,45 @@ function run(ionic, argv, rawCliArguments) { var promiseList = [] .concat(!cordovaUtils.isPlatformInstalled(runPlatform, appDirectory) ? cordovaUtils.installPlatform(runPlatform) : - []) + Q()) .concat(!cordovaUtils.arePluginsInstalled(appDirectory) ? cordovaUtils.installPlugins() : - []); + Q()) + .concat(npmScripts.hasIonicScript('build')) + .concat(npmScripts.hasIonicScript('serve')); - return Q.all(promiseList).then(function() { - return npmScripts.hasIonicScript('build'); - }) - .then(function(hasBuildCommand) { - if (hasBuildCommand) { + return Q.all(promiseList).then(function(results) { + hasBuildCommand = results[2]; + hasServeCommand = results[3]; + + if (hasBuildCommand && !(isLiveReload && hasServeCommand)) { return npmScripts.runIonicScript('build'); } - return Q.resolve(); + return Q(); }) .then(function() { - if (isLiveReload) { + + if (isLiveReload && hasServeCommand) { + + // using app-scripts and livereload is requested + return ConfigXml.setConfigXml(process.cwd(), { + devServer: serveUtil.getUrl(address, port) + }).then(function() { + isLiveReload = false; + return npmScripts.runIonicScript('serve', ['-p', port, '--address', address]); + }); + } else if (isLiveReload) { + + // not an app-scripts project but the user wants livereload return cordovaUtils.setupLiveReload(argv, appDirectory); - } + } else { - // ensure the content node was set back to its original - return ConfigXml.setConfigXml(appDirectory, { - resetContent: true, - errorWhenNotFound: false - }); + // ensure the content node was set back to its original + return ConfigXml.setConfigXml(appDirectory, { + resetContent: true, + errorWhenNotFound: false + }); + } }) .then(function(serveOptions) { var optionList = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawArgs); diff --git a/lib/ionic/info.js b/lib/ionic/info.js index 8827b22b5e..049fa9a0e4 100644 --- a/lib/ionic/info.js +++ b/lib/ionic/info.js @@ -1,7 +1,6 @@ 'use strict'; var extend = require('../utils/extend'); -var path = require('path'); var IonicAppLib = require('ionic-app-lib'); var Info = IonicAppLib.info; var appLibUtils = IonicAppLib.utils; @@ -14,20 +13,13 @@ var settings = { }; function run() { - try { - - var info = Info.gatherInfo(); - - Info.getIonicVersion(info, process.cwd()); - Info.getIonicCliVersion(info, path.join(__dirname, '../../')); - Info.getIonicAppScriptsVersion(info, process.cwd()); - + return Info.gatherInfo().then(function(info) { Info.printInfo(info); - Info.checkRuntime(); - } catch (ex) { + Info.checkRuntime(info); + }).catch(function(ex) { appLibUtils.fail('There was an error retrieving your environment information:', ex); - } + }); } module.exports = extend(settings, { diff --git a/lib/ionic/run.js b/lib/ionic/run.js index 385273cc58..85e4c4ec3f 100644 --- a/lib/ionic/run.js +++ b/lib/ionic/run.js @@ -1,6 +1,7 @@ 'use strict'; var chalk = require('chalk'); +var serveUtil = require('../utils/serve'); var extend = require('../utils/extend'); var npmScripts = require('../utils/npmScripts'); var os = require('os'); @@ -46,6 +47,10 @@ function run(ionic, argv, rawCliArguments) { var appDirectory = process.cwd(); var rawArgs = rawCliArguments.slice(0); var cmdName = argv._[0].toLowerCase(); + var hasBuildCommand = false; + var hasServeCommand = false; + var address = argv.address || serveUtil.DEFAULT_ADDRESS; + var port = argv.port || serveUtil.DEFAULT_HTTP_PORT; var isLiveReload = argv.livereload || argv['live-reload'] || argv.l || false; @@ -64,30 +69,44 @@ function run(ionic, argv, rawCliArguments) { var promiseList = [] .concat(!cordovaUtils.isPlatformInstalled(runPlatform, appDirectory) ? cordovaUtils.installPlatform(runPlatform) : - []) + Q()) .concat(!cordovaUtils.arePluginsInstalled(appDirectory) ? cordovaUtils.installPlugins() : - []); + Q()) + .concat(npmScripts.hasIonicScript('build')) + .concat(npmScripts.hasIonicScript('serve')); - return Q.all(promiseList).then(function() { - return npmScripts.hasIonicScript('build'); - }) - .then(function(hasBuildCommand) { - if (hasBuildCommand) { + return Q.all(promiseList).then(function(results) { + hasBuildCommand = results[2]; + hasServeCommand = results[3]; + + if (hasBuildCommand && !(isLiveReload && hasServeCommand)) { return npmScripts.runIonicScript('build'); } - return Q.resolve(); - }) - .then(function() { - if (isLiveReload) { + return Q(); + }).then(function() { + + if (isLiveReload && hasServeCommand) { + + // using app-scripts and livereload is requested + return ConfigXml.setConfigXml(process.cwd(), { + devServer: serveUtil.getUrl(address, port) + }).then(function() { + isLiveReload = false; + return npmScripts.runIonicScript('serve', ['-p', port, '--address', address]); + }); + } else if (isLiveReload) { + + // not an app-scripts project but the user wants livereload return cordovaUtils.setupLiveReload(argv, appDirectory); - } + } else { - // ensure the content node was set back to its original - return ConfigXml.setConfigXml(appDirectory, { - resetContent: true, - errorWhenNotFound: false - }); + // ensure the content node was set back to its original + return ConfigXml.setConfigXml(appDirectory, { + resetContent: true, + errorWhenNotFound: false + }); + } }) .then(function(serveOptions) { var optionList = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawArgs); diff --git a/lib/ionic/stats.js b/lib/ionic/stats.js deleted file mode 100644 index 4509a10bd3..0000000000 --- a/lib/ionic/stats.js +++ /dev/null @@ -1,145 +0,0 @@ -var request = require('request'), - IonicConfig = require('./config'); - -var proxy = process.env.PROXY || process.env.http_proxy || null; - -var s = { - track: function(event, uuid, data, callback) { - var data = { - '_event' : event, - '_uuid': uuid, - 'data': data - }; - - request({ - url: 'https://t.ionic.io/event/cli', - method: "POST", - json: data, - proxy: proxy - }, function(err, res, body) { - callback(err, res, body); - }); - } -}; - -var ionicConfig = IonicConfig.load(); - -exports.IonicStats = { - t: function(additionalData) { - try { - - if(process.argv.length < 3) return; - - if( ionicConfig.get('statsOptOut') === true ) { - return; - } - - var cmdName = process.argv[2].toLowerCase(); - var cmdArgs = (process.argv.length > 3 ? process.argv.slice(3) : []); // skip the cmdName - - var statsData = additionalData || {}; - var platforms = []; - var x, y, cmd; - - // update any aliases with the full cmd so there's a common property - var aliasMap = { - 'rm': 'remove', - 'ls': 'list', - 'up': 'update', - '-w': '--no-cordova', - '-b': '--nobrowser', - '-r': '--nolivereload', - '-x': '--noproxy', - '-l': '--livereload', - '-c': '--consolelogs', - '-s': '--serverlogs', - '-n': '--no-email', - '-s': '--sass' - }; - for(x=0; x Date: Wed, 9 Nov 2016 12:20:25 -0600 Subject: [PATCH 1076/1100] chore(): update changelog and package.json. --- CHANGELOG.md | 16 ++++++++++++++++ package.json | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1eb17d536f..b859d6648a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ + +## [2.1.5](https://github.com/driftyco/ionic-cli/compare/v2.1.4...v2.1.5) (2016-11-09) + + +### Bug Fixes + +* **cli:** npm install under windows ([#1539](https://github.com/driftyco/ionic-cli/issues/1539)) ([26486d2](https://github.com/driftyco/ionic-cli/commit/26486d2)), closes [#1445](https://github.com/driftyco/ionic-cli/issues/1445) +* **docs:** Add summary and options in settings of login ([dc4ff0a](https://github.com/driftyco/ionic-cli/commit/dc4ff0a)) + + +### Features + +* allow emulate and run to use serve for livereload. ([#1614](https://github.com/driftyco/ionic-cli/issues/1614)) ([6bd2658](https://github.com/driftyco/ionic-cli/commit/6bd2658)) + + + ## [2.1.4](https://github.com/driftyco/ionic-cli/compare/v2.1.2...v2.1.4) (2016-10-21) diff --git a/package.json b/package.json index 84c768c1b2..4fbd5b3a6d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.1.4", + "version": "2.1.5", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 70a691c1f4194eb81c41141e1993581a3539e4b9 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 9 Nov 2016 13:36:45 -0600 Subject: [PATCH 1077/1100] fix(start): fix regression with new version of app-lib --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4fbd5b3a6d..8ee95c4a4f 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "gulp": "3.8.8", "gulp-util": "3.0.7", "inquirer": "0.11.2", - "ionic-app-lib": "2.1.3", + "ionic-app-lib": "2.1.4", "moment": "2.11.1", "open": "0.0.5", "optimist": "0.6.0", From f53eea90fd482557232b9a45d58c3bff8504b40d Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 9 Nov 2016 13:37:50 -0600 Subject: [PATCH 1078/1100] chore(): create changelog and increment npm version. --- CHANGELOG.md | 10 ++++++++++ package.json | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b859d6648a..bb0b28647d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ + +## [2.1.6](https://github.com/driftyco/ionic-cli/compare/v2.1.5...v2.1.6) (2016-11-09) + + +### Bug Fixes + +* **start:** fix regression with new version of app-lib ([70a691c](https://github.com/driftyco/ionic-cli/commit/70a691c)) + + + ## [2.1.5](https://github.com/driftyco/ionic-cli/compare/v2.1.4...v2.1.5) (2016-11-09) diff --git a/package.json b/package.json index 8ee95c4a4f..81b589f5a7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.1.5", + "version": "2.1.6", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From ddcc2a0dbc33fe0501858feb936a1b7b908593cb Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 9 Nov 2016 14:53:25 -0600 Subject: [PATCH 1079/1100] fix(serve): prevent emulate and run livereload from using app-scripts for now. --- lib/ionic/emulate.js | 26 ++++++++++++++------------ lib/ionic/run.js | 29 ++++++++++++++++------------- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/lib/ionic/emulate.js b/lib/ionic/emulate.js index e09025822c..b74ad7e596 100644 --- a/lib/ionic/emulate.js +++ b/lib/ionic/emulate.js @@ -80,39 +80,41 @@ function run(ionic, argv, rawCliArguments) { hasBuildCommand = results[2]; hasServeCommand = results[3]; - if (hasBuildCommand && !(isLiveReload && hasServeCommand)) { + if (hasBuildCommand) { return npmScripts.runIonicScript('build'); } return Q(); }) .then(function() { - if (isLiveReload && hasServeCommand) { + if (isLiveReload && hasServeCommand && false) { // using app-scripts and livereload is requested return ConfigXml.setConfigXml(process.cwd(), { devServer: serveUtil.getUrl(address, port) - }).then(function() { - isLiveReload = false; - return npmScripts.runIonicScript('serve', ['-p', port, '--address', address]); }); } else if (isLiveReload) { // not an app-scripts project but the user wants livereload return cordovaUtils.setupLiveReload(argv, appDirectory); - } else { - - // ensure the content node was set back to its original - return ConfigXml.setConfigXml(appDirectory, { - resetContent: true, - errorWhenNotFound: false - }); } + + // ensure the content node was set back to its original + return ConfigXml.setConfigXml(appDirectory, { + resetContent: true, + errorWhenNotFound: false + }); }) .then(function(serveOptions) { var optionList = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawArgs); return cordovaUtils.execCordovaCommand(optionList, isLiveReload, serveOptions); }) + .then(function() { + if (isLiveReload && hasServeCommand && false) { + return npmScripts.runIonicScript('serve', ['-p', port, '--address', address]); + } + return Q(); + }) .catch(function(ex) { if (ex instanceof Error) { log.error(ex); diff --git a/lib/ionic/run.js b/lib/ionic/run.js index 85e4c4ec3f..c22f87b989 100644 --- a/lib/ionic/run.js +++ b/lib/ionic/run.js @@ -80,38 +80,41 @@ function run(ionic, argv, rawCliArguments) { hasBuildCommand = results[2]; hasServeCommand = results[3]; - if (hasBuildCommand && !(isLiveReload && hasServeCommand)) { + if (hasBuildCommand) { return npmScripts.runIonicScript('build'); } return Q(); - }).then(function() { + }) + .then(function() { - if (isLiveReload && hasServeCommand) { + if (isLiveReload && hasServeCommand && false) { // using app-scripts and livereload is requested return ConfigXml.setConfigXml(process.cwd(), { devServer: serveUtil.getUrl(address, port) - }).then(function() { - isLiveReload = false; - return npmScripts.runIonicScript('serve', ['-p', port, '--address', address]); }); } else if (isLiveReload) { // not an app-scripts project but the user wants livereload return cordovaUtils.setupLiveReload(argv, appDirectory); - } else { - - // ensure the content node was set back to its original - return ConfigXml.setConfigXml(appDirectory, { - resetContent: true, - errorWhenNotFound: false - }); } + + // ensure the content node was set back to its original + return ConfigXml.setConfigXml(appDirectory, { + resetContent: true, + errorWhenNotFound: false + }); }) .then(function(serveOptions) { var optionList = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawArgs); return cordovaUtils.execCordovaCommand(optionList, isLiveReload, serveOptions); }) + .then(function() { + if (isLiveReload && hasServeCommand && false) { + return npmScripts.runIonicScript('serve', ['-p', port, '--address', address]); + } + return Q(); + }) .catch(function(ex) { if (ex instanceof Error) { log.error(ex); From 01112e9a2c231c39a3de932ba1e205869da95c78 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 9 Nov 2016 14:54:25 -0600 Subject: [PATCH 1080/1100] chore(): update npm version. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 81b589f5a7..10ebf8e74a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.1.6", + "version": "2.1.7", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From 3fde6464af7f175ccd857fb35040c15a948c0b9c Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 10 Nov 2016 14:09:36 -0600 Subject: [PATCH 1081/1100] fix(): check that npm scripts exists before checking for properties. --- lib/cli.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli.js b/lib/cli.js index b8d7a2b564..c8682d5227 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -188,7 +188,7 @@ Cli.runr = function runr(processArgv) { log.debug('\nNpm scripts:', npmScripts); log.debug('Gulpfile found:', gulpLoaded, '\n'); - if (npmScripts.hasOwnProperty(taskName + ':before') || npmScripts.hasOwnProperty(taskName + ':after')) { + if (npmScripts && (npmScripts.hasOwnProperty(taskName + ':before') || npmScripts.hasOwnProperty(taskName + ':after'))) { return Cli.runWithNpmScripts(argv, task, rawCliArguments); } else if (gulpLoaded) { return Cli.runWithGulp(argv, task, rawCliArguments); From 9c5ebeda33feed75d8058fc785a57be035f0ac83 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 10 Nov 2016 16:59:21 -0600 Subject: [PATCH 1082/1100] fix(): ensure params are passed correctly to app-scripts --- lib/ionic/serve.js | 4 ++-- lib/utils/npmScripts.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 6febc53ac9..2c5412de33 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -59,7 +59,7 @@ var settings = { isProjectTask: true }; -function run(ionic, argv) { +function run(ionic, argv, rawCliArguments) { /** * Before running the internal server check to see if npmscripts has @@ -68,7 +68,7 @@ function run(ionic, argv) { return npmScripts.hasIonicScript('serve') .then(function(hasServeCommand) { if (hasServeCommand) { - return npmScripts.runIonicScript('serve', argv); + return npmScripts.runIonicScript('serve', rawCliArguments.slice(1)); } else { return runServer(argv); } diff --git a/lib/utils/npmScripts.js b/lib/utils/npmScripts.js index e538e5dd7c..a380326426 100644 --- a/lib/utils/npmScripts.js +++ b/lib/utils/npmScripts.js @@ -23,7 +23,7 @@ function runIonicScript(name, argv) { var scriptName = getScriptName(name); var q = Q.defer(); - var scriptSpawn = spawn('npm', ['run', scriptName].concat(argv || []), { stdio: 'inherit' }) + var scriptSpawn = spawn('npm', ['run', scriptName, '--'].concat(argv || []), { stdio: 'inherit' }) .on('error', function(err) { log.debug('Spawn command', scriptName, 'failed'); q.reject('Unable to run spawn command ' + err); From 8a6b55428640ff262cf9d933393bd314d7e06c81 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 10 Nov 2016 17:02:34 -0600 Subject: [PATCH 1083/1100] chore(): update changelog and package.json file. --- CHANGELOG.md | 11 +++++++++++ package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb0b28647d..4de1a513eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ + +## [2.1.8](https://github.com/driftyco/ionic-cli/compare/v2.1.7...v2.1.8) (2016-11-10) + + +### Bug Fixes + +* check that npm scripts exists before checking for properties. ([3fde646](https://github.com/driftyco/ionic-cli/commit/3fde646)) +* ensure params are passed correctly to app-scripts ([9c5ebed](https://github.com/driftyco/ionic-cli/commit/9c5ebed)) + + + ## [2.1.6](https://github.com/driftyco/ionic-cli/compare/v2.1.5...v2.1.6) (2016-11-09) diff --git a/package.json b/package.json index 10ebf8e74a..1870af4ea1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.1.7", + "version": "2.1.8", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From c34b3c0958d897a30ea832ae97342fdf2fdf09cf Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 14 Nov 2016 19:55:03 -0600 Subject: [PATCH 1084/1100] fix(): ensure run and emulate are delegating livereload and serve to app-scripts when applicable. --- lib/ionic/emulate.js | 21 ++++++++++----------- lib/ionic/run.js | 21 ++++++++++----------- lib/utils/cordova.js | 2 +- lib/utils/npmScripts.js | 14 +++++++++++++- 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/lib/ionic/emulate.js b/lib/ionic/emulate.js index b74ad7e596..bf3d29fbb5 100644 --- a/lib/ionic/emulate.js +++ b/lib/ionic/emulate.js @@ -80,19 +80,24 @@ function run(ionic, argv, rawCliArguments) { hasBuildCommand = results[2]; hasServeCommand = results[3]; - if (hasBuildCommand) { + if (hasBuildCommand && !(isLiveReload && hasServeCommand)) { return npmScripts.runIonicScript('build'); } return Q(); }) .then(function() { - if (isLiveReload && hasServeCommand && false) { + // If we are running livereload and are using "ionic:serve" app-script + // then configure devServer manually + if (isLiveReload && hasServeCommand) { // using app-scripts and livereload is requested - return ConfigXml.setConfigXml(process.cwd(), { - devServer: serveUtil.getUrl(address, port) - }); + return npmScripts.runIonicScript('serve', ['-p', port, '--address', address, '--nobrowser']) + .then(function() { + return ConfigXml.setConfigXml(process.cwd(), { + devServer: serveUtil.getUrl(address, port) + }); + }); } else if (isLiveReload) { // not an app-scripts project but the user wants livereload @@ -109,12 +114,6 @@ function run(ionic, argv, rawCliArguments) { var optionList = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawArgs); return cordovaUtils.execCordovaCommand(optionList, isLiveReload, serveOptions); }) - .then(function() { - if (isLiveReload && hasServeCommand && false) { - return npmScripts.runIonicScript('serve', ['-p', port, '--address', address]); - } - return Q(); - }) .catch(function(ex) { if (ex instanceof Error) { log.error(ex); diff --git a/lib/ionic/run.js b/lib/ionic/run.js index c22f87b989..1e8b1b046b 100644 --- a/lib/ionic/run.js +++ b/lib/ionic/run.js @@ -80,19 +80,24 @@ function run(ionic, argv, rawCliArguments) { hasBuildCommand = results[2]; hasServeCommand = results[3]; - if (hasBuildCommand) { + if (hasBuildCommand && !(isLiveReload && hasServeCommand)) { return npmScripts.runIonicScript('build'); } return Q(); }) .then(function() { - if (isLiveReload && hasServeCommand && false) { + // If we are running livereload and are using "ionic:serve" app-script + // then configure devServer manually + if (isLiveReload && hasServeCommand) { // using app-scripts and livereload is requested - return ConfigXml.setConfigXml(process.cwd(), { - devServer: serveUtil.getUrl(address, port) - }); + return npmScripts.runIonicScript('serve', ['-p', port, '--address', address, '--nobrowser']) + .then(function() { + return ConfigXml.setConfigXml(process.cwd(), { + devServer: serveUtil.getUrl(address, port) + }); + }); } else if (isLiveReload) { // not an app-scripts project but the user wants livereload @@ -109,12 +114,6 @@ function run(ionic, argv, rawCliArguments) { var optionList = cordovaUtils.filterArgumentsForCordova(cmdName, argv, rawArgs); return cordovaUtils.execCordovaCommand(optionList, isLiveReload, serveOptions); }) - .then(function() { - if (isLiveReload && hasServeCommand && false) { - return npmScripts.runIonicScript('serve', ['-p', port, '--address', address]); - } - return Q(); - }) .catch(function(ex) { if (ex instanceof Error) { log.error(ex); diff --git a/lib/utils/cordova.js b/lib/utils/cordova.js index 8cee6053d2..48fd2f33cb 100644 --- a/lib/utils/cordova.js +++ b/lib/utils/cordova.js @@ -93,7 +93,6 @@ function execCordovaCommand(optionList, isLiveReload, serveOptions) { if (isLiveReload) { cordovaProcess.on('exit', function() { - Serve.printCommandTips(serveOptions); setTimeout(function() { // set it back to the original src after a few seconds @@ -206,6 +205,7 @@ function filterArgumentsForCordova(cmdName, argv, rawCliArguments) { * * @param {Array} argv List of arguments * @param {String} baseDir The projects base directory + * @param {Obj} options options for live reload function * @return {Promise} Promise upon completion */ function setupLiveReload(argv, baseDir) { diff --git a/lib/utils/npmScripts.js b/lib/utils/npmScripts.js index a380326426..121b0fdfdd 100644 --- a/lib/utils/npmScripts.js +++ b/lib/utils/npmScripts.js @@ -5,6 +5,7 @@ var path = require('path'); var fs = require('fs'); var IonicAppLib = require('ionic-app-lib'); var log = IonicAppLib.logging.logger; +var DEV_SERVER_COMPLETION_STRING = 'dev server running'; function getScriptName(name) { return 'ionic:' + name; @@ -23,12 +24,23 @@ function runIonicScript(name, argv) { var scriptName = getScriptName(name); var q = Q.defer(); - var scriptSpawn = spawn('npm', ['run', scriptName, '--'].concat(argv || []), { stdio: 'inherit' }) + var scriptSpawn = spawn('npm', ['run', scriptName, '--', '--colors'].concat(argv || []), [process.stdin, 'pipe', process.stderr]) .on('error', function(err) { log.debug('Spawn command', scriptName, 'failed'); q.reject('Unable to run spawn command ' + err); }); + scriptSpawn.stdout.pipe(process.stdout); + + scriptSpawn.stdout.on('data', function(data) { + var dataLines = data.toString().split('\n').find(function(line) { + return line.indexOf(DEV_SERVER_COMPLETION_STRING) > -1; + }); + if (dataLines && dataLines.length > 0) { + return q.resolve(); + } + }); + scriptSpawn.on('exit', function(code) { log.debug('Spawn command', scriptName, 'completed'); if (code !== 0) { From 9667f3d211b08478f28418b400f57bd40eada8d4 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 14 Nov 2016 21:57:59 -0600 Subject: [PATCH 1085/1100] chore(): increment npm version and app-lib version. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 1870af4ea1..e70492c273 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.1.8", + "version": "2.1.9", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -69,7 +69,7 @@ "gulp": "3.8.8", "gulp-util": "3.0.7", "inquirer": "0.11.2", - "ionic-app-lib": "2.1.4", + "ionic-app-lib": "2.1.5", "moment": "2.11.1", "open": "0.0.5", "optimist": "0.6.0", From bfadd19ae8e5f9ed326651099fd415bacc15b2f6 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 14 Nov 2016 22:01:36 -0600 Subject: [PATCH 1086/1100] chore(): update changelog. --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4de1a513eb..3270ac6e2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ + +## [2.1.9](https://github.com/driftyco/ionic-cli/compare/v2.1.8...v2.1.9) (2016-11-15) + + +### Bug Fixes + +* ensure run and emulate are delegating livereload and serve to app-scripts when applicable. ([c34b3c0](https://github.com/driftyco/ionic-cli/commit/c34b3c0)) + + + ## [2.1.8](https://github.com/driftyco/ionic-cli/compare/v2.1.7...v2.1.8) (2016-11-10) From 70531bc3e26531c27ec2ab2fa2e75d9940894d69 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Tue, 15 Nov 2016 23:07:16 -0600 Subject: [PATCH 1087/1100] feat(emulate): ensure emulate and run are choosing an IP and port that are available. --- lib/ionic/emulate.js | 14 ++++-------- lib/ionic/run.js | 13 +++-------- lib/utils/cordova.js | 50 ++++++++++++++++++++++++++++++++++++++++- lib/utils/npmScripts.js | 10 +++++++-- 4 files changed, 64 insertions(+), 23 deletions(-) diff --git a/lib/ionic/emulate.js b/lib/ionic/emulate.js index bf3d29fbb5..7295d0158a 100644 --- a/lib/ionic/emulate.js +++ b/lib/ionic/emulate.js @@ -1,7 +1,6 @@ 'use strict'; var chalk = require('chalk'); -var serveUtil = require('../utils/serve'); var extend = require('../utils/extend'); var npmScripts = require('../utils/npmScripts'); var os = require('os'); @@ -49,8 +48,6 @@ function run(ionic, argv, rawCliArguments) { var cmdName = argv._[0].toLowerCase(); var hasBuildCommand = false; var hasServeCommand = false; - var address = argv.address || serveUtil.DEFAULT_ADDRESS; - var port = argv.port || serveUtil.DEFAULT_HTTP_PORT; var isLiveReload = argv.livereload || argv['live-reload'] || argv.l || false; @@ -58,7 +55,7 @@ function run(ionic, argv, rawCliArguments) { var runPlatform = argv._[1]; if (!runPlatform) { runPlatform = 'ios'; - rawArgs.push(runPlatform); + rawArgs.splice(1, 0, runPlatform); } if (runPlatform === 'ios' && os.platform() !== 'darwin') { @@ -92,12 +89,9 @@ function run(ionic, argv, rawCliArguments) { if (isLiveReload && hasServeCommand) { // using app-scripts and livereload is requested - return npmScripts.runIonicScript('serve', ['-p', port, '--address', address, '--nobrowser']) - .then(function() { - return ConfigXml.setConfigXml(process.cwd(), { - devServer: serveUtil.getUrl(address, port) - }); - }); + // Also remove commandName from the rawArgs passed + return cordovaUtils.startAppScriptsServer(argv); + } else if (isLiveReload) { // not an app-scripts project but the user wants livereload diff --git a/lib/ionic/run.js b/lib/ionic/run.js index 1e8b1b046b..ab052daf5c 100644 --- a/lib/ionic/run.js +++ b/lib/ionic/run.js @@ -1,7 +1,6 @@ 'use strict'; var chalk = require('chalk'); -var serveUtil = require('../utils/serve'); var extend = require('../utils/extend'); var npmScripts = require('../utils/npmScripts'); var os = require('os'); @@ -49,8 +48,6 @@ function run(ionic, argv, rawCliArguments) { var cmdName = argv._[0].toLowerCase(); var hasBuildCommand = false; var hasServeCommand = false; - var address = argv.address || serveUtil.DEFAULT_ADDRESS; - var port = argv.port || serveUtil.DEFAULT_HTTP_PORT; var isLiveReload = argv.livereload || argv['live-reload'] || argv.l || false; @@ -58,7 +55,7 @@ function run(ionic, argv, rawCliArguments) { var runPlatform = argv._[1]; if (!runPlatform) { runPlatform = 'ios'; - rawArgs.push(runPlatform); + rawArgs.splice(1, 0, runPlatform); } if (runPlatform === 'ios' && os.platform() !== 'darwin') { @@ -92,12 +89,8 @@ function run(ionic, argv, rawCliArguments) { if (isLiveReload && hasServeCommand) { // using app-scripts and livereload is requested - return npmScripts.runIonicScript('serve', ['-p', port, '--address', address, '--nobrowser']) - .then(function() { - return ConfigXml.setConfigXml(process.cwd(), { - devServer: serveUtil.getUrl(address, port) - }); - }); + // Also remove commandName from the rawArgs passed + return cordovaUtils.startAppScriptsServer(argv); } else if (isLiveReload) { // not an app-scripts project but the user wants livereload diff --git a/lib/utils/cordova.js b/lib/utils/cordova.js index 48fd2f33cb..52ac816577 100644 --- a/lib/utils/cordova.js +++ b/lib/utils/cordova.js @@ -13,6 +13,7 @@ var ConfigXml = IonicAppLib.configXml; var childProcess = require('child_process'); var childExec = childProcess.exec; var promiseExec = Q.denodeify(childExec); +var npmScripts = require('../utils/npmScripts'); /** * Returns true or false after checking if the platform exists @@ -266,6 +267,52 @@ function setupLiveReload(argv, baseDir) { }); } +/** + * Start the app scripts server for emulator or device + * + * @param {Number} port + * @param {String} address + * @return {Promise} Promise upon completion + */ +function startAppScriptsServer(argv) { + var options = _.extend(argv, { + runLivereload: true, + isPlatformServe: true + }); + + return Serve.getAddress(options) + .then(function() { + + // Check that the server port is available + return Serve.checkPorts(true, options.port, options.address, options); + }) + .then(function() { + + // Check that the liveReload port is available + return Serve.checkPorts(false, options.liveReloadPort, options.address, options); + }) + .then(function() { + + // Execute the serve command from app-scripts + // Also remove platform from the raw args passed + return npmScripts.runIonicScript('serve', + [ + '--port', options.port, + '--address', options.address, + '--liveReloadPort', options.liveReloadPort, + '--nobrowser' + ] + .concat(options.consolelogs ? '--consolelogs' : []) + .concat(options.serverlogs ? '--serverlogs' : []) + ); + }) + .then(function() { + return ConfigXml.setConfigXml(process.cwd(), { + devServer: Serve.host(options.address, options.port) + }); + }); +} + module.exports = { isPlatformInstalled: isPlatformInstalled, arePluginsInstalled: arePluginsInstalled, @@ -273,5 +320,6 @@ module.exports = { installPlugins: installPlugins, execCordovaCommand: execCordovaCommand, filterArgumentsForCordova: filterArgumentsForCordova, - setupLiveReload: setupLiveReload + setupLiveReload: setupLiveReload, + startAppScriptsServer: startAppScriptsServer }; diff --git a/lib/utils/npmScripts.js b/lib/utils/npmScripts.js index 121b0fdfdd..999c6b05d0 100644 --- a/lib/utils/npmScripts.js +++ b/lib/utils/npmScripts.js @@ -20,11 +20,12 @@ function hasIonicScript(name) { } function runIonicScript(name, argv) { - var spawn = require('cross-spawn-async'); + var spawn = require('cross-spawn-async'); var scriptName = getScriptName(name); var q = Q.defer(); - var scriptSpawn = spawn('npm', ['run', scriptName, '--', '--colors'].concat(argv || []), [process.stdin, 'pipe', process.stderr]) + process.env['FORCE_COLOR'] = true; + var scriptSpawn = spawn('npm', ['run', scriptName, '--'].concat(argv || []), [process.stdin, 'pipe', process.stderr]) .on('error', function(err) { log.debug('Spawn command', scriptName, 'failed'); q.reject('Unable to run spawn command ' + err); @@ -48,6 +49,11 @@ function runIonicScript(name, argv) { } return q.resolve(); }); + + // If this process ends ensure that we killed the spawned child + process.on('exit', function() { + scriptSpawn.kill(); + }); return q.promise; } From acce85307413b5292afb7e6a24fdd734a5f2afe0 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 16 Nov 2016 13:53:23 -0600 Subject: [PATCH 1088/1100] chore(): update npm version and changelog. --- CHANGELOG.md | 15 +++++++++++++++ package.json | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3270ac6e2c..2d0671f166 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ + +## [2.1.10](https://github.com/driftyco/ionic-cli/compare/v2.1.8...v2.1.10) (2016-11-16) + + +### Bug Fixes + +* ensure run and emulate are delegating livereload and serve to app-scripts when applicable. ([c34b3c0](https://github.com/driftyco/ionic-cli/commit/c34b3c0)) + + +### Features + +* **emulate:** ensure emulate and run are choosing an IP and port that are available. ([70531bc](https://github.com/driftyco/ionic-cli/commit/70531bc)) + + + ## [2.1.9](https://github.com/driftyco/ionic-cli/compare/v2.1.8...v2.1.9) (2016-11-15) diff --git a/package.json b/package.json index e70492c273..d1e5647c00 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.1.9", + "version": "2.1.10", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -69,7 +69,7 @@ "gulp": "3.8.8", "gulp-util": "3.0.7", "inquirer": "0.11.2", - "ionic-app-lib": "2.1.5", + "ionic-app-lib": "2.1.6", "moment": "2.11.1", "open": "0.0.5", "optimist": "0.6.0", From bdb35d84d5debb01b4d1e9fafe54cc92897e8957 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 16 Nov 2016 14:26:53 -0600 Subject: [PATCH 1089/1100] chore(): update app-lib dependency. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d1e5647c00..e0f1dcbaa5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.1.10", + "version": "2.1.11", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", @@ -69,7 +69,7 @@ "gulp": "3.8.8", "gulp-util": "3.0.7", "inquirer": "0.11.2", - "ionic-app-lib": "2.1.6", + "ionic-app-lib": "2.1.7", "moment": "2.11.1", "open": "0.0.5", "optimist": "0.6.0", From 768733771488bdc8d5d5354bc64ba94329c3ee2a Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 17 Nov 2016 10:04:55 -0600 Subject: [PATCH 1090/1100] fix(): when spawning npmScripts ensure that stderr is piped correctly --- lib/utils/npmScripts.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/utils/npmScripts.js b/lib/utils/npmScripts.js index 999c6b05d0..5b786a01aa 100644 --- a/lib/utils/npmScripts.js +++ b/lib/utils/npmScripts.js @@ -25,7 +25,9 @@ function runIonicScript(name, argv) { var q = Q.defer(); process.env['FORCE_COLOR'] = true; - var scriptSpawn = spawn('npm', ['run', scriptName, '--'].concat(argv || []), [process.stdin, 'pipe', process.stderr]) + var scriptSpawn = spawn('npm', ['run', scriptName, '--'].concat(argv || []), { + stdio: [process.stdin, 'pipe', process.stderr] + }) .on('error', function(err) { log.debug('Spawn command', scriptName, 'failed'); q.reject('Unable to run spawn command ' + err); From 1a3de90787c0b4a8e24a22726e1d4f1a2921dd42 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 17 Nov 2016 10:06:42 -0600 Subject: [PATCH 1091/1100] chore(): increment npm script and update changelog. --- CHANGELOG.md | 10 ++++++++++ package.json | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d0671f166..8e3f49077c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ + +## [2.1.12](https://github.com/driftyco/ionic-cli/compare/v2.1.11...v2.1.12) (2016-11-17) + + +### Bug Fixes + +* when spawning npmScripts ensure that stderr is piped correctly ([7687337](https://github.com/driftyco/ionic-cli/commit/7687337)) + + + ## [2.1.10](https://github.com/driftyco/ionic-cli/compare/v2.1.8...v2.1.10) (2016-11-16) diff --git a/package.json b/package.json index e0f1dcbaa5..e0efe32ad8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.1.11", + "version": "2.1.12", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From ae6d904f3c58b959515fd63c3208f098c85d6775 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Wed, 23 Nov 2016 15:31:23 -0600 Subject: [PATCH 1092/1100] fix(cordova): maxBuffer was too small --- lib/utils/cordova.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/utils/cordova.js b/lib/utils/cordova.js index 52ac816577..02cf863e88 100644 --- a/lib/utils/cordova.js +++ b/lib/utils/cordova.js @@ -72,7 +72,9 @@ function execCordovaCommand(optionList, isLiveReload, serveOptions) { isLiveReload = !!isLiveReload; log.debug('Executing cordova cli: ' + optionList.join(' ')); - var cordovaProcess = childProcess.exec('cordova ' + optionList.join(' ')); + var cordovaProcess = childProcess.exec('cordova ' + optionList.join(' '), { + maxBuffer: 1024 * 1024 * 1024 + }); cordovaProcess.stdout.on('data', function(data) { log.info(data.toString()); From 57e4ad49040d6984217e085f3df1628e17f15633 Mon Sep 17 00:00:00 2001 From: Tim Lancina Date: Wed, 23 Nov 2016 15:51:42 -0600 Subject: [PATCH 1093/1100] chore(): update npm version and changelog --- CHANGELOG.md | 10 ++++++++++ package.json | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e3f49077c..cadf0bd1ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ + +## [2.1.13](https://github.com/driftyco/ionic-cli/compare/v2.1.12...v2.1.13) (2016-11-23) + + +### Bug Fixes + +* **cordova:** maxBuffer was too small ([ae6d904](https://github.com/driftyco/ionic-cli/commit/ae6d904)) + + + ## [2.1.12](https://github.com/driftyco/ionic-cli/compare/v2.1.11...v2.1.12) (2016-11-17) diff --git a/package.json b/package.json index e0efe32ad8..32d37b13b9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.1.12", + "version": "2.1.13", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From cdf2ed3b5d95a95e4f2ee64405136e76dbdb2b29 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Mon, 28 Nov 2016 22:30:52 -0600 Subject: [PATCH 1094/1100] chore(): replace exec with spawn and ensure all tests pass. --- lib/cli.js | 4 +- lib/utils/cordova.js | 106 +++++++++++++++++++++++++------------ lib/utils/npmScripts.js | 4 +- package.json | 4 +- spec/tasks/emulate.spec.js | 11 ++-- spec/tasks/run.spec.js | 11 ++-- spec/tasks/serve.spec.js | 4 +- spec/utils/cordova.spec.js | 104 ++++++++++++++++++++---------------- 8 files changed, 154 insertions(+), 94 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index c8682d5227..71f711698b 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -332,9 +332,9 @@ Cli.runNpmHook = function runNpmHook(hook) { process.env['FORCE_COLOR'] = true; var q = Q.defer(); - var spawn = require('cross-spawn-async'); + var crossSpawn = require('cross-spawn'); - var spawned = spawn('npm', args, { stdio: ['pipe', 'pipe', process.stderr] }); + var spawned = crossSpawn.spawn('npm', args, { stdio: ['pipe', 'pipe', process.stderr] }); spawned.on('error', function(err) { log.error('Unable to run spawn command ' + err); }); diff --git a/lib/utils/cordova.js b/lib/utils/cordova.js index 02cf863e88..2010a8962e 100644 --- a/lib/utils/cordova.js +++ b/lib/utils/cordova.js @@ -10,11 +10,38 @@ var Serve = IonicAppLib.serve; var Project = IonicAppLib.project; var log = IonicAppLib.logging.logger; var ConfigXml = IonicAppLib.configXml; -var childProcess = require('child_process'); -var childExec = childProcess.exec; -var promiseExec = Q.denodeify(childExec); +var crossSpawn = require('cross-spawn'); var npmScripts = require('../utils/npmScripts'); + +function promiseSpawn(cmd, args) { + var deferred = Q.defer(); + var info = ''; + + try { + var proc = crossSpawn.spawn(cmd, args); + + proc.stdout.on('data', function(data) { + info += data.toString('utf8'); + }); + + proc.on('error', function(error) { + deferred.reject(error); + }); + + proc.on('close', function(code) { + if (code !== 0) { + return deferred.reject(code); + } + deferred.resolve(info.replace('\n', ' ')); + }); + } catch (e) { + return deferred.reject(e); + } + + return deferred.promise; +} + /** * Returns true or false after checking if the platform exists * Synchronous @@ -24,14 +51,21 @@ var npmScripts = require('../utils/npmScripts'); * @return {Boolean} True if platform is installed */ function isPlatformInstalled(platform, baseDir) { + var deferred = Q.defer(); var platformPath = path.join(baseDir, 'platforms', platform); try { - fs.statSync(platformPath); - return true; + fs.stat(platformPath, function(err) { + if (err) { + return deferred.resolve(false); + } + return deferred.resolve(true); + }); } catch (ex) { - return false; + deferred.resolve(false); } + + return deferred.promise; } /** @@ -42,14 +76,42 @@ function isPlatformInstalled(platform, baseDir) { * @return {Boolean} True if any plugin is installed */ function arePluginsInstalled(baseDir) { + var deferred = Q.defer(); var pluginPath = path.join(baseDir, 'plugins'); try { - fs.statSync(pluginPath); - return true; + fs.stat(pluginPath, function(err) { + if (err) { + return deferred.resolve(false); + } + return deferred.resolve(true); + }); } catch (ex) { - return false; + deferred.resolve(false); } + + return deferred.promise; +} + +/** + * Install ionic required plugins + * + * @return {Promise} Promise upon completion + */ +function installPlugins() { + var plugins = [ + 'cordova-plugin-device', + 'cordova-plugin-console', + 'cordova-plugin-whitelist', + 'cordova-plugin-splashscreen', + 'cordova-plugin-statusbar', + 'ionic-plugin-keyboard' + ]; + + return Q.all(plugins.map(function(plugin) { + log.info(['Installing ', plugin].join('')); + return promiseSpawn('cordova', ['plugin', 'add', '--save', plugin]); + })); } /** @@ -62,7 +124,7 @@ function installPlatform(platform) { log.info(chalk.yellow('• You\'re trying to build for ' + platform + ' but don\'t have the platform installed yet.')); log.info('∆ Installing ' + platform + ' for you.'); - return promiseExec('cordova platform add ' + platform).then(function() { + return promiseSpawn('cordova', ['platform', 'add', platform]).then(function() { log.info('√ Installed platform ' + platform); }); } @@ -72,9 +134,7 @@ function execCordovaCommand(optionList, isLiveReload, serveOptions) { isLiveReload = !!isLiveReload; log.debug('Executing cordova cli: ' + optionList.join(' ')); - var cordovaProcess = childProcess.exec('cordova ' + optionList.join(' '), { - maxBuffer: 1024 * 1024 * 1024 - }); + var cordovaProcess = crossSpawn.spawn('cordova', optionList); cordovaProcess.stdout.on('data', function(data) { log.info(data.toString()); @@ -137,26 +197,6 @@ function execCordovaCommand(optionList, isLiveReload, serveOptions) { return deferred.promise; } -/** - * Install ionic required plugins - * - * @return {Promise} Promise upon completion - */ -function installPlugins() { - var plugins = [ - 'cordova-plugin-device', - 'cordova-plugin-console', - 'cordova-plugin-whitelist', - 'cordova-plugin-splashscreen', - 'cordova-plugin-statusbar', - 'ionic-plugin-keyboard' - ]; - - return Q.all(plugins.map(function(plugin) { - log.info(['Installing ', plugin].join('')); - return promiseExec('cordova plugin add --save ' + plugin); - })); -} /** * Filter and gather arguments from command line to be passed to Cordova diff --git a/lib/utils/npmScripts.js b/lib/utils/npmScripts.js index 5b786a01aa..728a8a385e 100644 --- a/lib/utils/npmScripts.js +++ b/lib/utils/npmScripts.js @@ -20,12 +20,12 @@ function hasIonicScript(name) { } function runIonicScript(name, argv) { - var spawn = require('cross-spawn-async'); + var crossSpawn = require('cross-spawn'); var scriptName = getScriptName(name); var q = Q.defer(); process.env['FORCE_COLOR'] = true; - var scriptSpawn = spawn('npm', ['run', scriptName, '--'].concat(argv || []), { + var scriptSpawn = crossSpawn.spawn('npm', ['run', scriptName, '--'].concat(argv || []), { stdio: [process.stdin, 'pipe', process.stderr] }) .on('error', function(err) { diff --git a/package.json b/package.json index 32d37b13b9..320f7181cc 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "@ionic/app-generators": "0.0.3", "chalk": "^1.1.3", "cli-table": "0.3.1", - "cross-spawn-async": "2.1.9", + "cross-spawn": "^5.0.1", "expand-tilde": "1.2.0", "form-data": "0.1.4", "gulp": "3.8.8", @@ -98,7 +98,7 @@ "bundledDependencies": [ "cli-table", "chalk", - "cross-spawn-async", + "cross-spawn", "expand-tilde", "form-data", "gulp", diff --git a/spec/tasks/emulate.spec.js b/spec/tasks/emulate.spec.js index 36ce8aeecb..85d8bec5cc 100644 --- a/spec/tasks/emulate.spec.js +++ b/spec/tasks/emulate.spec.js @@ -186,7 +186,7 @@ describe('emulate command', function() { spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); emulate.run(null, argv, rawCliArguments).then(function() { - expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['emulate', '-n', 'ios'], false, true); + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['emulate', 'ios', '-n'], false, true); done(); }); }); @@ -319,8 +319,13 @@ describe('emulate command', function() { emulate.run(null, argv, rawCliArguments).then(function() { expect(npmScripts.hasIonicScript).toHaveBeenCalledWith('build'); - expect(npmScripts.runIonicScript).toHaveBeenCalledWith('serve', ['-p', jasmine.any(Number), '--address', jasmine.any(String)]); - expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['emulate', 'android'], false, true); + expect(npmScripts.runIonicScript).toHaveBeenCalledWith('serve', [ + '--port', jasmine.any(Number), + '--address', jasmine.any(String), + '--liveReloadPort', jasmine.any(Number), + '--nobrowser' + ]); + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['emulate', 'android'], true, true); done(); }).catch(function(e) { console.log(e); diff --git a/spec/tasks/run.spec.js b/spec/tasks/run.spec.js index 40aa3e22a3..ebd2325f2c 100644 --- a/spec/tasks/run.spec.js +++ b/spec/tasks/run.spec.js @@ -157,7 +157,7 @@ describe('run command', function() { spyOn(cordovaUtils, 'execCordovaCommand').andReturn(Q(true)); run.run(null, argv, rawCliArguments).then(function() { - expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['run', '-n', 'ios'], false, true); + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['run', 'ios', '-n'], false, true); done(); }); }); @@ -314,8 +314,13 @@ describe('run command', function() { run.run(null, argv, rawCliArguments).then(function() { expect(npmScripts.hasIonicScript).toHaveBeenCalledWith('build'); - expect(npmScripts.runIonicScript).toHaveBeenCalledWith('serve', ['-p', jasmine.any(Number), '--address', jasmine.any(String)]); - expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['run', 'android'], false, true); + expect(npmScripts.runIonicScript).toHaveBeenCalledWith('serve', [ + '--port', jasmine.any(Number), + '--address', jasmine.any(String), + '--liveReloadPort', jasmine.any(Number), + '--nobrowser' + ]); + expect(cordovaUtils.execCordovaCommand).toHaveBeenCalledWith(['run', 'android'], true, true); done(); }).catch(function(e) { console.log(e); diff --git a/spec/tasks/serve.spec.js b/spec/tasks/serve.spec.js index e3f53220eb..35bdbe365a 100644 --- a/spec/tasks/serve.spec.js +++ b/spec/tasks/serve.spec.js @@ -283,9 +283,9 @@ describe('Serve', function() { spyOn(npmScripts, 'hasIonicScript').andReturn(Q(true)); spyOn(npmScripts, 'runIonicScript').andReturn(Q(true)); - serveTask.run({}, argv).then(function() { + serveTask.run({}, argv, rawCliArguments).then(function() { expect(npmScripts.hasIonicScript).toHaveBeenCalledWith('serve'); - expect(npmScripts.runIonicScript).toHaveBeenCalledWith('serve', argv); + expect(npmScripts.runIonicScript).toHaveBeenCalledWith('serve', rawCliArguments.slice(1)); done(); }); }); diff --git a/spec/utils/cordova.spec.js b/spec/utils/cordova.spec.js index f3641e51a7..b87db6e729 100644 --- a/spec/utils/cordova.spec.js +++ b/spec/utils/cordova.spec.js @@ -3,60 +3,64 @@ var fs = require('fs'); var Q = require('q'); var rewire = require('rewire'); -var childProcess = require('child_process'); var cordovaUtils = rewire('../../lib/utils/cordova'); var optimist = require('optimist'); var IonicAppLib = require('ionic-app-lib'); var Project = require('ionic-app-lib').project; var Serve = require('ionic-app-lib').serve; +var crossSpawn = require('cross-spawn'); var ConfigXml = require('ionic-app-lib').configXml; var log = IonicAppLib.logging.logger; describe('isPlatformInstalled', function() { - it('should return true if the platform directory does exist', function() { - spyOn(fs, 'statSync').andCallFake(function() { - return; + it('should return true if the platform directory does exist', function(done) { + spyOn(fs, 'stat').andCallFake(function(param, callback) { + callback(null); }); - var result = cordovaUtils.isPlatformInstalled('ios', '/tmp'); - - expect(result).toEqual(true); - expect(fs.statSync).toHaveBeenCalledWith('/tmp/platforms/ios'); + cordovaUtils.isPlatformInstalled('ios', '/tmp').then(function(result) { + expect(result).toEqual(true); + expect(fs.stat).toHaveBeenCalledWith('/tmp/platforms/ios', jasmine.any(Function)); + done(); + }); }); - it('should return false if the platform directory does not exist', function() { - spyOn(fs, 'statSync').andCallFake(function() { + it('should return false if the platform directory does not exist', function(done) { + spyOn(fs, 'stat').andCallFake(function() { throw new Error('Dir does not exist'); }); - var result = cordovaUtils.isPlatformInstalled('ios', '/tmp'); - - expect(result).toEqual(false); - expect(fs.statSync).toHaveBeenCalledWith('/tmp/platforms/ios'); + cordovaUtils.isPlatformInstalled('ios', '/tmp').then(function(result) { + expect(result).toEqual(false); + expect(fs.stat).toHaveBeenCalledWith('/tmp/platforms/ios', jasmine.any(Function)); + done(); + }); }); }); describe('arePluginsInstalled', function() { - it('should return true if the plugins directory does exist', function() { - spyOn(fs, 'statSync').andCallFake(function() { - return; + it('should return true if the plugins directory does exist', function(done) { + spyOn(fs, 'stat').andCallFake(function(param, callback) { + callback(null); }); - var result = cordovaUtils.arePluginsInstalled('/tmp'); - - expect(result).toEqual(true); - expect(fs.statSync).toHaveBeenCalledWith('/tmp/plugins'); + cordovaUtils.arePluginsInstalled('/tmp').then(function(result) { + expect(fs.stat).toHaveBeenCalledWith('/tmp/plugins', jasmine.any(Function)); + expect(result).toEqual(true); + done(); + }); }); - it('should return false if the plugins directory does not exist', function() { - spyOn(fs, 'statSync').andCallFake(function() { + it('should return false if the plugins directory does not exist', function(done) { + spyOn(fs, 'stat').andCallFake(function() { throw new Error('Dir does not exist'); }); - var result = cordovaUtils.arePluginsInstalled('/tmp'); - - expect(result).toEqual(false); - expect(fs.statSync).toHaveBeenCalledWith('/tmp/plugins'); + cordovaUtils.arePluginsInstalled('/tmp').then(function(result) { + expect(result).toEqual(false); + expect(fs.stat).toHaveBeenCalledWith('/tmp/plugins', jasmine.any(Function)); + done(); + }); }); }); @@ -65,16 +69,16 @@ describe('installPlatform', function() { spyOn(log, 'info'); }); - it('should call promiseExec', function(done) { + it('should call promiseSpawn', function(done) { var installPlatform = cordovaUtils.__get__('installPlatform'); - var promiseExecSpy = jasmine.createSpy('promiseExec'); - promiseExecSpy.andReturn(Q(true)); - var revertPromiseExecSpy = cordovaUtils.__set__('promiseExec', promiseExecSpy); + var promiseSpawnSpy = jasmine.createSpy('promiseSpawn'); + promiseSpawnSpy.andReturn(Q(true)); + var revertPromiseSpawnSpy = cordovaUtils.__set__('promiseSpawn', promiseSpawnSpy); installPlatform('ios').then(function() { - expect(promiseExecSpy).toHaveBeenCalledWith('cordova platform add ios'); - revertPromiseExecSpy(); + expect(promiseSpawnSpy).toHaveBeenCalledWith('cordova', ['platform', 'add', 'ios']); + revertPromiseSpawnSpy(); done(); }); }); @@ -85,20 +89,26 @@ describe('installPlugins', function() { spyOn(log, 'info'); }); - it('should call promiseExec', function(done) { + it('should call promiseSpawn', function(done) { var installPlugins = cordovaUtils.__get__('installPlugins'); - var promiseExecSpy = jasmine.createSpy('promiseExec'); - promiseExecSpy.andReturn(Q(true)); - var revertPromiseExecSpy = cordovaUtils.__set__('promiseExec', promiseExecSpy); + var promiseSpawnSpy = jasmine.createSpy('promiseSpawn'); + promiseSpawnSpy.andReturn(Q(true)); + var revertPromiseSpawnSpy = cordovaUtils.__set__('promiseSpawn', promiseSpawnSpy); installPlugins().then(function() { - expect(promiseExecSpy.calls[0].args[0]).toEqual('cordova plugin add --save cordova-plugin-device'); - expect(promiseExecSpy.calls[1].args[0]).toEqual('cordova plugin add --save cordova-plugin-console'); - expect(promiseExecSpy.calls[2].args[0]).toEqual('cordova plugin add --save cordova-plugin-whitelist'); - expect(promiseExecSpy.calls[3].args[0]).toEqual('cordova plugin add --save cordova-plugin-splashscreen'); - expect(promiseExecSpy.calls[4].args[0]).toEqual('cordova plugin add --save cordova-plugin-statusbar'); - expect(promiseExecSpy.calls[5].args[0]).toEqual('cordova plugin add --save ionic-plugin-keyboard'); - revertPromiseExecSpy(); + expect(promiseSpawnSpy.calls[0].args) + .toEqual(['cordova', ['plugin', 'add', '--save', 'cordova-plugin-device']]); + expect(promiseSpawnSpy.calls[1].args) + .toEqual(['cordova', ['plugin', 'add', '--save', 'cordova-plugin-console']]); + expect(promiseSpawnSpy.calls[2].args) + .toEqual(['cordova', ['plugin', 'add', '--save', 'cordova-plugin-whitelist']]); + expect(promiseSpawnSpy.calls[3].args) + .toEqual(['cordova', ['plugin', 'add', '--save', 'cordova-plugin-splashscreen']]); + expect(promiseSpawnSpy.calls[4].args) + .toEqual(['cordova', ['plugin', 'add', '--save', 'cordova-plugin-statusbar']]); + expect(promiseSpawnSpy.calls[5].args) + .toEqual(['cordova', ['plugin', 'add', '--save', 'ionic-plugin-keyboard']]); + revertPromiseSpawnSpy(); done(); }); }); @@ -277,7 +287,7 @@ describe('setupLiveReload', function() { describe('execCordovaCommand', function() { beforeEach(function() { - spyOn(childProcess, 'exec').andCallThrough(); + spyOn(crossSpawn, 'spawn').andCallThrough(); }); it('should execute the command against the cordova util', function(done) { @@ -290,7 +300,7 @@ describe('execCordovaCommand', function() { var serveOptions = {}; cordovaUtils.execCordovaCommand(optionList, isLiveReload, serveOptions).catch(function() { - expect(childProcess.exec).toHaveBeenCalledWith('cordova build -n ios'); + expect(crossSpawn.spawn).toHaveBeenCalledWith('cordova', ['build', '-n', 'ios']); done(); }); }); @@ -304,7 +314,7 @@ describe('execCordovaCommand', function() { var serveOptions = {}; cordovaUtils.execCordovaCommand(optionList, isLiveReload, serveOptions).catch(function() { - expect(childProcess.exec).toHaveBeenCalledWith('cordova build android'); + expect(crossSpawn.spawn).toHaveBeenCalledWith('cordova', ['build', 'android']); done(); }); }); From fb53608d41ac5f70c2ce166d03bbd206e2fb2387 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Tue, 29 Nov 2016 14:04:35 -0600 Subject: [PATCH 1095/1100] fix(run): add flag so that serve has info on when it is serving a cordova app. --- lib/utils/cordova.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/utils/cordova.js b/lib/utils/cordova.js index 2010a8962e..ae28e63622 100644 --- a/lib/utils/cordova.js +++ b/lib/utils/cordova.js @@ -342,6 +342,7 @@ function startAppScriptsServer(argv) { '--port', options.port, '--address', options.address, '--liveReloadPort', options.liveReloadPort, + '--iscordovaserve', '--nobrowser' ] .concat(options.consolelogs ? '--consolelogs' : []) From 044171c988c41cd3f6b39b11a0a954febb031ca4 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Wed, 30 Nov 2016 00:26:23 -0600 Subject: [PATCH 1096/1100] chore(): update package.json version. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 320f7181cc..ea77a28bfb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.1.13", + "version": "2.1.14", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From f834ccacd34c9ceaf27398cb6c209b6d7583255a Mon Sep 17 00:00:00 2001 From: Justin Willis Date: Wed, 30 Nov 2016 09:39:12 -0600 Subject: [PATCH 1097/1100] fix(start): getting started url (#1738) --- lib/ionic/start.js | 2 +- spec/tasks/start.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ionic/start.js b/lib/ionic/start.js index b49598a49e..9ea8d364b9 100644 --- a/lib/ionic/start.js +++ b/lib/ionic/start.js @@ -102,7 +102,7 @@ function run(ionic, argv) { }) .then(function() { if (options.v2 && startingApp) { - log.info('\nNew to Ionic? Get started here: http://ionicframework.com/docs/v2/getting-started\n'); + log.info('\nNew to Ionic? Get started here: http://ionicframework.com/getting-started\n'); } }) .catch(function(error) { diff --git a/spec/tasks/start.spec.js b/spec/tasks/start.spec.js index 109f476bc0..fcc24c795c 100644 --- a/spec/tasks/start.spec.js +++ b/spec/tasks/start.spec.js @@ -190,7 +190,7 @@ describe('start command', function() { start.run({}, argv).then(function() { expect(appLibUtils.preprocessCliOptions.calls[0].args[0].v2).toEqual(true); expect(log.info).toHaveBeenCalledWith( - '\nNew to Ionic? Get started here: http://ionicframework.com/docs/v2/getting-started\n' + '\nNew to Ionic? Get started here: http://ionicframework.com/getting-started\n' ); done(); }); From 68eb3e53f4b24775fe8957f1bb2f080cde88565c Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Tue, 6 Dec 2016 10:16:03 -0600 Subject: [PATCH 1098/1100] fix(run): update run and emulate to pass additional options on to app-scripts. (#1735) --- lib/ionic/emulate.js | 2 +- lib/ionic/run.js | 2 +- lib/utils/cordova.js | 60 ++++++++++++++++++++++--- spec/utils/cordova.spec.js | 91 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+), 9 deletions(-) diff --git a/lib/ionic/emulate.js b/lib/ionic/emulate.js index 7295d0158a..447c2dbc5a 100644 --- a/lib/ionic/emulate.js +++ b/lib/ionic/emulate.js @@ -78,7 +78,7 @@ function run(ionic, argv, rawCliArguments) { hasServeCommand = results[3]; if (hasBuildCommand && !(isLiveReload && hasServeCommand)) { - return npmScripts.runIonicScript('build'); + return npmScripts.runIonicScript('build', rawArgs.slice(2)); } return Q(); }) diff --git a/lib/ionic/run.js b/lib/ionic/run.js index ab052daf5c..35c21e79e7 100644 --- a/lib/ionic/run.js +++ b/lib/ionic/run.js @@ -78,7 +78,7 @@ function run(ionic, argv, rawCliArguments) { hasServeCommand = results[3]; if (hasBuildCommand && !(isLiveReload && hasServeCommand)) { - return npmScripts.runIonicScript('build'); + return npmScripts.runIonicScript('build', rawArgs.slice(2)); } return Q(); }) diff --git a/lib/utils/cordova.js b/lib/utils/cordova.js index ae28e63622..e6d546ad29 100644 --- a/lib/utils/cordova.js +++ b/lib/utils/cordova.js @@ -321,6 +321,11 @@ function startAppScriptsServer(argv) { runLivereload: true, isPlatformServe: true }); + options = consolidateOptions(['port', 'p'], options); + options = consolidateOptions(['liveReloadPort', 'r'], options); + options = consolidateOptions(['consolelogs', 'c'], options); + options = consolidateOptions(['serverlogs', 's'], options); + options = consolidateOptions(['livereload', 'l'], options); return Serve.getAddress(options) .then(function() { @@ -338,15 +343,13 @@ function startAppScriptsServer(argv) { // Execute the serve command from app-scripts // Also remove platform from the raw args passed return npmScripts.runIonicScript('serve', - [ - '--port', options.port, - '--address', options.address, - '--liveReloadPort', options.liveReloadPort, + optionsToArray(options) + + // Serve specific options not related to the actual run or emulate code + .concat([ '--iscordovaserve', '--nobrowser' - ] - .concat(options.consolelogs ? '--consolelogs' : []) - .concat(options.serverlogs ? '--serverlogs' : []) + ]) ); }) .then(function() { @@ -356,6 +359,49 @@ function startAppScriptsServer(argv) { }); } +/** + * Consolidate a list of options based on those passed to it. + * + * @param {Array} list of option names. Consolidate will be to the first array element name + * @param {Object} key/value object that is to be used as the source of consolidation + * @return {Object} key/value object with consolidated options + */ +function consolidateOptions(list, options) { + var newOptions = _.extend({}, options); + + list.forEach(function(optionName) { + delete newOptions[optionName]; + }); + + var value = list.reduce(function(final, optionName) { + if (options.hasOwnProperty(optionName) && !!options[optionName]) { + final = options[optionName]; + } + return final; + }, null); + + if (value !== null) { + newOptions[list[0]] = value; + } + + return newOptions; +} + +function optionsToArray(options) { + return Object.keys(options) + .filter(function(itemName) { + return itemName !== '_' && itemName.indexOf('$') !== 0; + }) + .reduce(function(array, optionName) { + if (typeof options[optionName] === 'boolean' && options[optionName]) { + array.push('--' + optionName); + } else if (typeof options[optionName] !== 'boolean') { + array.push('--' + optionName, options[optionName]); + } + return array; + }, []); +} + module.exports = { isPlatformInstalled: isPlatformInstalled, arePluginsInstalled: arePluginsInstalled, diff --git a/spec/utils/cordova.spec.js b/spec/utils/cordova.spec.js index b87db6e729..8e5b477af8 100644 --- a/spec/utils/cordova.spec.js +++ b/spec/utils/cordova.spec.js @@ -423,3 +423,94 @@ describe('filterArgumentsForCordova', function() { }); }); +describe('consolidate options', function() { + var consolidateOptions = cordovaUtils.__get__('consolidateOptions'); + + it('should consolidate options to the first array element', function() { + var options = { + _: ['run', 'ios'], + consolelogs: false, + c: true, + serverlogs: false, + s: true, + debug: false, + release: false, + l: true, + p: 8100, + r: 6000, + '$0': 'ionic', + v2: true, + runLivereload: true, + isPlatformServe: true + }; + var results = consolidateOptions(['port', 'p'], options); + expect(results).toEqual({ + _: ['run', 'ios'], + consolelogs: false, + c: true, + serverlogs: false, + s: true, + debug: false, + release: false, + l: true, + port: 8100, + r: 6000, + '$0': 'ionic', + v2: true, + runLivereload: true, + isPlatformServe: true + }); + }); + + it('should consolidate duplicates', function() { + var options = { + _: ['run', 'ios'], + consolelogs: false, + c: true, + serverlogs: false, + s: true, + debug: false, + release: false, + l: true, + p: 8100, + port: 8100, + r: 6000, + '$0': 'ionic', + v2: true, + runLivereload: true, + isPlatformServe: true + }; + var results = consolidateOptions(['port', 'p'], options); + expect(results).toEqual({ + _: ['run', 'ios'], + consolelogs: false, + c: true, + serverlogs: false, + s: true, + debug: false, + release: false, + l: true, + port: 8100, + r: 6000, + '$0': 'ionic', + v2: true, + runLivereload: true, + isPlatformServe: true + }); + }); +}); + +describe('optionsToArray options', function() { + var optionsToArray = cordovaUtils.__get__('optionsToArray'); + + it('should convert argv options into an array', function() { + var processArguments = ['node', 'ionic', 'run', 'ios', '--port', '8100', '--livereload']; + var rawCliArguments = processArguments.slice(2); + var argv = optimist(rawCliArguments).argv; + var results = optionsToArray(argv); + expect(results).toEqual([ + '--port', 8100, + '--livereload', + ]); + }); +}); From d5a00692cfdece8b2f43dbef6a7ddc655ddda2af Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Tue, 6 Dec 2016 10:24:32 -0600 Subject: [PATCH 1099/1100] chore(): increment version and update changelog. --- CHANGELOG.md | 11 +++++++++++ package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cadf0bd1ba..3b61e8d308 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ + +## [2.1.15](https://github.com/driftyco/ionic-cli/compare/v2.1.14...v2.1.15) (2016-12-06) + + +### Bug Fixes + +* **run:** update run and emulate to pass additional options on to app-scripts. ([#1735](https://github.com/driftyco/ionic-cli/issues/1735)) ([68eb3e5](https://github.com/driftyco/ionic-cli/commit/68eb3e5)) +* **start:** getting started url ([#1738](https://github.com/driftyco/ionic-cli/issues/1738)) ([f834cca](https://github.com/driftyco/ionic-cli/commit/f834cca)) + + + ## [2.1.13](https://github.com/driftyco/ionic-cli/compare/v2.1.12...v2.1.13) (2016-11-23) diff --git a/package.json b/package.json index ea77a28bfb..845e76fb6f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ionic", - "version": "2.1.14", + "version": "2.1.15", "preferGlobal": true, "description": "A tool for creating and developing Ionic Framework mobile apps.", "homepage": "http://ionicframework.com/", From ae718c0d1db262b75c7c58f2cfad2f14bf17793d Mon Sep 17 00:00:00 2001 From: robustwangace Date: Wed, 7 Dec 2016 23:49:07 +0800 Subject: [PATCH 1100/1100] Add feature to disable cordova mock by command line --- lib/ionic/serve.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/ionic/serve.js b/lib/ionic/serve.js index 2c5412de33..ac4ef23bcf 100644 --- a/lib/ionic/serve.js +++ b/lib/ionic/serve.js @@ -54,6 +54,10 @@ var settings = { title: 'Disable running gulp during serve', boolean: true }, + '--nocordovamock|-m': { + title: 'Disable cordova mock', + boolean: true + }, '--platform|-t': 'Start serve with a specific platform (ios/android)' }, isProjectTask: true @@ -62,7 +66,7 @@ var settings = { function run(ionic, argv, rawCliArguments) { /** - * Before running the internal server check to see if npmscripts has + * Before running the internal server check to see if npmscripts has * a ionic specific serve command. If so then run it instead. */ return npmScripts.hasIonicScript('serve')