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.
- package/.npmignore +6 -0
- package/.nyc_output/1f2a0db5a6d6559149db56d397f47cfc.json +1 -0
- package/.nyc_output/75b82d38f2186df930141082076e11c6.json +1 -0
- package/.travis.yml +9 -0
- package/GNUmakefile +34 -0
- package/README.md +1 -19
- package/coverage/base.css +212 -0
- package/coverage/coa/index.html +93 -0
- package/coverage/coa/index.js.html +68 -0
- package/coverage/coa/lib/arg.js.html +239 -0
- package/coverage/coa/lib/cmd.js.html +1556 -0
- package/coverage/coa/lib/coaobject.js.html +365 -0
- package/coverage/coa/lib/coaparam.js.html +440 -0
- package/coverage/coa/lib/color.js.html +131 -0
- package/coverage/coa/lib/completion.js.html +593 -0
- package/coverage/coa/lib/index.html +197 -0
- package/coverage/coa/lib/index.js.html +107 -0
- package/coverage/coa/lib/opt.js.html +524 -0
- package/coverage/coa/lib/shell.js.html +107 -0
- package/coverage/index.html +106 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +1 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +158 -0
- package/index.js +1 -1
- package/lib/arg.js +161 -44
- package/lib/cmd.js +547 -434
- package/lib/color.js +22 -19
- package/lib/completion.js +119 -161
- package/lib/index.js +10 -14
- package/lib/opt.js +313 -130
- package/lib/shell.js +13 -13
- package/package.json +14 -19
- package/qq.js +17 -0
- package/src/arg.coffee +130 -0
- package/src/cmd.coffee +456 -0
- package/src/color.coffee +25 -0
- package/src/completion.coffee +156 -0
- package/src/index.coffee +5 -0
- package/src/opt.coffee +243 -0
- package/src/shell.coffee +10 -0
- package/test/coa.js +496 -0
- package/test/mocha.opts +2 -0
- package/test/shell-test.js +60 -0
- package/tests/api-h.js +9 -0
- package/tests/h.js +6 -0
- package/LICENSE +0 -21
- package/lib/coaobject.js +0 -100
- package/lib/coaparam.js +0 -125
package/lib/cmd.js
CHANGED
@@ -1,492 +1,605 @@
|
|
1
|
-
|
1
|
+
// Generated by CoffeeScript 1.6.3
|
2
|
+
var Cmd, Color, PATH, Q, UTIL,
|
3
|
+
__slice = [].slice;
|
2
4
|
|
3
|
-
|
4
|
-
UTIL = require('util'),
|
5
|
-
PATH = require('path'),
|
6
|
-
EOL = require('os').EOL,
|
5
|
+
UTIL = require('util');
|
7
6
|
|
8
|
-
|
7
|
+
PATH = require('path');
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
Arg = require('./arg'),
|
14
|
-
completion = require('./completion');
|
9
|
+
Color = require('./color').Color;
|
10
|
+
|
11
|
+
Q = require('q');
|
15
12
|
|
16
13
|
/**
|
17
|
-
|
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
|
-
|
44
|
-
|
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
|
-
|
61
|
-
|
22
|
+
exports.Cmd = Cmd = (function() {
|
23
|
+
/**
|
24
|
+
@constructs
|
25
|
+
@param {COA.Cmd} [cmd] parent command
|
26
|
+
*/
|
62
27
|
|
63
|
-
|
28
|
+
function Cmd(cmd) {
|
29
|
+
if (!(this instanceof Cmd)) {
|
30
|
+
return new Cmd(cmd);
|
64
31
|
}
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
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
|
-
|
78
|
-
|
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
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|
-
|
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
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
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
|
-
|
119
|
-
|
120
|
-
|
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
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
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
|
-
|
170
|
-
|
171
|
-
|
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
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
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
|
-
|
194
|
-
|
195
|
-
|
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
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
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
|
-
|
234
|
-
|
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
|
-
|
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
|
-
|
246
|
-
|
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
|
-
|
250
|
-
|
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
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
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
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
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
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
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
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
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
|
-
|
321
|
-
|
322
|
-
|
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
|
-
|
370
|
+
if (cmd) {
|
371
|
+
return cmd._parseCmd(argv, unparsed);
|
372
|
+
}
|
373
|
+
}
|
374
|
+
unparsed.push(i);
|
326
375
|
}
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
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
|
-
|
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
|
-
|
366
|
-
|
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
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
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
|
-
|
589
|
+
Cmd.prototype.reject = function(reason) {
|
590
|
+
return Q.reject(reason);
|
591
|
+
};
|
437
592
|
|
438
|
-
|
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
|
-
|
463
|
-
|
464
|
-
|
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
|
-
|
605
|
+
})();
|