coa 0.4.1 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
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;