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