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