coa 0.4.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/cmd.js CHANGED
@@ -1,605 +1,492 @@
1
- // Generated by CoffeeScript 1.6.3
2
- var Cmd, Color, PATH, Q, UTIL,
3
- __slice = [].slice;
1
+ 'use strict';
4
2
 
5
- UTIL = require('util');
3
+ const
4
+ UTIL = require('util'),
5
+ PATH = require('path'),
6
+ EOL = require('os').EOL,
6
7
 
7
- PATH = require('path');
8
+ Q = require('q'),
8
9
 
9
- Color = require('./color').Color;
10
-
11
- Q = require('q');
10
+ CoaObject = require('./coaobject'),
11
+ Color = require('./color'),
12
+ Opt = require('./opt'),
13
+ Arg = require('./arg'),
14
+ completion = require('./completion');
12
15
 
13
16
  /**
14
- Command
17
+ * Command
18
+ *
19
+ * Top level entity. Commands may have options and arguments.
20
+ *
21
+ * @namespace
22
+ * @class Cmd
23
+ * @extends CoaObject
24
+ */
25
+ class Cmd extends CoaObject {
26
+ /**
27
+ * @constructs
28
+ * @param {COA.Cmd} [cmd] parent command
29
+ */
30
+ constructor(cmd) {
31
+ super(cmd);
32
+
33
+ this._parent(cmd);
34
+ this._cmds = [];
35
+ this._cmdsByName = {};
36
+ this._opts = [];
37
+ this._optsByKey = {};
38
+ this._args = [];
39
+ this._api = null;
40
+ this._ext = false;
41
+ }
15
42
 
16
- Top level entity. Commands may have options and arguments.
17
- @namespace
18
- @class Presents command
19
- */
43
+ static create(cmd) {
44
+ return new Cmd(cmd);
45
+ }
20
46
 
47
+ /**
48
+ * Returns object containing all its subcommands as methods
49
+ * to use from other programs.
50
+ *
51
+ * @returns {Object}
52
+ */
53
+ get api() {
54
+ // Need _this here because of passed arguments into _api
55
+ const _this = this;
56
+ this._api || (this._api = function () {
57
+ return _this.invoke.apply(_this, arguments);
58
+ });
21
59
 
22
- exports.Cmd = Cmd = (function() {
23
- /**
24
- @constructs
25
- @param {COA.Cmd} [cmd] parent command
26
- */
60
+ const cmds = this._cmdsByName;
61
+ Object.keys(cmds).forEach(cmd => { this._api[cmd] = cmds[cmd].api; });
27
62
 
28
- function Cmd(cmd) {
29
- if (!(this instanceof Cmd)) {
30
- return new Cmd(cmd);
31
- }
32
- this._parent(cmd);
33
- this._cmds = [];
34
- this._cmdsByName = {};
35
- this._opts = [];
36
- this._optsByKey = {};
37
- this._args = [];
38
- this._ext = false;
39
- }
40
-
41
- Cmd.get = function(propertyName, func) {
42
- return Object.defineProperty(this.prototype, propertyName, {
43
- configurable: true,
44
- enumerable: true,
45
- get: func
46
- });
47
- };
48
-
49
- /**
50
- Returns object containing all its subcommands as methods
51
- to use from other programs.
52
- @returns {Object}
53
- */
54
-
55
-
56
- Cmd.get('api', function() {
57
- var c, _fn,
58
- _this = this;
59
- if (!this._api) {
60
- this._api = function() {
61
- return _this.invoke.apply(_this, arguments);
62
- };
63
+ return this._api;
63
64
  }
64
- _fn = function(c) {
65
- return _this._api[c] = _this._cmdsByName[c].api;
66
- };
67
- for (c in this._cmdsByName) {
68
- _fn(c);
65
+
66
+ _parent(cmd) {
67
+ this._cmd = cmd || this;
68
+
69
+ this.isRootCmd ||
70
+ cmd._cmds.push(this) &&
71
+ this._name &&
72
+ (this._cmd._cmdsByName[this._name] = this);
73
+
74
+ return this;
69
75
  }
70
- return this._api;
71
- });
72
-
73
- Cmd.prototype._parent = function(cmd) {
74
- this._cmd = cmd || this;
75
- if (cmd) {
76
- cmd._cmds.push(this);
77
- if (this._name) {
78
- this._cmd._cmdsByName[this._name] = this;
79
- }
76
+
77
+ get isRootCmd() {
78
+ return this._cmd === this;
80
79
  }
81
- return this;
82
- };
83
80
 
84
- /**
85
- Set a canonical command identifier to be used anywhere in the API.
86
- @param {String} _name command name
87
- @returns {COA.Cmd} this instance (for chainability)
88
- */
81
+ /**
82
+ * Set a canonical command identifier to be used anywhere in the API.
83
+ *
84
+ * @param {String} name - command name
85
+ * @returns {COA.Cmd} - this instance (for chainability)
86
+ */
87
+ name(name) {
88
+ super.name(name);
89
89
 
90
+ this.isRootCmd ||
91
+ (this._cmd._cmdsByName[name] = this);
90
92
 
91
- Cmd.prototype.name = function(_name) {
92
- this._name = _name;
93
- if (this._cmd !== this) {
94
- this._cmd._cmdsByName[_name] = this;
93
+ return this;
95
94
  }
96
- return this;
97
- };
98
-
99
- /**
100
- Set a long description for command to be used anywhere in text messages.
101
- @param {String} _title command title
102
- @returns {COA.Cmd} this instance (for chainability)
103
- */
104
95
 
96
+ /**
97
+ * Create new or add existing subcommand for current command.
98
+ *
99
+ * @param {COA.Cmd} [cmd] existing command instance
100
+ * @returns {COA.Cmd} new subcommand instance
101
+ */
102
+ cmd(cmd) {
103
+ return cmd?
104
+ cmd._parent(this)
105
+ : new Cmd(this);
106
+ }
105
107
 
106
- Cmd.prototype.title = function(_title) {
107
- this._title = _title;
108
- return this;
109
- };
108
+ /**
109
+ * Create option for current command.
110
+ *
111
+ * @returns {COA.Opt} new option instance
112
+ */
113
+ opt() {
114
+ return new Opt(this);
115
+ }
110
116
 
111
- /**
112
- Create new or add existing subcommand for current command.
113
- @param {COA.Cmd} [cmd] existing command instance
114
- @returns {COA.Cmd} new subcommand instance
115
- */
117
+ /**
118
+ * Create argument for current command.
119
+ *
120
+ * @returns {COA.Opt} new argument instance
121
+ */
122
+ arg() {
123
+ return new Arg(this);
124
+ }
116
125
 
126
+ /**
127
+ * Add (or set) action for current command.
128
+ *
129
+ * @param {Function} act - action function,
130
+ * invoked in the context of command instance
131
+ * and has the parameters:
132
+ * - {Object} opts - parsed options
133
+ * - {String[]} args - parsed arguments
134
+ * - {Object} res - actions result accumulator
135
+ * It can return rejected promise by Cmd.reject (in case of error)
136
+ * or any other value treated as result.
137
+ * @param {Boolean} [force=false] flag for set action instead add to existings
138
+ * @returns {COA.Cmd} - this instance (for chainability)
139
+ */
140
+ act(act, force) {
141
+ if(!act) return this;
142
+
143
+ (!this._act || force) && (this._act = []);
144
+ this._act.push(act);
145
+
146
+ return this;
147
+ }
117
148
 
118
- Cmd.prototype.cmd = function(cmd) {
119
- if (cmd) {
120
- return cmd._parent(this);
121
- } else {
122
- return new Cmd(this);
149
+ /**
150
+ * Make command "helpful", i.e. add -h --help flags for print usage.
151
+ *
152
+ * @returns {COA.Cmd} - this instance (for chainability)
153
+ */
154
+ helpful() {
155
+ return this.opt()
156
+ .name('help')
157
+ .title('Help')
158
+ .short('h')
159
+ .long('help')
160
+ .flag()
161
+ .only()
162
+ .act(function() {
163
+ return this.usage();
164
+ })
165
+ .end();
123
166
  }
124
- };
125
-
126
- /**
127
- Create option for current command.
128
- @returns {COA.Opt} new option instance
129
- */
130
-
131
-
132
- Cmd.prototype.opt = function() {
133
- return new (require('./opt').Opt)(this);
134
- };
135
-
136
- /**
137
- Create argument for current command.
138
- @returns {COA.Opt} new argument instance
139
- */
140
-
141
-
142
- Cmd.prototype.arg = function() {
143
- return new (require('./arg').Arg)(this);
144
- };
145
-
146
- /**
147
- Add (or set) action for current command.
148
- @param {Function} act action function,
149
- invoked in the context of command instance
150
- and has the parameters:
151
- - {Object} opts parsed options
152
- - {Array} args parsed arguments
153
- - {Object} res actions result accumulator
154
- It can return rejected promise by Cmd.reject (in case of error)
155
- or any other value treated as result.
156
- @param {Boolean} [force=false] flag for set action instead add to existings
157
- @returns {COA.Cmd} this instance (for chainability)
158
- */
159
-
160
-
161
- Cmd.prototype.act = function(act, force) {
162
- if (!act) {
163
- return this;
167
+
168
+ /**
169
+ * Adds shell completion to command, adds "completion" subcommand,
170
+ * that makes all the magic.
171
+ * Must be called only on root command.
172
+ *
173
+ * @returns {COA.Cmd} - this instance (for chainability)
174
+ */
175
+ completable() {
176
+ return this.cmd()
177
+ .name('completion')
178
+ .apply(completion)
179
+ .end();
164
180
  }
165
- if (!force && this._act) {
166
- this._act.push(act);
167
- } else {
168
- this._act = [act];
181
+
182
+ /**
183
+ * Allow command to be extendable by external node.js modules.
184
+ *
185
+ * @param {String} [pattern] Pattern of node.js module to find subcommands at.
186
+ * @returns {COA.Cmd} - this instance (for chainability)
187
+ */
188
+ extendable(pattern) {
189
+ this._ext = pattern || true;
190
+ return this;
169
191
  }
170
- return this;
171
- };
172
-
173
- /**
174
- Set custom additional completion for current command.
175
- @param {Function} completion generation function,
176
- invoked in the context of command instance.
177
- Accepts parameters:
178
- - {Object} opts completion options
179
- It can return promise or any other value treated as result.
180
- @returns {COA.Cmd} this instance (for chainability)
181
- */
182
-
183
-
184
- Cmd.prototype.comp = function(_comp) {
185
- this._comp = _comp;
186
- return this;
187
- };
188
-
189
- /**
190
- Apply function with arguments in context of command instance.
191
- @param {Function} fn
192
- @param {Array} args
193
- @returns {COA.Cmd} this instance (for chainability)
194
- */
195
-
196
-
197
- Cmd.prototype.apply = function() {
198
- var args, fn;
199
- fn = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
200
- fn.apply(this, args);
201
- return this;
202
- };
203
-
204
- /**
205
- Make command "helpful", i.e. add -h --help flags for print usage.
206
- @returns {COA.Cmd} this instance (for chainability)
207
- */
208
-
209
-
210
- Cmd.prototype.helpful = function() {
211
- return this.opt().name('help').title('Help').short('h').long('help').flag().only().act(function() {
212
- return this.usage();
213
- }).end();
214
- };
215
-
216
- /**
217
- Adds shell completion to command, adds "completion" subcommand,
218
- that makes all the magic.
219
- Must be called only on root command.
220
- @returns {COA.Cmd} this instance (for chainability)
221
- */
222
-
223
-
224
- Cmd.prototype.completable = function() {
225
- return this.cmd().name('completion').apply(require('./completion')).end();
226
- };
227
-
228
- /**
229
- Allow command to be extendable by external node.js modules.
230
- @param {String} [pattern] Pattern of node.js module to find subcommands at.
231
- @returns {COA.Cmd} this instance (for chainability)
232
- */
233
-
234
-
235
- Cmd.prototype.extendable = function(pattern) {
236
- this._ext = pattern || true;
237
- return this;
238
- };
239
-
240
- Cmd.prototype._exit = function(msg, code) {
241
- return process.once('exit', function() {
242
- if (msg) {
243
- UTIL.error(msg);
244
- }
245
- return process.exit(code || 0);
246
- });
247
- };
248
-
249
- /**
250
- Build full usage text for current command instance.
251
- @returns {String} usage text
252
- */
253
-
254
-
255
- Cmd.prototype.usage = function() {
256
- var res;
257
- res = [];
258
- if (this._title) {
259
- res.push(this._fullTitle());
192
+
193
+ _exit(msg, code) {
194
+ return process.once('exit', function() {
195
+ msg && console[code === 0 ? 'log' : 'error'](msg);
196
+ process.exit(code || 0);
197
+ });
260
198
  }
261
- res.push('', 'Usage:');
262
- if (this._cmds.length) {
263
- res.push(['', '', Color('lred', this._fullName()), Color('lblue', 'COMMAND'), Color('lgreen', '[OPTIONS]'), Color('lpurple', '[ARGS]')].join(' '));
199
+
200
+ /**
201
+ * Build full usage text for current command instance.
202
+ *
203
+ * @returns {String} usage text
204
+ */
205
+ usage() {
206
+ const res = [];
207
+
208
+ this._title && res.push(this._fullTitle());
209
+
210
+ res.push('', 'Usage:');
211
+
212
+ this._cmds.length
213
+ && res.push([
214
+ '', '', Color('lred', this._fullName()), Color('lblue', 'COMMAND'),
215
+ Color('lgreen', '[OPTIONS]'), Color('lpurple', '[ARGS]')
216
+ ].join(' '));
217
+
218
+ (this._opts.length + this._args.length)
219
+ && res.push([
220
+ '', '', Color('lred', this._fullName()),
221
+ Color('lgreen', '[OPTIONS]'), Color('lpurple', '[ARGS]')
222
+ ].join(' '));
223
+
224
+ res.push(
225
+ this._usages(this._cmds, 'Commands'),
226
+ this._usages(this._opts, 'Options'),
227
+ this._usages(this._args, 'Arguments')
228
+ );
229
+
230
+ return res.join(EOL);
264
231
  }
265
- if (this._opts.length + this._args.length) {
266
- res.push(['', '', Color('lred', this._fullName()), Color('lgreen', '[OPTIONS]'), Color('lpurple', '[ARGS]')].join(' '));
232
+
233
+ _usage() {
234
+ return Color('lblue', this._name) + ' : ' + this._title;
267
235
  }
268
- res.push(this._usages(this._cmds, 'Commands'), this._usages(this._opts, 'Options'), this._usages(this._args, 'Arguments'));
269
- return res.join('\n');
270
- };
271
-
272
- Cmd.prototype._usage = function() {
273
- return Color('lblue', this._name) + ' : ' + this._title;
274
- };
275
-
276
- Cmd.prototype._usages = function(os, title) {
277
- var o, res, _i, _len;
278
- if (!os.length) {
279
- return;
236
+
237
+ _usages(os, title) {
238
+ if(!os.length) return;
239
+
240
+ return ['', title + ':']
241
+ .concat(os.map(o => ` ${o._usage()}`))
242
+ .join(EOL);
280
243
  }
281
- res = ['', title + ':'];
282
- for (_i = 0, _len = os.length; _i < _len; _i++) {
283
- o = os[_i];
284
- res.push(' ' + o._usage());
244
+
245
+ _fullTitle() {
246
+ return `${this.isRootCmd? '' : this._cmd._fullTitle() + EOL}${this._title}`;
285
247
  }
286
- return res.join('\n');
287
- };
288
-
289
- Cmd.prototype._fullTitle = function() {
290
- return (this._cmd === this ? '' : this._cmd._fullTitle() + '\n') + this._title;
291
- };
292
-
293
- Cmd.prototype._fullName = function() {
294
- return (this._cmd === this ? '' : this._cmd._fullName() + ' ') + PATH.basename(this._name);
295
- };
296
-
297
- Cmd.prototype._ejectOpt = function(opts, opt) {
298
- var pos;
299
- if ((pos = opts.indexOf(opt)) >= 0) {
300
- if (opts[pos]._arr) {
301
- return opts[pos];
302
- } else {
303
- return opts.splice(pos, 1)[0];
304
- }
248
+
249
+ _fullName() {
250
+ return `${this.isRootCmd? '' : this._cmd._fullName() + ' '}${PATH.basename(this._name)}`;
305
251
  }
306
- };
307
-
308
- Cmd.prototype._checkRequired = function(opts, args) {
309
- var all, i;
310
- if (!(this._opts.filter(function(o) {
311
- return o._only && o._name in opts;
312
- })).length) {
313
- all = this._opts.concat(this._args);
314
- while (i = all.shift()) {
315
- if (i._req && i._checkParsed(opts, args)) {
316
- return this.reject(i._requiredText());
317
- }
318
- }
252
+
253
+ _ejectOpt(opts, opt) {
254
+ const pos = opts.indexOf(opt);
255
+ if(pos === -1) return;
256
+
257
+ return opts[pos]._arr?
258
+ opts[pos] :
259
+ opts.splice(pos, 1)[0];
319
260
  }
320
- };
321
261
 
322
- Cmd.prototype._parseCmd = function(argv, unparsed) {
323
- var c, cmd, cmdDesc, e, i, optSeen, pkg;
324
- if (unparsed == null) {
325
- unparsed = [];
262
+ _checkRequired(opts, args) {
263
+ if(this._opts.some(opt => opt._only && opts.hasOwnProperty(opt._name))) return;
264
+
265
+ const all = this._opts.concat(this._args);
266
+ let i;
267
+ while(i = all.shift())
268
+ if(i._req && i._checkParsed(opts, args))
269
+ return this.reject(i._requiredText());
326
270
  }
327
- argv = argv.concat();
328
- optSeen = false;
329
- while (i = argv.shift()) {
330
- if (!i.indexOf('-')) {
331
- optSeen = true;
332
- }
333
- if (!optSeen && /^\w[\w-_]*$/.test(i)) {
334
- cmd = this._cmdsByName[i];
335
- if (!cmd && this._ext) {
336
- if (typeof this._ext === 'string') {
337
- if (~this._ext.indexOf('%s')) {
338
- pkg = UTIL.format(this._ext, i);
339
- } else {
340
- pkg = this._ext + i;
341
- }
342
- } else if (this._ext === true) {
343
- pkg = i;
344
- c = this;
345
- while (true) {
346
- pkg = c._name + '-' + pkg;
347
- if (c._cmd === c) {
348
- break;
349
- }
350
- c = c._cmd;
271
+
272
+ _parseCmd(argv, unparsed) {
273
+ unparsed || (unparsed = []);
274
+
275
+ let i,
276
+ optSeen = false;
277
+ while(i = argv.shift()) {
278
+ i.indexOf('-') || (optSeen = true);
279
+
280
+ if(optSeen || !/^\w[\w-_]*$/.test(i)) {
281
+ unparsed.push(i);
282
+ continue;
351
283
  }
352
- }
353
- try {
354
- cmdDesc = require(pkg);
355
- } catch (_error) {
356
- e = _error;
357
- }
358
- if (cmdDesc) {
359
- if (typeof cmdDesc === 'function') {
360
- this.cmd().name(i).apply(cmdDesc).end();
361
- } else if (typeof cmdDesc === 'object') {
362
- this.cmd(cmdDesc);
363
- cmdDesc.name(i);
364
- } else {
365
- throw new Error('Error: Unsupported command declaration type, ' + 'should be function or COA.Cmd() object');
284
+
285
+ let pkg, cmd = this._cmdsByName[i];
286
+ if(!cmd && this._ext) {
287
+ if(this._ext === true) {
288
+ pkg = i;
289
+ let c = this;
290
+ while(true) { // eslint-disable-line
291
+ pkg = c._name + '-' + pkg;
292
+ if(c.isRootCmd) break;
293
+ c = c._cmd;
294
+ }
295
+ } else if(typeof this._ext === 'string')
296
+ pkg = ~this._ext.indexOf('%s')?
297
+ UTIL.format(this._ext, i) :
298
+ this._ext + i;
299
+
300
+ let cmdDesc;
301
+ try {
302
+ cmdDesc = require(pkg);
303
+ } catch(e) {
304
+ // Dummy
305
+ }
306
+
307
+ if(cmdDesc) {
308
+ if(typeof cmdDesc === 'function') {
309
+ this.cmd().name(i).apply(cmdDesc).end();
310
+ } else if(typeof cmdDesc === 'object') {
311
+ this.cmd(cmdDesc);
312
+ cmdDesc.name(i);
313
+ } else throw new Error('Error: Unsupported command declaration type, '
314
+ + 'should be a function or COA.Cmd() object');
315
+
316
+ cmd = this._cmdsByName[i];
317
+ }
366
318
  }
367
- cmd = this._cmdsByName[i];
368
- }
369
- }
370
- if (cmd) {
371
- return cmd._parseCmd(argv, unparsed);
319
+
320
+ if(cmd) return cmd._parseCmd(argv, unparsed);
321
+
322
+ unparsed.push(i);
372
323
  }
373
- }
374
- unparsed.push(i);
324
+
325
+ return { cmd : this, argv : unparsed };
375
326
  }
376
- return {
377
- cmd: this,
378
- argv: unparsed
379
- };
380
- };
381
-
382
- Cmd.prototype._parseOptsAndArgs = function(argv) {
383
- var a, arg, args, i, m, nonParsedArgs, nonParsedOpts, opt, opts, res;
384
- opts = {};
385
- args = {};
386
- nonParsedOpts = this._opts.concat();
387
- nonParsedArgs = this._args.concat();
388
- while (i = argv.shift()) {
389
- if (i !== '--' && !i.indexOf('-')) {
390
- if (m = i.match(/^(--\w[\w-_]*)=(.*)$/)) {
391
- i = m[1];
392
- if (!this._optsByKey[i]._flag) {
393
- argv.unshift(m[2]);
394
- }
395
- }
396
- if (opt = this._ejectOpt(nonParsedOpts, this._optsByKey[i])) {
397
- if (Q.isRejected(res = opt._parse(argv, opts))) {
398
- return res;
399
- }
400
- } else {
401
- return this.reject("Unknown option: " + i);
402
- }
403
- } else {
404
- if (i === '--') {
405
- i = argv.splice(0);
406
- }
407
- i = Array.isArray(i) ? i : [i];
408
- while (a = i.shift()) {
409
- if (arg = nonParsedArgs.shift()) {
410
- if (arg._arr) {
411
- nonParsedArgs.unshift(arg);
327
+
328
+ _parseOptsAndArgs(argv) {
329
+ const opts = {},
330
+ args = {},
331
+ nonParsedOpts = this._opts.concat(),
332
+ nonParsedArgs = this._args.concat();
333
+
334
+ let res, i;
335
+ while(i = argv.shift()) {
336
+ if(i !== '--' && i[0] === '-') {
337
+ const m = i.match(/^(--\w[\w-_]*)=(.*)$/);
338
+ if(m) {
339
+ i = m[1];
340
+ this._optsByKey[i]._flag || argv.unshift(m[2]);
341
+ }
342
+
343
+ const opt = this._ejectOpt(nonParsedOpts, this._optsByKey[i]);
344
+ if(!opt) return this.reject(`Unknown option: ${i}`);
345
+
346
+ if(Q.isRejected(res = opt._parse(argv, opts))) return res;
347
+
348
+ continue;
412
349
  }
413
- if (Q.isRejected(res = arg._parse(a, args))) {
414
- return res;
350
+
351
+ i === '--' && (i = argv.splice(0));
352
+ Array.isArray(i) || (i = [i]);
353
+
354
+ let a;
355
+ while(a = i.shift()) {
356
+ let arg = nonParsedArgs.shift();
357
+ if(!arg) return this.reject(`Unknown argument: ${a}`);
358
+
359
+ arg._arr && nonParsedArgs.unshift(arg);
360
+ if(Q.isRejected(res = arg._parse(a, args))) return res;
415
361
  }
416
- } else {
417
- return this.reject("Unknown argument: " + a);
418
- }
419
362
  }
420
- }
421
- }
422
- return {
423
- opts: this._setDefaults(opts, nonParsedOpts),
424
- args: this._setDefaults(args, nonParsedArgs)
425
- };
426
- };
427
-
428
- Cmd.prototype._setDefaults = function(params, desc) {
429
- var i, _i, _len;
430
- for (_i = 0, _len = desc.length; _i < _len; _i++) {
431
- i = desc[_i];
432
- if (!(i._name in params) && '_def' in i) {
433
- i._saveVal(params, i._def);
434
- }
435
- }
436
- return params;
437
- };
438
-
439
- Cmd.prototype._processParams = function(params, desc) {
440
- var i, n, notExists, res, v, vals, _i, _j, _len, _len1;
441
- notExists = [];
442
- for (_i = 0, _len = desc.length; _i < _len; _i++) {
443
- i = desc[_i];
444
- n = i._name;
445
- if (!(n in params)) {
446
- notExists.push(i);
447
- continue;
448
- }
449
- vals = params[n];
450
- delete params[n];
451
- if (!Array.isArray(vals)) {
452
- vals = [vals];
453
- }
454
- for (_j = 0, _len1 = vals.length; _j < _len1; _j++) {
455
- v = vals[_j];
456
- if (Q.isRejected(res = i._saveVal(params, v))) {
457
- return res;
458
- }
459
- }
460
- }
461
- return this._setDefaults(params, notExists);
462
- };
463
363
 
464
- Cmd.prototype._parseArr = function(argv) {
465
- return Q.when(this._parseCmd(argv), function(p) {
466
- return Q.when(p.cmd._parseOptsAndArgs(p.argv), function(r) {
467
364
  return {
468
- cmd: p.cmd,
469
- opts: r.opts,
470
- args: r.args
365
+ opts : this._setDefaults(opts, nonParsedOpts),
366
+ args : this._setDefaults(args, nonParsedArgs)
471
367
  };
472
- });
473
- });
474
- };
475
-
476
- Cmd.prototype._do = function(input) {
477
- var _this = this;
478
- return Q.when(input, function(input) {
479
- var cmd;
480
- cmd = input.cmd;
481
- return [_this._checkRequired].concat(cmd._act || []).reduce(function(res, act) {
482
- return Q.when(res, function(res) {
483
- return act.call(cmd, input.opts, input.args, res);
484
- });
485
- }, void 0);
486
- });
487
- };
488
-
489
- /**
490
- Parse arguments from simple format like NodeJS process.argv
491
- and run ahead current program, i.e. call process.exit when all actions done.
492
- @param {Array} argv
493
- @returns {COA.Cmd} this instance (for chainability)
494
- */
495
-
496
-
497
- Cmd.prototype.run = function(argv) {
498
- var cb,
499
- _this = this;
500
- if (argv == null) {
501
- argv = process.argv.slice(2);
502
368
  }
503
- cb = function(code) {
504
- return function(res) {
505
- var _ref, _ref1;
506
- if (res) {
507
- return _this._exit((_ref = res.stack) != null ? _ref : res.toString(), (_ref1 = res.exitCode) != null ? _ref1 : code);
508
- } else {
509
- return _this._exit();
510
- }
511
- };
512
- };
513
- Q.when(this["do"](argv), cb(0), cb(1)).done();
514
- return this;
515
- };
516
-
517
- /**
518
- Convenient function to run command from tests.
519
- @param {Array} argv
520
- @returns {Q.Promise}
521
- */
522
369
 
370
+ _setDefaults(params, desc) {
371
+ for(const item of desc)
372
+ item._def &&
373
+ !params.hasOwnProperty(item._name) &&
374
+ item._saveVal(params, item._def);
523
375
 
524
- Cmd.prototype["do"] = function(argv) {
525
- return this._do(this._parseArr(argv || []));
526
- };
527
-
528
- /**
529
- Invoke specified (or current) command using provided
530
- options and arguments.
531
- @param {String|Array} cmds subcommand to invoke (optional)
532
- @param {Object} opts command options (optional)
533
- @param {Object} args command arguments (optional)
534
- @returns {Q.Promise}
535
- */
536
-
537
-
538
- Cmd.prototype.invoke = function(cmds, opts, args) {
539
- var _this = this;
540
- if (cmds == null) {
541
- cmds = [];
542
- }
543
- if (opts == null) {
544
- opts = {};
376
+ return params;
545
377
  }
546
- if (args == null) {
547
- args = {};
548
- }
549
- if (typeof cmds === 'string') {
550
- cmds = cmds.split(' ');
378
+
379
+ _processParams(params, desc) {
380
+ const notExists = [];
381
+
382
+ for(const item of desc) {
383
+ const n = item._name;
384
+
385
+ if(!params.hasOwnProperty(n)) {
386
+ notExists.push(item);
387
+ continue;
388
+ }
389
+
390
+ const vals = Array.isArray(params[n])? params[n] : [params[n]];
391
+ delete params[n];
392
+
393
+ let res;
394
+ for(const v of vals)
395
+ if(Q.isRejected(res = item._saveVal(params, v)))
396
+ return res;
397
+ }
398
+
399
+ return this._setDefaults(params, notExists);
551
400
  }
552
- if (arguments.length < 3) {
553
- if (!Array.isArray(cmds)) {
554
- args = opts;
555
- opts = cmds;
556
- cmds = [];
557
- }
401
+
402
+ _parseArr(argv) {
403
+ return Q.when(this._parseCmd(argv), p =>
404
+ Q.when(p.cmd._parseOptsAndArgs(p.argv), r => ({
405
+ cmd : p.cmd,
406
+ opts : r.opts,
407
+ args : r.args
408
+ })));
558
409
  }
559
- return Q.when(this._parseCmd(cmds), function(p) {
560
- if (p.argv.length) {
561
- return _this.reject("Unknown command: " + cmds.join(' '));
562
- }
563
- return Q.all([_this._processParams(opts, _this._opts), _this._processParams(args, _this._args)]).spread(function(opts, args) {
564
- return _this._do({
565
- cmd: p.cmd,
566
- opts: opts,
567
- args: args
568
- }).fail(function(res) {
569
- if (res && res.exitCode === 0) {
570
- return res.toString();
571
- } else {
572
- return _this.reject(res);
573
- }
410
+
411
+ _do(inputPromise) {
412
+ return Q.when(inputPromise, input => {
413
+ return [this._checkRequired]
414
+ .concat(input.cmd._act || [])
415
+ .reduce((res, act) =>
416
+ Q.when(res, prev => act.call(input.cmd, input.opts, input.args, prev)),
417
+ undefined);
574
418
  });
575
- });
576
- });
577
- };
419
+ }
578
420
 
579
- /**
580
- Return reject of actions results promise with error code.
581
- Use in .act() for return with error.
582
- @param {Object} reject reason
583
- You can customize toString() method and exitCode property
584
- of reason object.
585
- @returns {Q.promise} rejected promise
586
- */
421
+ /**
422
+ * Parse arguments from simple format like NodeJS process.argv
423
+ * and run ahead current program, i.e. call process.exit when all actions done.
424
+ *
425
+ * @param {String[]} argv - arguments
426
+ * @returns {COA.Cmd} - this instance (for chainability)
427
+ */
428
+ run(argv) {
429
+ argv || (argv = process.argv.slice(2));
587
430
 
431
+ const cb = code =>
432
+ res => res?
433
+ this._exit(res.stack || res.toString(), (res.hasOwnProperty('exitCode')? res.exitCode : code) || 0) :
434
+ this._exit();
588
435
 
589
- Cmd.prototype.reject = function(reason) {
590
- return Q.reject(reason);
591
- };
436
+ Q.when(this.do(argv), cb(0), cb(1)).done();
592
437
 
593
- /**
594
- Finish chain for current subcommand and return parent command instance.
595
- @returns {COA.Cmd} parent command
596
- */
438
+ return this;
439
+ }
597
440
 
441
+ /**
442
+ * Invoke specified (or current) command using provided
443
+ * options and arguments.
444
+ *
445
+ * @param {String|String[]} [cmds] - subcommand to invoke (optional)
446
+ * @param {Object} [opts] - command options (optional)
447
+ * @param {Object} [args] - command arguments (optional)
448
+ * @returns {Q.Promise}
449
+ */
450
+ invoke(cmds, opts, args) {
451
+ cmds || (cmds = []);
452
+ opts || (opts = {});
453
+ args || (args = {});
454
+ typeof cmds === 'string' && (cmds = cmds.split(' '));
455
+
456
+ if(arguments.length < 3 && !Array.isArray(cmds)) {
457
+ args = opts;
458
+ opts = cmds;
459
+ cmds = [];
460
+ }
598
461
 
599
- Cmd.prototype.end = function() {
600
- return this._cmd;
601
- };
462
+ return Q.when(this._parseCmd(cmds), p => {
463
+ if(p.argv.length)
464
+ return this.reject(`Unknown command: ${cmds.join(' ')}`);
465
+
466
+ return Q.all([
467
+ this._processParams(opts, this._opts),
468
+ this._processParams(args, this._args)
469
+ ]).spread((_opts, _args) =>
470
+ this._do({
471
+ cmd : p.cmd,
472
+ opts : _opts,
473
+ args : _args
474
+ })
475
+ .fail(res => (res && res.exitCode === 0)?
476
+ res.toString() :
477
+ this.reject(res)));
478
+ });
479
+ }
480
+ }
602
481
 
603
- return Cmd;
482
+ /**
483
+ * Convenient function to run command from tests.
484
+ *
485
+ * @param {String[]} argv - arguments
486
+ * @returns {Q.Promise}
487
+ */
488
+ Cmd.prototype.do = function(argv) {
489
+ return this._do(this._parseArr(argv || []));
490
+ };
604
491
 
605
- })();
492
+ module.exports = Cmd;