diff --git a/bin/check_reqs b/bin/check_reqs index 4a8abee4..2ac87521 100755 --- a/bin/check_reqs +++ b/bin/check_reqs @@ -21,7 +21,8 @@ var check_reqs = require('./lib/check_reqs'); -if(!check_reqs.run()) { - process.exit(2); -} +check_reqs.run().done(null, function(err) { + console.log(err); + process.exit(2); +}); diff --git a/bin/create b/bin/create index 020bf862..9ab6689b 100755 --- a/bin/create +++ b/bin/create @@ -31,6 +31,9 @@ if(args.length < 3 || (args[2] == '--help' || args[2] == '/?' || args[2] == '-h' console.log(' : Project name'); process.exit(1); } else { - create.createProject(args[2], args[3], args[4], args[5]); + create.createProject(args[2], args[3], args[4], args[5]).done(null, function(err) { + console.error(err); + process.exit(2); + }); } diff --git a/bin/lib/check_reqs.js b/bin/lib/check_reqs.js index c064499f..80f88a84 100644 --- a/bin/lib/check_reqs.js +++ b/bin/lib/check_reqs.js @@ -20,6 +20,8 @@ */ var shell = require('shelljs'), + child_process = require('child_process'), + Q = require('q'), path = require('path'), fs = require('fs'), ROOT = path.join(__dirname, '..', '..'); @@ -36,43 +38,54 @@ module.exports.get_target = function() { } } +// Returns a promise. module.exports.check_ant = function() { - var test = shell.exec('ant -version', {silent:true, async:false}); - if(test.code > 0) { - console.error('ERROR : executing command \'ant\', make sure you have ant installed and added to your path.'); - return false; - } - return true; + var d = Q.defer(); + child_process.exec('ant -version', function(err, stdout, stderr) { + if (err) d.reject(new Error('ERROR : executing command \'ant\', make sure you have ant installed and added to your path.')); + else d.resolve(); + }); + return d.promise; } +// Returns a promise. module.exports.check_java = function() { if(process.env.JAVA_HOME) { - var test = shell.exec('java', {silent:true, async:false}); - if(test.code > 0) { - console.error('ERROR : executing command \'java\', make sure you java environment is set up. Including your JDK and JRE.'); - return false; - } - return true; + var d = Q.defer(); + child_process.exec('java', function(err, stdout, stderr) { + if(err) d.reject(new Error('ERROR : executing command \'java\', make sure you java environment is set up. Including your JDK and JRE.')); + else d.resolve(); + }); + return d.promise; } else { - console.error('ERROR : Make sure JAVA_HOME is set, as well as paths to your JDK and JRE for java.'); - return false; + return Q.reject(new Error('ERROR : Make sure JAVA_HOME is set, as well as paths to your JDK and JRE for java.')); } } +// Returns a promise. module.exports.check_android = function() { var valid_target = this.get_target(); - var targets = shell.exec('android list targets', {silent:true, async:false}); + var d = Q.defer(); + child_process.exec('android list targets', function(err, stdout, stderr) { + if (err) d.reject(stderr); + else d.resolve(stdout); + }); - if(targets.code > 0 && targets.output.match(/command\snot\sfound/)) { - console.error('The command \"android\" failed. Make sure you have the latest Android SDK installed, and the \"android\" command (inside the tools/ folder) is added to your path.'); - return false; - } else if(!targets.output.match(valid_target)) { - console.error('Please install Android target ' + valid_target.split('-')[1] + ' (the Android newest SDK). Make sure you have the latest Android tools installed as well. Run \"android\" from your command-line to install/update any missing SDKs or tools.'); - return false; - } - return true; + return d.promise.then(function(output) { + if (!output.match(valid_target)) { + return Q.reject(new Error('Please install Android target ' + valid_target.split('-')[1] + ' (the Android newest SDK). Make sure you have the latest Android tools installed as well. Run \"android\" from your command-line to install/update any missing SDKs or tools.')); + } + return Q(); + }, function(stderr) { + if (stderr.match(/command\snot\sfound/)) { + return Q.reject(new Error('The command \"android\" failed. Make sure you have the latest Android SDK installed, and the \"android\" command (inside the tools/ folder) is added to your path.')); + } else { + return Q.reject(new Error('An error occurred while listing Android targets')); + } + }); } +// Returns a promise. module.exports.run = function() { - return this.check_ant() && this.check_java && this.check_android(); + return Q.all([this.check_ant(), this.check_java, this.check_android()]); } diff --git a/bin/lib/create.js b/bin/lib/create.js index f34a244a..9ba460d5 100755 --- a/bin/lib/create.js +++ b/bin/lib/create.js @@ -19,27 +19,25 @@ under the License. */ var shell = require('shelljs'), + child_process = require('child_process'), + Q = require('q'), path = require('path'), fs = require('fs'), check_reqs = require('./check_reqs'), ROOT = path.join(__dirname, '..', '..'); -function exec(command) { - var result; +// Returns a promise. +function exec(command, opt_cwd) { + var d = Q.defer(); try { - result = shell.exec(command, {silent:false, async:false}); + child_process.exec(command, { cwd: opt_cwd }, function(err, stdout, stderr) { + if (err) d.reject(err); + else d.resolve(stdout); + }); } catch(e) { - console.error('Command error on execuation : ' + command); - console.error(e); - process.exit(2); - } - if(result && result.code > 0) { - console.error('Command failed to execute : ' + command); - console.error(result.output); - process.exit(2); - } else { - return result; + return Q.reject('Command error on execution: ' + command + '\n' + e); } + return d.promise; } function setShellFatal(value, func) { @@ -49,19 +47,20 @@ function setShellFatal(value, func) { shell.config.fatal = oldVal; } +// Returns a promise. function ensureJarIsBuilt(version, target_api) { var isDevVersion = /-dev$/.test(version); if (isDevVersion || !fs.existsSync(path.join(ROOT, 'framework', 'cordova-' + version + '.jar')) && fs.existsSync(path.join(ROOT, 'framework'))) { var valid_target = check_reqs.get_target(); console.log('Building cordova-' + version + '.jar'); // update the cordova-android framework for the desired target - exec('android --silent update lib-project --target "' + target_api + '" --path "' + path.join(ROOT, 'framework') + '"'); - // compile cordova.js and cordova.jar - var cwd = process.cwd(); - process.chdir(path.join(ROOT, 'framework')); - exec('ant jar'); - process.chdir(cwd); + return exec('android --silent update lib-project --target "' + target_api + '" --path "' + path.join(ROOT, 'framework') + '"') + .then(function() { + // compile cordova.js and cordova.jar + return exec('ant jar', path.join(ROOT, 'framework')); + }); } + return Q(); } function copyJsAndJar(projectPath, version) { @@ -100,6 +99,8 @@ function copyScripts(projectPath) { * - `package_name`{String} Package name, following reverse-domain style convention. * - `project_name` {String} Project name. * - 'project_template_dir' {String} Path to project template (override). + * + * Returns a promise. */ exports.createProject = function(project_path, package_name, project_name, project_template_dir) { @@ -123,72 +124,72 @@ exports.createProject = function(project_path, package_name, project_name, proje // Check if project already exists if(fs.existsSync(project_path)) { - console.error('Project already exists! Delete and recreate'); - process.exit(2); + return Q.reject('Project already exists! Delete and recreate'); } if (!/[a-zA-Z0-9_]+\.[a-zA-Z0-9_](.[a-zA-Z0-9_])*/.test(package_name)) { - console.error('Package name must look like: com.company.Name'); - process.exit(2); + return Q.reject('Package name must look like: com.company.Name'); } // Check that requirements are met and proper targets are installed - if(!check_reqs.run()) { - process.exit(2); - } + return check_reqs.run() + .then(function() { + // Log the given values for the project + console.log('Creating Cordova project for the Android platform:'); + console.log('\tPath: ' + project_path); + console.log('\tPackage: ' + package_name); + console.log('\tName: ' + project_name); + console.log('\tAndroid target: ' + target_api); - // Log the given values for the project - console.log('Creating Cordova project for the Android platform:'); - console.log('\tPath: ' + project_path); - console.log('\tPackage: ' + package_name); - console.log('\tName: ' + project_name); - console.log('\tAndroid target: ' + target_api); + // build from source. distro should have these files + return ensureJarIsBuilt(VERSION, target_api); + }).then(function() { + console.log('Copying template files...'); - // build from source. distro should have these files - ensureJarIsBuilt(VERSION, target_api); + setShellFatal(true, function() { + // copy project template + shell.cp('-r', path.join(project_template_dir, 'assets'), project_path); + shell.cp('-r', path.join(project_template_dir, 'res'), project_path); + // Manually create directories that would be empty within the template (since git doesn't track directories). + shell.mkdir(path.join(project_path, 'libs')); - console.log('Copying template files...'); + // copy cordova.js, cordova.jar and res/xml + shell.cp('-r', path.join(ROOT, 'framework', 'res', 'xml'), path.join(project_path, 'res')); + copyJsAndJar(project_path, VERSION); - setShellFatal(true, function() { - // copy project template - shell.cp('-r', path.join(project_template_dir, 'assets'), project_path); - shell.cp('-r', path.join(project_template_dir, 'res'), project_path); - // Manually create directories that would be empty within the template (since git doesn't track directories). - shell.mkdir(path.join(project_path, 'libs')); + // interpolate the activity name and package + shell.mkdir('-p', activity_dir); + shell.cp('-f', path.join(project_template_dir, 'Activity.java'), activity_path); + shell.sed('-i', /__ACTIVITY__/, safe_activity_name, activity_path); + shell.sed('-i', /__NAME__/, project_name, path.join(project_path, 'res', 'values', 'strings.xml')); + shell.sed('-i', /__ID__/, package_name, activity_path); - // copy cordova.js, cordova.jar and res/xml - shell.cp('-r', path.join(ROOT, 'framework', 'res', 'xml'), path.join(project_path, 'res')); - copyJsAndJar(project_path, VERSION); - - // interpolate the activity name and package - shell.mkdir('-p', activity_dir); - shell.cp('-f', path.join(project_template_dir, 'Activity.java'), activity_path); - shell.sed('-i', /__ACTIVITY__/, safe_activity_name, activity_path); - shell.sed('-i', /__NAME__/, project_name, path.join(project_path, 'res', 'values', 'strings.xml')); - shell.sed('-i', /__ID__/, package_name, activity_path); - - shell.cp('-f', path.join(project_template_dir, 'AndroidManifest.xml'), manifest_path); - shell.sed('-i', /__ACTIVITY__/, safe_activity_name, manifest_path); - shell.sed('-i', /__PACKAGE__/, package_name, manifest_path); - shell.sed('-i', /__APILEVEL__/, target_api.split('-')[1], manifest_path); - copyScripts(project_path); + shell.cp('-f', path.join(project_template_dir, 'AndroidManifest.xml'), manifest_path); + shell.sed('-i', /__ACTIVITY__/, safe_activity_name, manifest_path); + shell.sed('-i', /__PACKAGE__/, package_name, manifest_path); + shell.sed('-i', /__APILEVEL__/, target_api.split('-')[1], manifest_path); + copyScripts(project_path); + }); + // Link it to local android install. + console.log('Running "android update project"'); + return exec('android --silent update project --target "'+target_api+'" --path "'+ project_path+'"'); + }).then(function() { + console.log('Project successfully created.'); }); - // Link it to local android install. - console.log('Running "android update project"'); - exec('android --silent update project --target "'+target_api+'" --path "'+ project_path+'"'); - console.log('Project successfully created.'); } +// Returns a promise. exports.updateProject = function(projectPath) { // Check that requirements are met and proper targets are installed - if (!check_reqs.run()) { - process.exit(2); - } - var version = fs.readFileSync(path.join(ROOT, 'VERSION'), 'utf-8').trim(); - var target_api = check_reqs.get_target(); - ensureJarIsBuilt(version, target_api); - copyJsAndJar(projectPath, version); - copyScripts(projectPath); - console.log('Android project is now at version ' + version); + return check_reqs.run() + .then(function() { + var version = fs.readFileSync(path.join(ROOT, 'VERSION'), 'utf-8').trim(); + var target_api = check_reqs.get_target(); + return ensureJarIsBuilt(version, target_api); + }).then(function() { + copyJsAndJar(projectPath, version); + copyScripts(projectPath); + console.log('Android project is now at version ' + version); + }); }; diff --git a/bin/node_modules/q/CONTRIBUTING.md b/bin/node_modules/q/CONTRIBUTING.md new file mode 100644 index 00000000..500ab17b --- /dev/null +++ b/bin/node_modules/q/CONTRIBUTING.md @@ -0,0 +1,40 @@ + +For pull requests: + +- Be consistent with prevalent style and design decisions. +- Add a Jasmine spec to `specs/q-spec.js`. +- Use `npm test` to avoid regressions. +- Run tests in `q-spec/run.html` in as many supported browsers as you + can find the will to deal with. +- Do not build minified versions; we do this each release. +- If you would be so kind, add a note to `CHANGES.md` in an + appropriate section: + + - `Next Major Version` if it introduces backward incompatibilities + to code in the wild using documented features. + - `Next Minor Version` if it adds a new feature. + - `Next Patch Version` if it fixes a bug. + +For releases: + +- Run `npm test`. +- Run tests in `q-spec/run.html` in a representative sample of every + browser under the sun. +- Run `npm run cover` and make sure you're happy with the results. +- Run `npm run minify` and be sure to commit the resulting `q.min.js`. +- Note the Gzipped size output by the previous command, and update + `README.md` if it has changed to 1 significant digit. +- Stash any local changes. +- Update `CHANGES.md` to reflect all changes in the differences + between `HEAD` and the previous tagged version. Give credit where + credit is due. +- Update `README.md` to address all new, non-experimental features. +- Update the API reference on the Wiki to reflect all non-experimental + features. +- Use `npm version major|minor|patch` to update `package.json`, + commit, and tag the new version. +- Use `npm publish` to send up a new release. +- Send an email to the q-continuum mailing list announcing the new + release and the notes from the change log. This helps folks + maintaining other package ecosystems. + diff --git a/bin/node_modules/q/LICENSE b/bin/node_modules/q/LICENSE new file mode 100644 index 00000000..76c5fe4c --- /dev/null +++ b/bin/node_modules/q/LICENSE @@ -0,0 +1,19 @@ + +Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved. +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/bin/node_modules/q/README.md b/bin/node_modules/q/README.md new file mode 100644 index 00000000..c0f513ce --- /dev/null +++ b/bin/node_modules/q/README.md @@ -0,0 +1,813 @@ +[![Build Status](https://secure.travis-ci.org/kriskowal/q.png?branch=master)](http://travis-ci.org/kriskowal/q) + + + Promises/A+ logo + + +If a function cannot return a value or throw an exception without +blocking, it can return a promise instead. A promise is an object +that represents the return value or the thrown exception that the +function may eventually provide. A promise can also be used as a +proxy for a [remote object][Q-Connection] to overcome latency. + +[Q-Connection]: https://github.com/kriskowal/q-connection + +On the first pass, promises can mitigate the “[Pyramid of +Doom][POD]”: the situation where code marches to the right faster +than it marches forward. + +[POD]: http://calculist.org/blog/2011/12/14/why-coroutines-wont-work-on-the-web/ + +```javascript +step1(function (value1) { + step2(value1, function(value2) { + step3(value2, function(value3) { + step4(value3, function(value4) { + // Do something with value4 + }); + }); + }); +}); +``` + +With a promise library, you can flatten the pyramid. + +```javascript +Q.fcall(promisedStep1) +.then(promisedStep2) +.then(promisedStep3) +.then(promisedStep4) +.then(function (value4) { + // Do something with value4 +}) +.catch(function (error) { + // Handle any error from all above steps +}) +.done(); +``` + +With this approach, you also get implicit error propagation, just like `try`, +`catch`, and `finally`. An error in `promisedStep1` will flow all the way to +the `catch` function, where it’s caught and handled. (Here `promisedStepN` is +a version of `stepN` that returns a promise.) + +The callback approach is called an “inversion of control”. +A function that accepts a callback instead of a return value +is saying, “Don’t call me, I’ll call you.”. Promises +[un-invert][IOC] the inversion, cleanly separating the input +arguments from control flow arguments. This simplifies the +use and creation of API’s, particularly variadic, +rest and spread arguments. + +[IOC]: http://www.slideshare.net/domenicdenicola/callbacks-promises-and-coroutines-oh-my-the-evolution-of-asynchronicity-in-javascript + + +## Getting Started + +The Q module can be loaded as: + +- A ``