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