| /** |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| */ |
| |
| /* jshint node:true, bitwise:true, undef:true, trailing:true, quotmark:true, |
| indent:4, unused:vars, latedef:nofunc, |
| laxcomma:true |
| */ |
| |
| |
| var path = require('path'), |
| fs = require('fs'), |
| help = require('./help'), |
| nopt, |
| _, |
| updateNotifier, |
| pkg = require('../package.json'); |
| |
| var cordova_lib = require('cordova-lib'), |
| CordovaError = cordova_lib.CordovaError, |
| cordova = cordova_lib.cordova, |
| events = cordova_lib.events, |
| logger = require('cordova-common').CordovaLogger.get(); |
| |
| |
| /* |
| * init |
| * |
| * initializes nopt and underscore |
| * nopt and underscore are require()d in try-catch below to print a nice error |
| * message if one of them is not installed. |
| */ |
| function init() { |
| try { |
| nopt = require('nopt'); |
| _ = require('underscore'); |
| updateNotifier = require('update-notifier'); |
| } catch (e) { |
| console.error( |
| 'Please run npm install from this directory:\n\t' + |
| path.dirname(__dirname) |
| ); |
| process.exit(2); |
| } |
| } |
| |
| function checkForUpdates() { |
| // Checks for available update and returns an instance |
| var notifier = updateNotifier({ |
| pkg: pkg |
| }); |
| |
| // Notify using the built-in convenience method |
| notifier.notify(); |
| } |
| |
| module.exports = cli; |
| function cli(inputArgs) { |
| // When changing command line arguments, update doc/help.txt accordingly. |
| var knownOpts = |
| { 'verbose' : Boolean |
| , 'version' : Boolean |
| , 'help' : Boolean |
| , 'silent' : Boolean |
| , 'experimental' : Boolean |
| , 'noregistry' : Boolean |
| , 'nohooks': Array |
| , 'shrinkwrap' : Boolean |
| , 'copy-from' : String |
| , 'link-to' : path |
| , 'searchpath' : String |
| , 'variable' : Array |
| , 'link': Boolean |
| , 'force': Boolean |
| // Flags to be passed to `cordova build/run/emulate` |
| , 'debug' : Boolean |
| , 'release' : Boolean |
| , 'archs' : String |
| , 'device' : Boolean |
| , 'emulator': Boolean |
| , 'target' : String |
| , 'browserify': Boolean |
| , 'nobuild': Boolean |
| , 'list': Boolean |
| , 'buildConfig' : String |
| , 'template' : String |
| }; |
| |
| var shortHands = |
| { 'd' : '--verbose' |
| , 'v' : '--version' |
| , 'h' : '--help' |
| , 'src' : '--copy-from' |
| , 't' : '--template' |
| }; |
| |
| // If no inputArgs given, use process.argv. |
| inputArgs = inputArgs || process.argv; |
| |
| init(); |
| |
| checkForUpdates(); |
| |
| var args = nopt(knownOpts, shortHands, inputArgs); |
| |
| if (args.version) { |
| var cliVersion = require('../package').version; |
| var libVersion = require('cordova-lib/package').version; |
| var toPrint = cliVersion; |
| if (cliVersion != libVersion || /-dev$/.exec(libVersion)) { |
| toPrint += ' (cordova-lib@' + libVersion + ')'; |
| } |
| console.log(toPrint); |
| return; |
| } |
| |
| |
| // For CordovaError print only the message without stack trace unless we |
| // are in a verbose mode. |
| process.on('uncaughtException', function(err) { |
| logger.error(err); |
| process.exit(1); |
| }); |
| |
| logger.subscribe(events); |
| |
| if (args.silent) { |
| logger.setLevel('error'); |
| } |
| |
| if (args.verbose) { |
| logger.setLevel('verbose'); |
| } |
| |
| // TODO: Example wanted, is this functionality ever used? |
| // If there were arguments protected from nopt with a double dash, keep |
| // them in unparsedArgs. For example: |
| // cordova build ios -- --verbose --whatever |
| // In this case "--verbose" is not parsed by nopt and args.vergbose will be |
| // false, the unparsed args after -- are kept in unparsedArgs and can be |
| // passed downstream to some scripts invoked by Cordova. |
| var unparsedArgs = []; |
| var parseStopperIdx = args.argv.original.indexOf('--'); |
| if (parseStopperIdx != -1) { |
| unparsedArgs = args.argv.original.slice(parseStopperIdx + 1); |
| } |
| |
| // args.argv.remain contains both the undashed args (like platform names) |
| // and whatever unparsed args that were protected by " -- ". |
| // "undashed" stores only the undashed args without those after " -- " . |
| var remain = args.argv.remain; |
| var undashed = remain.slice(0, remain.length - unparsedArgs.length); |
| var cmd = undashed[0]; |
| var subcommand; |
| var msg; |
| var known_platforms = Object.keys(cordova_lib.cordova_platforms); |
| |
| if ( !cmd || cmd == 'help' || args.help ) { |
| if (!args.help && remain[0] == 'help') { |
| remain.shift(); |
| } |
| return help(remain); |
| } |
| |
| if ( !cordova.hasOwnProperty(cmd) ) { |
| msg = |
| 'Cordova does not know ' + cmd + '; try `' + cordova_lib.binname + |
| ' help` for a list of all the available commands.'; |
| throw new CordovaError(msg); |
| } |
| |
| var opts = { |
| platforms: [], |
| options: [], |
| verbose: args.verbose || false, |
| silent: args.silent || false, |
| browserify: args.browserify || false, |
| nohooks: args.nohooks || [], |
| searchpath : args.searchpath |
| }; |
| |
| if (cmd == 'emulate' || cmd == 'build' || cmd == 'prepare' || cmd == 'compile' || cmd == 'run' || cmd === 'clean') { |
| // All options without dashes are assumed to be platform names |
| opts.platforms = undashed.slice(1); |
| var badPlatforms = _.difference(opts.platforms, known_platforms); |
| if( !_.isEmpty(badPlatforms) ) { |
| msg = 'Unknown platforms: ' + badPlatforms.join(', '); |
| throw new CordovaError(msg); |
| } |
| |
| // Pass nopt-parsed args to PlatformApi through opts.options |
| opts.options = args; |
| opts.options.argv = unparsedArgs; |
| |
| if (cmd == 'run' && args.list && cordova.raw.targets) { |
| cordova.raw.targets.call(null, opts).done(); |
| return; |
| } |
| |
| cordova.raw[cmd].call(null, opts).done(); |
| } else if (cmd === 'requirements') { |
| // All options without dashes are assumed to be platform names |
| opts.platforms = undashed.slice(1); |
| var badPlatforms = _.difference(opts.platforms, known_platforms); |
| if( !_.isEmpty(badPlatforms) ) { |
| msg = 'Unknown platforms: ' + badPlatforms.join(', '); |
| throw new CordovaError(msg); |
| } |
| |
| cordova.raw[cmd].call(null, opts.platforms) |
| .then(function (platformChecks) { |
| |
| var someChecksFailed = Object.keys(platformChecks).map(function (platformName) { |
| events.emit('log', '\nRequirements check results for ' + platformName + ':'); |
| var platformCheck = platformChecks[platformName]; |
| if (platformCheck instanceof CordovaError) { |
| events.emit('warn', 'Check failed for ' + platformName + ' due to ' + platformCheck); |
| return true; |
| } |
| |
| var someChecksFailed = false; |
| platformCheck.forEach(function (checkItem) { |
| var checkSummary = checkItem.name + ': ' + |
| (checkItem.installed ? 'installed ' : 'not installed ') + |
| (checkItem.metadata.version || ''); |
| events.emit('log', checkSummary); |
| if (!checkItem.installed) { |
| someChecksFailed = true; |
| events.emit('warn', checkItem.metadata.reason); |
| } |
| }); |
| |
| return someChecksFailed; |
| }).some(function (isCheckFailedForPlatform) { |
| return isCheckFailedForPlatform; |
| }); |
| |
| if (someChecksFailed) throw new CordovaError('Some of requirements check failed'); |
| }).done(); |
| } else if (cmd == 'serve') { |
| var port = undashed[1]; |
| cordova.raw.serve(port).done(); |
| } else if (cmd == 'create') { |
| create(); |
| } else { |
| // platform/plugins add/rm [target(s)] |
| subcommand = undashed[1]; // sub-command like "add", "ls", "rm" etc. |
| var targets = undashed.slice(2); // array of targets, either platforms or plugins |
| var cli_vars = {}; |
| if (args.variable) { |
| args.variable.forEach(function (s) { |
| // CB-9171 |
| var eq = s.indexOf('='); |
| if (eq == -1) |
| throw new CordovaError("invalid variable format: " + s); |
| var key = s.substr(0, eq).toUpperCase(); |
| var val = s.substr(eq + 1, s.length); |
| cli_vars[key] = val; |
| }); |
| } |
| var download_opts = { searchpath : args.searchpath |
| , noregistry : args.noregistry |
| , nohooks : args.nohooks |
| , cli_variables : cli_vars |
| , browserify: args.browserify || false |
| , link: args.link || false |
| , save: args.save || false |
| , shrinkwrap: args.shrinkwrap || false |
| , force: args.force || false |
| }; |
| cordova.raw[cmd](subcommand, targets, download_opts).done(); |
| } |
| |
| function create() { |
| var cfg; // Create config |
| var customWww; // Template path |
| var wwwCfg; // Template config |
| |
| // If we got a fourth parameter, consider it to be JSON to init the config. |
| if (undashed[4]) |
| cfg = JSON.parse(undashed[4]); |
| else |
| cfg = {}; |
| |
| customWww = args['copy-from'] || args['link-to'] || args.template; |
| |
| if (customWww) { |
| if (!args.template && customWww.indexOf('http') === 0) { |
| throw new CordovaError( |
| 'Only local paths for custom www assets are supported.' |
| ); |
| } |
| |
| // Resolve tilda |
| if (customWww.substr(0,1) === '~') |
| customWww = path.join(process.env.HOME, customWww.substr(1)); |
| |
| wwwCfg = { |
| url: customWww, |
| template: false |
| }; |
| |
| if (args['link-to']) |
| wwwCfg.link = true; |
| else if (args.template) |
| wwwCfg.template = true; |
| |
| cfg.lib = cfg.lib || {}; |
| cfg.lib.www = wwwCfg; |
| } |
| |
| // create(dir, id, name, cfg) |
| cordova.raw.create( undashed[1] // dir to create the project in |
| , undashed[2] // App id |
| , undashed[3] // App name |
| , cfg |
| ).done(); |
| } |
| } |