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/LICENSE +21 -0
- package/README.md +28 -33
- package/README.ru.md +316 -0
- package/index.js +1 -1
- package/lib/arg.js +44 -161
- package/lib/cmd.js +434 -547
- package/lib/coaobject.js +100 -0
- package/lib/coaparam.js +125 -0
- package/lib/color.js +19 -22
- package/lib/completion.js +161 -119
- package/lib/index.js +14 -10
- package/lib/opt.js +130 -313
- package/lib/shell.js +13 -13
- package/package.json +21 -17
- package/.npmignore +0 -6
- package/.travis.yml +0 -11
- package/GNUmakefile +0 -34
- package/src/arg.coffee +0 -130
- package/src/cmd.coffee +0 -456
- package/src/color.coffee +0 -25
- package/src/completion.coffee +0 -158
- package/src/index.coffee +0 -5
- package/src/opt.coffee +0 -243
- package/src/shell.coffee +0 -10
- package/test/coa.js +0 -496
- package/test/common.js +0 -1
- package/test/mocha.opts +0 -3
- package/test/shell-test.js +0 -60
- package/tests/api-h.js +0 -9
- package/tests/h.js +0 -6
package/lib/cmd.js
CHANGED
@@ -1,605 +1,492 @@
|
|
1
|
-
|
2
|
-
var Cmd, Color, PATH, Q, UTIL,
|
3
|
-
__slice = [].slice;
|
1
|
+
'use strict';
|
4
2
|
|
5
|
-
|
3
|
+
const
|
4
|
+
UTIL = require('util'),
|
5
|
+
PATH = require('path'),
|
6
|
+
EOL = require('os').EOL,
|
6
7
|
|
7
|
-
|
8
|
+
Q = require('q'),
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
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
|
-
|
107
|
-
|
108
|
-
|
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
|
-
|
113
|
-
|
114
|
-
|
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
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
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
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
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
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
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
|
-
|
262
|
-
|
263
|
-
|
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
|
-
|
266
|
-
|
232
|
+
|
233
|
+
_usage() {
|
234
|
+
return Color('lblue', this._name) + ' : ' + this._title;
|
267
235
|
}
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
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
|
-
|
282
|
-
|
283
|
-
|
284
|
-
res.push(' ' + o._usage());
|
244
|
+
|
245
|
+
_fullTitle() {
|
246
|
+
return `${this.isRootCmd? '' : this._cmd._fullTitle() + EOL}${this._title}`;
|
285
247
|
}
|
286
|
-
|
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
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
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
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
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
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
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
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
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
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
return cmd._parseCmd(argv, unparsed);
|
319
|
+
|
320
|
+
if(cmd) return cmd._parseCmd(argv, unparsed);
|
321
|
+
|
322
|
+
unparsed.push(i);
|
372
323
|
}
|
373
|
-
|
374
|
-
|
324
|
+
|
325
|
+
return { cmd : this, argv : unparsed };
|
375
326
|
}
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
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
|
-
|
414
|
-
|
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
|
-
|
469
|
-
|
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
|
-
|
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
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
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
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
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
|
-
|
560
|
-
|
561
|
-
return
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
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
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
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
|
-
|
590
|
-
return Q.reject(reason);
|
591
|
-
};
|
436
|
+
Q.when(this.do(argv), cb(0), cb(1)).done();
|
592
437
|
|
593
|
-
|
594
|
-
|
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
|
-
|
600
|
-
|
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
|
-
|
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;
|