coa 1.0.3 → 1.0.4

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.
Files changed (49) hide show
  1. package/.npmignore +6 -0
  2. package/.nyc_output/1f2a0db5a6d6559149db56d397f47cfc.json +1 -0
  3. package/.nyc_output/75b82d38f2186df930141082076e11c6.json +1 -0
  4. package/.travis.yml +9 -0
  5. package/GNUmakefile +34 -0
  6. package/README.md +1 -19
  7. package/coverage/base.css +212 -0
  8. package/coverage/coa/index.html +93 -0
  9. package/coverage/coa/index.js.html +68 -0
  10. package/coverage/coa/lib/arg.js.html +239 -0
  11. package/coverage/coa/lib/cmd.js.html +1556 -0
  12. package/coverage/coa/lib/coaobject.js.html +365 -0
  13. package/coverage/coa/lib/coaparam.js.html +440 -0
  14. package/coverage/coa/lib/color.js.html +131 -0
  15. package/coverage/coa/lib/completion.js.html +593 -0
  16. package/coverage/coa/lib/index.html +197 -0
  17. package/coverage/coa/lib/index.js.html +107 -0
  18. package/coverage/coa/lib/opt.js.html +524 -0
  19. package/coverage/coa/lib/shell.js.html +107 -0
  20. package/coverage/index.html +106 -0
  21. package/coverage/prettify.css +1 -0
  22. package/coverage/prettify.js +1 -0
  23. package/coverage/sort-arrow-sprite.png +0 -0
  24. package/coverage/sorter.js +158 -0
  25. package/index.js +1 -1
  26. package/lib/arg.js +161 -44
  27. package/lib/cmd.js +547 -434
  28. package/lib/color.js +22 -19
  29. package/lib/completion.js +119 -161
  30. package/lib/index.js +10 -14
  31. package/lib/opt.js +313 -130
  32. package/lib/shell.js +13 -13
  33. package/package.json +14 -19
  34. package/qq.js +17 -0
  35. package/src/arg.coffee +130 -0
  36. package/src/cmd.coffee +456 -0
  37. package/src/color.coffee +25 -0
  38. package/src/completion.coffee +156 -0
  39. package/src/index.coffee +5 -0
  40. package/src/opt.coffee +243 -0
  41. package/src/shell.coffee +10 -0
  42. package/test/coa.js +496 -0
  43. package/test/mocha.opts +2 -0
  44. package/test/shell-test.js +60 -0
  45. package/tests/api-h.js +9 -0
  46. package/tests/h.js +6 -0
  47. package/LICENSE +0 -21
  48. package/lib/coaobject.js +0 -100
  49. package/lib/coaparam.js +0 -125
package/lib/cmd.js CHANGED
@@ -1,492 +1,605 @@
1
- 'use strict';
1
+ // Generated by CoffeeScript 1.6.3
2
+ var Cmd, Color, PATH, Q, UTIL,
3
+ __slice = [].slice;
2
4
 
3
- const
4
- UTIL = require('util'),
5
- PATH = require('path'),
6
- EOL = require('os').EOL,
5
+ UTIL = require('util');
7
6
 
8
- Q = require('q'),
7
+ PATH = require('path');
9
8
 
10
- CoaObject = require('./coaobject'),
11
- Color = require('./color'),
12
- Opt = require('./opt'),
13
- Arg = require('./arg'),
14
- completion = require('./completion');
9
+ Color = require('./color').Color;
10
+
11
+ Q = require('q');
15
12
 
16
13
  /**
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
- }
14
+ Command
42
15
 
43
- static create(cmd) {
44
- return new Cmd(cmd);
45
- }
16
+ Top level entity. Commands may have options and arguments.
17
+ @namespace
18
+ @class Presents command
19
+ */
46
20
 
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
- });
59
21
 
60
- const cmds = this._cmdsByName;
61
- Object.keys(cmds).forEach(cmd => { this._api[cmd] = cmds[cmd].api; });
22
+ exports.Cmd = Cmd = (function() {
23
+ /**
24
+ @constructs
25
+ @param {COA.Cmd} [cmd] parent command
26
+ */
62
27
 
63
- return this._api;
28
+ function Cmd(cmd) {
29
+ if (!(this instanceof Cmd)) {
30
+ return new Cmd(cmd);
64
31
  }
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;
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
+ };
75
63
  }
76
-
77
- get isRootCmd() {
78
- return this._cmd === this;
64
+ _fn = function(c) {
65
+ return _this._api[c] = _this._cmdsByName[c].api;
66
+ };
67
+ for (c in this._cmdsByName) {
68
+ _fn(c);
79
69
  }
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
+ }
80
+ }
81
+ return this;
82
+ };
80
83
 
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);
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
+ */
89
89
 
90
- this.isRootCmd ||
91
- (this._cmd._cmdsByName[name] = this);
92
90
 
93
- return this;
91
+ Cmd.prototype.name = function(_name) {
92
+ this._name = _name;
93
+ if (this._cmd !== this) {
94
+ this._cmd._cmdsByName[_name] = this;
94
95
  }
96
+ return this;
97
+ };
95
98
 
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
- }
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
+ */
107
104
 
108
- /**
109
- * Create option for current command.
110
- *
111
- * @returns {COA.Opt} new option instance
112
- */
113
- opt() {
114
- return new Opt(this);
115
- }
116
105
 
117
- /**
118
- * Create argument for current command.
119
- *
120
- * @returns {COA.Opt} new argument instance
121
- */
122
- arg() {
123
- return new Arg(this);
124
- }
106
+ Cmd.prototype.title = function(_title) {
107
+ this._title = _title;
108
+ return this;
109
+ };
125
110
 
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
- }
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
+ */
148
116
 
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();
166
- }
167
117
 
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();
118
+ Cmd.prototype.cmd = function(cmd) {
119
+ if (cmd) {
120
+ return cmd._parent(this);
121
+ } else {
122
+ return new Cmd(this);
180
123
  }
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;
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;
191
164
  }
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
- });
165
+ if (!force && this._act) {
166
+ this._act.push(act);
167
+ } else {
168
+ this._act = [act];
198
169
  }
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);
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
+ console.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());
231
260
  }
232
-
233
- _usage() {
234
- return Color('lblue', this._name) + ' : ' + this._title;
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(' '));
235
264
  }
236
-
237
- _usages(os, title) {
238
- if(!os.length) return;
239
-
240
- return ['', title + ':']
241
- .concat(os.map(o => ` ${o._usage()}`))
242
- .join(EOL);
265
+ if (this._opts.length + this._args.length) {
266
+ res.push(['', '', Color('lred', this._fullName()), Color('lgreen', '[OPTIONS]'), Color('lpurple', '[ARGS]')].join(' '));
243
267
  }
244
-
245
- _fullTitle() {
246
- return `${this.isRootCmd? '' : this._cmd._fullTitle() + EOL}${this._title}`;
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;
247
280
  }
248
-
249
- _fullName() {
250
- return `${this.isRootCmd? '' : this._cmd._fullName() + ' '}${PATH.basename(this._name)}`;
281
+ res = ['', title + ':'];
282
+ for (_i = 0, _len = os.length; _i < _len; _i++) {
283
+ o = os[_i];
284
+ res.push(' ' + o._usage());
251
285
  }
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];
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
+ }
260
305
  }
261
-
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());
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
+ }
270
319
  }
320
+ };
271
321
 
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;
322
+ Cmd.prototype._parseCmd = function(argv, unparsed) {
323
+ var c, cmd, cmdDesc, e, i, optSeen, pkg;
324
+ if (unparsed == null) {
325
+ unparsed = [];
326
+ }
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;
283
341
  }
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
- }
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;
318
351
  }
319
-
320
- if(cmd) return cmd._parseCmd(argv, unparsed);
321
-
322
- unparsed.push(i);
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');
366
+ }
367
+ cmd = this._cmdsByName[i];
368
+ }
323
369
  }
324
-
325
- return { cmd : this, argv : unparsed };
370
+ if (cmd) {
371
+ return cmd._parseCmd(argv, unparsed);
372
+ }
373
+ }
374
+ unparsed.push(i);
326
375
  }
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;
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);
349
412
  }
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;
413
+ if (Q.isRejected(res = arg._parse(a, args))) {
414
+ return res;
361
415
  }
416
+ } else {
417
+ return this.reject("Unknown argument: " + a);
418
+ }
362
419
  }
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
+ };
363
463
 
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) {
364
467
  return {
365
- opts : this._setDefaults(opts, nonParsedOpts),
366
- args : this._setDefaults(args, nonParsedArgs)
468
+ cmd: p.cmd,
469
+ opts: r.opts,
470
+ args: r.args
367
471
  };
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);
368
502
  }
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
+ };
369
516
 
370
- _setDefaults(params, desc) {
371
- for(const item of desc)
372
- item._def &&
373
- !params.hasOwnProperty(item._name) &&
374
- item._saveVal(params, item._def);
375
-
376
- return params;
377
- }
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];
517
+ /**
518
+ Convenient function to run command from tests.
519
+ @param {Array} argv
520
+ @returns {Q.Promise}
521
+ */
392
522
 
393
- let res;
394
- for(const v of vals)
395
- if(Q.isRejected(res = item._saveVal(params, v)))
396
- return res;
397
- }
398
523
 
399
- return this._setDefaults(params, notExists);
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 = [];
400
542
  }
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
- })));
543
+ if (opts == null) {
544
+ opts = {};
409
545
  }
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);
418
- });
546
+ if (args == null) {
547
+ args = {};
419
548
  }
549
+ if (typeof cmds === 'string') {
550
+ cmds = cmds.split(' ');
551
+ }
552
+ if (arguments.length < 3) {
553
+ if (!Array.isArray(cmds)) {
554
+ args = opts;
555
+ opts = cmds;
556
+ cmds = [];
557
+ }
558
+ }
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
+ }
574
+ });
575
+ });
576
+ });
577
+ };
420
578
 
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));
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
+ */
430
587
 
431
- const cb = code =>
432
- res => res?
433
- this._exit(res.stack || res.toString(), (res.hasOwnProperty('exitCode')? res.exitCode : code) || 0) :
434
- this._exit();
435
588
 
436
- Q.when(this.do(argv), cb(0), cb(1)).done();
589
+ Cmd.prototype.reject = function(reason) {
590
+ return Q.reject(reason);
591
+ };
437
592
 
438
- return this;
439
- }
593
+ /**
594
+ Finish chain for current subcommand and return parent command instance.
595
+ @returns {COA.Cmd} parent command
596
+ */
440
597
 
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
- }
461
598
 
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
- }
599
+ Cmd.prototype.end = function() {
600
+ return this._cmd;
601
+ };
481
602
 
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
- };
603
+ return Cmd;
491
604
 
492
- module.exports = Cmd;
605
+ })();