1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 | var path = require('path'),
|
27 | Q = require('q'),
|
28 | nopt = require('nopt'),
|
29 | updateNotifier = require('update-notifier'),
|
30 | pkg = require('../package.json'),
|
31 | telemetry = require('./telemetry'),
|
32 | help = require('./help'),
|
33 | cordova_lib = require('cordova-lib'),
|
34 | CordovaError = cordova_lib.CordovaError,
|
35 | cordova = cordova_lib.cordova,
|
36 | events = cordova_lib.events,
|
37 | logger = require('cordova-common').CordovaLogger.get(),
|
38 | Configstore = require('configstore'),
|
39 | conf = new Configstore(pkg.name + '-config'),
|
40 | editor = require('editor'),
|
41 | fs = require('fs');
|
42 |
|
43 | var knownOpts = {
|
44 | 'verbose' : Boolean
|
45 | ,'version' : Boolean
|
46 | ,'help' : Boolean
|
47 | ,'silent' : Boolean
|
48 | ,'experimental' : Boolean
|
49 | ,'noregistry' : Boolean
|
50 | ,'nohooks': Array
|
51 | ,'shrinkwrap' : Boolean
|
52 | ,'copy-from' : String
|
53 | ,'link-to' : path
|
54 | ,'searchpath' : String
|
55 | ,'variable' : Array
|
56 | ,'link': Boolean
|
57 | ,'force': Boolean
|
58 |
|
59 | ,'debug' : Boolean
|
60 | ,'release' : Boolean
|
61 | ,'archs' : String
|
62 | ,'device' : Boolean
|
63 | ,'emulator': Boolean
|
64 | ,'target' : String
|
65 | ,'browserify': Boolean
|
66 | ,'noprepare': Boolean
|
67 | ,'fetch': Boolean
|
68 | ,'nobuild': Boolean
|
69 | ,'list': Boolean
|
70 | ,'buildConfig' : String
|
71 | ,'template' : String
|
72 | };
|
73 |
|
74 | var shortHands = {
|
75 | 'd' : '--verbose'
|
76 | ,'v' : '--version'
|
77 | ,'h' : '--help'
|
78 | ,'src' : '--copy-from'
|
79 | ,'t' : '--template'
|
80 | };
|
81 |
|
82 | var Configstore = require('configstore');
|
83 | var conf = new Configstore(pkg.name + '-config');
|
84 |
|
85 |
|
86 | function checkForUpdates() {
|
87 | try {
|
88 |
|
89 | var notifier = updateNotifier({
|
90 | pkg: pkg
|
91 | });
|
92 |
|
93 | notifier.notify();
|
94 | } catch (e) {
|
95 |
|
96 | if (e && e.message && /EACCES/.test(e.message)) {
|
97 | console.log('Update notifier was not able to access the config file.\n' +
|
98 | 'You may grant permissions to the file: \'sudo chmod 744 ~/.config/configstore/update-notifier-cordova.json\'');
|
99 | } else {
|
100 | throw e;
|
101 | }
|
102 | }
|
103 | }
|
104 |
|
105 | var shouldCollectTelemetry = false;
|
106 |
|
107 | module.exports = function (inputArgs, cb) {
|
108 | |
109 |
|
110 |
|
111 | cb = cb || function(){};
|
112 |
|
113 |
|
114 | inputArgs = inputArgs || process.argv;
|
115 | var cmd = inputArgs[2];
|
116 | var subcommand = getSubCommand(inputArgs, cmd);
|
117 | var isTelemetryCmd = (cmd === 'telemetry');
|
118 | var isConfigCmd = (cmd === 'config');
|
119 |
|
120 |
|
121 | if(cmd === '--version' || cmd === '-v') {
|
122 | cmd = 'version';
|
123 | } else if(!cmd || cmd === '--help' || cmd === 'h') {
|
124 | cmd = 'help';
|
125 | }
|
126 |
|
127 |
|
128 | if (isConfigCmd && inputArgs[3] === 'get') {
|
129 | if (inputArgs[4]) {
|
130 | logger.subscribe(events);
|
131 | conf.get(inputArgs[4]);
|
132 | if(conf.get(inputArgs[4]) !== undefined) {
|
133 | events.emit('log', conf.get(inputArgs[4]).toString());
|
134 | } else {
|
135 | events.emit('log', 'undefined');
|
136 | }
|
137 | }
|
138 | }
|
139 |
|
140 |
|
141 | if (isConfigCmd && inputArgs[3] === 'set') {
|
142 | if (inputArgs[5] === undefined) {
|
143 | conf.set(inputArgs[4], true);
|
144 | }
|
145 |
|
146 | if(inputArgs[5]) {
|
147 | conf.set(inputArgs[4], inputArgs[5]);
|
148 | }
|
149 | }
|
150 |
|
151 |
|
152 | if (isConfigCmd && inputArgs[3] === 'delete') {
|
153 | if (inputArgs[4]) {
|
154 | conf.del(inputArgs[4]);
|
155 | }
|
156 | }
|
157 |
|
158 |
|
159 | if (isConfigCmd && inputArgs[3] === 'edit') {
|
160 | editor(conf.path, function (code, sig) {
|
161 | console.log('Finished editing with code ' + code);
|
162 | });
|
163 | }
|
164 |
|
165 |
|
166 | if (isConfigCmd && inputArgs[3] === 'ls' || inputArgs[3] === 'list') {
|
167 | fs.readFile(conf.path, 'utf8', function (err,data) {
|
168 | if (err) {
|
169 | return console.log(err);
|
170 | }
|
171 | console.log(data);
|
172 | });
|
173 | }
|
174 |
|
175 | Q().then(function() {
|
176 | |
177 |
|
178 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 | if(telemetry.isCI(process.env) || telemetry.isNoTelemetryFlag(inputArgs)) {
|
184 | return Q(false);
|
185 | }
|
186 |
|
187 | |
188 |
|
189 |
|
190 |
|
191 | if(isTelemetryCmd) {
|
192 | var isOptedIn = telemetry.isOptedIn();
|
193 | return handleTelemetryCmd(subcommand, isOptedIn);
|
194 | }
|
195 |
|
196 | if(telemetry.hasUserOptedInOrOut()) {
|
197 | return Q(telemetry.isOptedIn());
|
198 | }
|
199 |
|
200 | |
201 |
|
202 |
|
203 |
|
204 | return telemetry.showPrompt();
|
205 | }).then(function (collectTelemetry) {
|
206 | shouldCollectTelemetry = collectTelemetry;
|
207 | if(isTelemetryCmd) {
|
208 | return Q();
|
209 | }
|
210 | return cli(inputArgs);
|
211 | }).then(function () {
|
212 | if (shouldCollectTelemetry && !isTelemetryCmd) {
|
213 | telemetry.track(cmd, subcommand, 'successful');
|
214 | }
|
215 |
|
216 | cb(null);
|
217 | }).fail(function (err) {
|
218 | if (shouldCollectTelemetry && !isTelemetryCmd) {
|
219 | telemetry.track(cmd, subcommand, 'unsuccessful');
|
220 | }
|
221 |
|
222 | cb(err);
|
223 | throw err;
|
224 | }).done();
|
225 | };
|
226 |
|
227 | function getSubCommand(args, cmd) {
|
228 | if(cmd === 'platform' || cmd === 'platforms' || cmd === 'plugin' || cmd === 'plugins' || cmd === 'telemetry' || cmd === 'config') {
|
229 | return args[3];
|
230 | }
|
231 | return null;
|
232 | }
|
233 |
|
234 | function handleTelemetryCmd(subcommand, isOptedIn) {
|
235 |
|
236 | if (subcommand !== 'on' && subcommand !== 'off') {
|
237 | logger.subscribe(events);
|
238 | return help(['telemetry']);
|
239 | }
|
240 |
|
241 | var turnOn = subcommand === 'on' ? true : false;
|
242 | var cmdSuccess = true;
|
243 |
|
244 |
|
245 | try {
|
246 | if (turnOn) {
|
247 | telemetry.turnOn();
|
248 | console.log('Thanks for opting into telemetry to help us improve cordova.');
|
249 | } else {
|
250 | telemetry.turnOff();
|
251 | console.log('You have been opted out of telemetry. To change this, run: cordova telemetry on.');
|
252 | }
|
253 | } catch (ex) {
|
254 | cmdSuccess = false;
|
255 | }
|
256 |
|
257 |
|
258 |
|
259 | if (!turnOn) {
|
260 |
|
261 | telemetry.track('telemetry', 'off', 'via-cordova-telemetry-cmd', cmdSuccess ? 'successful': 'unsuccessful');
|
262 | return Q();
|
263 | }
|
264 |
|
265 | if(isOptedIn) {
|
266 | telemetry.track('telemetry', 'on', 'via-cordova-telemetry-cmd', cmdSuccess ? 'successful' : 'unsuccessful');
|
267 | }
|
268 |
|
269 | return Q();
|
270 | }
|
271 |
|
272 | function cli(inputArgs) {
|
273 |
|
274 | checkForUpdates();
|
275 |
|
276 | var args = nopt(knownOpts, shortHands, inputArgs);
|
277 |
|
278 | process.on('uncaughtException', function(err) {
|
279 | if(err.message) {
|
280 | logger.error(err.message);
|
281 | } else {
|
282 | logger.error(err);
|
283 | }
|
284 |
|
285 | if(shouldCollectTelemetry) {
|
286 | telemetry.track('uncaughtException');
|
287 | }
|
288 | process.exit(1);
|
289 | });
|
290 |
|
291 | logger.subscribe(events);
|
292 |
|
293 | if (args.silent) {
|
294 | logger.setLevel('error');
|
295 | }
|
296 |
|
297 | if (args.verbose) {
|
298 | logger.setLevel('verbose');
|
299 | }
|
300 |
|
301 | var cliVersion = require('../package').version;
|
302 |
|
303 | var usingPrerelease = /-nightly|-dev$/.exec(cliVersion);
|
304 | if (args.version || usingPrerelease) {
|
305 | var libVersion = require('cordova-lib/package').version;
|
306 | var toPrint = cliVersion;
|
307 | if (cliVersion != libVersion || usingPrerelease) {
|
308 | toPrint += ' (cordova-lib@' + libVersion + ')';
|
309 | }
|
310 |
|
311 | if (args.version) {
|
312 | logger.results(toPrint);
|
313 | return Q();
|
314 | } else {
|
315 |
|
316 | logger.warn('Warning: using prerelease version ' + toPrint);
|
317 | }
|
318 | }
|
319 |
|
320 | if (/^v0.\d+[.\d+]*/.exec(process.version)) {
|
321 | var msg1 = 'Warning: using node version ' + process.version +
|
322 | ' which has been deprecated. Please upgrade to the latest node version available (v6.x is recommended).';
|
323 | logger.warn(msg1);
|
324 | }
|
325 |
|
326 |
|
327 |
|
328 |
|
329 |
|
330 |
|
331 |
|
332 | var unparsedArgs = [];
|
333 | var parseStopperIdx = args.argv.original.indexOf('--');
|
334 | if (parseStopperIdx != -1) {
|
335 | unparsedArgs = args.argv.original.slice(parseStopperIdx + 1);
|
336 | }
|
337 |
|
338 |
|
339 |
|
340 |
|
341 | var remain = args.argv.remain;
|
342 | var undashed = remain.slice(0, remain.length - unparsedArgs.length);
|
343 | var cmd = undashed[0];
|
344 | var subcommand;
|
345 |
|
346 | if ( !cmd || cmd == 'help' || args.help ) {
|
347 | if (!args.help && remain[0] == 'help') {
|
348 | remain.shift();
|
349 | }
|
350 | return help(remain);
|
351 | }
|
352 |
|
353 | if ( !cordova.hasOwnProperty(cmd) ) {
|
354 | var msg2 = 'Cordova does not know ' + cmd + '; try `' + cordova_lib.binname +
|
355 | ' help` for a list of all the available commands.';
|
356 | throw new CordovaError(msg2);
|
357 | }
|
358 |
|
359 | if (args.nofetch) {
|
360 | args.fetch = false;
|
361 | } else {
|
362 | args.fetch = true;
|
363 | }
|
364 |
|
365 | var opts = {
|
366 | platforms: [],
|
367 | options: [],
|
368 | verbose: args.verbose || false,
|
369 | silent: args.silent || false,
|
370 | browserify: args.browserify || false,
|
371 | fetch: args.fetch,
|
372 | nohooks: args.nohooks || [],
|
373 | searchpath : args.searchpath
|
374 | };
|
375 |
|
376 | var platformCommands = ['emulate', 'build', 'prepare', 'compile', 'run', 'clean'];
|
377 | if (platformCommands.indexOf(cmd) !== -1) {
|
378 |
|
379 |
|
380 | opts.platforms = undashed.slice(1);
|
381 |
|
382 |
|
383 | opts.options = args;
|
384 | opts.options.argv = unparsedArgs;
|
385 | if (cmd === 'run' && args.list && cordova.raw.targets) {
|
386 | return cordova.raw.targets.call(null, opts);
|
387 | }
|
388 | return cordova.raw[cmd].call(null, opts);
|
389 |
|
390 | } else if (cmd === 'requirements') {
|
391 |
|
392 | opts.platforms = undashed.slice(1);
|
393 |
|
394 | return cordova.raw[cmd].call(null, opts.platforms)
|
395 | .then(function(platformChecks) {
|
396 |
|
397 | var someChecksFailed = Object.keys(platformChecks).map(function(platformName) {
|
398 | events.emit('log', '\nRequirements check results for ' + platformName + ':');
|
399 | var platformCheck = platformChecks[platformName];
|
400 | if (platformCheck instanceof CordovaError) {
|
401 | events.emit('warn', 'Check failed for ' + platformName + ' due to ' + platformCheck);
|
402 | return true;
|
403 | }
|
404 |
|
405 | var someChecksFailed = false;
|
406 | platformCheck.forEach(function(checkItem) {
|
407 | var checkSummary = checkItem.name + ': ' +
|
408 | (checkItem.installed ? 'installed ' : 'not installed ') +
|
409 | (checkItem.metadata.version || '');
|
410 | events.emit('log', checkSummary);
|
411 | if (!checkItem.installed) {
|
412 | someChecksFailed = true;
|
413 | events.emit('warn', checkItem.metadata.reason);
|
414 | }
|
415 | });
|
416 |
|
417 | return someChecksFailed;
|
418 | }).some(function(isCheckFailedForPlatform) {
|
419 | return isCheckFailedForPlatform;
|
420 | });
|
421 |
|
422 | if (someChecksFailed) {
|
423 | throw new CordovaError('Some of requirements check failed');
|
424 | }
|
425 | });
|
426 | } else if (cmd === 'serve') {
|
427 | var port = undashed[1];
|
428 | return cordova.raw.serve(port);
|
429 | } else if (cmd === 'create') {
|
430 | return create(undashed,args);
|
431 | } else if (cmd === 'config') {
|
432 |
|
433 | return true;
|
434 | } else {
|
435 |
|
436 | subcommand = undashed[1];
|
437 | var targets = undashed.slice(2);
|
438 | var cli_vars = {};
|
439 | if (args.variable) {
|
440 | args.variable.forEach(function (strVar) {
|
441 |
|
442 | var keyVal = strVar.split('=');
|
443 | if(keyVal.length < 2) {
|
444 | throw new CordovaError('invalid variable format: ' + strVar);
|
445 | }
|
446 | else {
|
447 | var key = keyVal.shift().toUpperCase();
|
448 | var val = keyVal.join('=');
|
449 | cli_vars[key] = val;
|
450 | }
|
451 | });
|
452 | }
|
453 |
|
454 | if (args.nosave) {
|
455 | args.save = false;
|
456 | } else {
|
457 | args.save = true;
|
458 | }
|
459 |
|
460 | if (args.save === undefined) {
|
461 |
|
462 | args.save = conf.get('autosave');
|
463 | }
|
464 | if (args.fetch === undefined) {
|
465 |
|
466 | args.fetch = conf.get('fetch');
|
467 | }
|
468 | if(args.browserify === undefined) {
|
469 |
|
470 | args.browserify = conf.get('browserify');
|
471 | }
|
472 |
|
473 | var download_opts = { searchpath : args.searchpath
|
474 | , noregistry : args.noregistry
|
475 | , nohooks : args.nohooks
|
476 | , cli_variables : cli_vars
|
477 | , browserify: args.browserify || false
|
478 | , fetch: args.fetch
|
479 | , link: args.link || false
|
480 | , save: args.save
|
481 | , shrinkwrap: args.shrinkwrap || false
|
482 | , force: args.force || false
|
483 | };
|
484 | return cordova.raw[cmd](subcommand, targets, download_opts);
|
485 | }
|
486 | }
|
487 |
|
488 | function create(undashed, args) {
|
489 | var cfg;
|
490 | var customWww;
|
491 | var wwwCfg;
|
492 |
|
493 |
|
494 | if (undashed[4])
|
495 | cfg = JSON.parse(undashed[4]);
|
496 | else
|
497 | cfg = {};
|
498 |
|
499 | customWww = args['copy-from'] || args['link-to'] || args.template;
|
500 |
|
501 | if (customWww) {
|
502 | if (!args.template && !args['copy-from'] && customWww.indexOf('http') === 0) {
|
503 | throw new CordovaError(
|
504 | 'Only local paths for custom www assets are supported for linking' + customWww
|
505 | );
|
506 | }
|
507 |
|
508 |
|
509 | if (customWww.substr(0,1) === '~')
|
510 | customWww = path.join(process.env.HOME, customWww.substr(1));
|
511 |
|
512 | wwwCfg = {
|
513 | url: customWww,
|
514 | template: false,
|
515 | link: false
|
516 | };
|
517 |
|
518 | if (args['link-to']) {
|
519 | wwwCfg.link = true;
|
520 | }
|
521 | if (args.template) {
|
522 | wwwCfg.template = true;
|
523 | } else if (args['copy-from']) {
|
524 | logger.warn('Warning: --copy-from option is being deprecated. Consider using --template instead.');
|
525 | wwwCfg.template = true;
|
526 | }
|
527 |
|
528 | cfg.lib = cfg.lib || {};
|
529 | cfg.lib.www = wwwCfg;
|
530 | }
|
531 | return cordova.raw.create( undashed[1]
|
532 | , undashed[2]
|
533 | , undashed[3]
|
534 | , cfg
|
535 | , events || undefined
|
536 | );
|
537 | }
|