1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | var EventEmitter = require("events").EventEmitter
|
8 | , Option = require("./option")
|
9 | , VantageUtil = require("./util")
|
10 | , _ = require("lodash")
|
11 | ;
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | var command = Command.prototype;
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 | module.exports = exports = Command;
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 | function Command(name, parent) {
|
35 | if (!(this instanceof Command)) { return new Command(); }
|
36 | this.commands = [];
|
37 | this.options = [];
|
38 | this._allowUnknownOption = false;
|
39 | this._args = [];
|
40 | this._aliases = [];
|
41 | this._name = name;
|
42 | this._relay = false;
|
43 | this._hidden = false;
|
44 | this._parent = parent;
|
45 | this._mode = false;
|
46 | this._catch = false;
|
47 | this._init = void 0;
|
48 | this._after = void 0;
|
49 | }
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 | command.option = function(flags, description, fn, defaultValue) {
|
63 |
|
64 | var self = this
|
65 | , option = new Option(flags, description)
|
66 | , oname = option.name()
|
67 | , name = _camelcase(oname);
|
68 |
|
69 |
|
70 | if (typeof fn !== "function") {
|
71 | if (fn instanceof RegExp) {
|
72 | var regex = fn;
|
73 | fn = function(val, def) {
|
74 | var m = regex.exec(val);
|
75 | return m ? m[0] : def;
|
76 | };
|
77 | }
|
78 | else {
|
79 | defaultValue = fn;
|
80 | fn = null;
|
81 | }
|
82 | }
|
83 |
|
84 |
|
85 | if (option.bool === false || option.optional || option.required) {
|
86 |
|
87 | if (option.bool === false) { defaultValue = true; }
|
88 |
|
89 | if (defaultValue !== undefined) { self[name] = defaultValue; }
|
90 | }
|
91 |
|
92 |
|
93 | this.options.push(option);
|
94 |
|
95 |
|
96 |
|
97 | this.on(oname, function(val) {
|
98 |
|
99 | if (val !== null && fn) { val = fn(val, self[name] === undefined
|
100 | ? defaultValue
|
101 | : self[name]);
|
102 | }
|
103 |
|
104 |
|
105 | if (typeof self[name] === "boolean" || typeof self[name] === "undefined") {
|
106 |
|
107 | if (val === null) {
|
108 | self[name] = option.bool
|
109 | ? defaultValue || true
|
110 | : false;
|
111 | } else {
|
112 | self[name] = val;
|
113 | }
|
114 | } else if (val !== null) {
|
115 |
|
116 | self[name] = val;
|
117 | }
|
118 | });
|
119 |
|
120 | return this;
|
121 | };
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 | command.action = function(fn) {
|
132 | var self = this;
|
133 | self._fn = fn;
|
134 | return this;
|
135 | };
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 | command.autocompletion = function(fn, cb) {
|
147 |
|
148 | if (!_.isFunction(fn)) {
|
149 | throw new Error("An invalid object type was passed into the first parameter of command.autocompletion: function expected.");
|
150 | }
|
151 |
|
152 | this._autocompletion = fn;
|
153 |
|
154 | return this;
|
155 | };
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 | command.init = function(fn) {
|
166 | var self = this;
|
167 | if (self._mode !== true) {
|
168 | throw Error("Cannot call init from a non-mode action.");
|
169 | }
|
170 | self._init = fn;
|
171 | return this;
|
172 | };
|
173 |
|
174 |
|
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 | command.delimiter = function(delimiter) {
|
184 | this._delimiter = delimiter;
|
185 | return this;
|
186 | };
|
187 |
|
188 |
|
189 |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 |
|
195 |
|
196 | command.alias = function(alias) {
|
197 | this._aliases.push(alias);
|
198 | return this;
|
199 | };
|
200 |
|
201 |
|
202 |
|
203 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 |
|
209 | command.description = function(str) {
|
210 | if (arguments.length === 0) { return this._description; }
|
211 | this._description = str;
|
212 | return this;
|
213 | };
|
214 |
|
215 |
|
216 |
|
217 |
|
218 |
|
219 |
|
220 |
|
221 |
|
222 |
|
223 | command.arguments = function (desc) {
|
224 | return this._parseExpectedArgs(desc.split(/ +/));
|
225 | };
|
226 |
|
227 |
|
228 |
|
229 |
|
230 |
|
231 |
|
232 |
|
233 |
|
234 | command.helpInformation = function() {
|
235 |
|
236 | var desc = []
|
237 | , cmdName = this._name
|
238 | , alias = ""
|
239 | ;
|
240 |
|
241 | if (this._description) {
|
242 | desc = [
|
243 | " " + this._description
|
244 | , ""
|
245 | ];
|
246 | }
|
247 |
|
248 | if (this._aliases.length > 0) {
|
249 | alias = " Alias: " + this._aliases.join(" | ") + "\n";
|
250 | }
|
251 | var usage = [
|
252 | ""
|
253 | , " Usage: " + cmdName + " " + this.usage()
|
254 | , ""
|
255 | ];
|
256 |
|
257 | var cmds = [];
|
258 |
|
259 | var options = [
|
260 | " Options:"
|
261 | , ""
|
262 | , "" + this.optionHelp().replace(/^/gm, " ")
|
263 | , ""
|
264 | ];
|
265 |
|
266 | var res = usage
|
267 | .concat(cmds)
|
268 | .concat(alias)
|
269 | .concat(desc)
|
270 | .concat(options)
|
271 | .join("\n");
|
272 |
|
273 | return res;
|
274 | };
|
275 |
|
276 |
|
277 |
|
278 |
|
279 |
|
280 |
|
281 |
|
282 |
|
283 | command.hidden = function() {
|
284 | this._hidden = true;
|
285 | return this;
|
286 | };
|
287 |
|
288 |
|
289 |
|
290 |
|
291 |
|
292 |
|
293 |
|
294 |
|
295 |
|
296 | command.usage = function(str) {
|
297 | var args = this._args.map(function(arg) {
|
298 | return VantageUtil.humanReadableArgName(arg);
|
299 | });
|
300 |
|
301 | var usage = "[options]"
|
302 | + (this.commands.length ? " [command]" : "")
|
303 | + (this._args.length ? " " + args.join(" ") : "");
|
304 |
|
305 | if (arguments.length === 0) { return (this._usage || usage); }
|
306 | this._usage = str;
|
307 |
|
308 | return this;
|
309 | };
|
310 |
|
311 |
|
312 |
|
313 |
|
314 |
|
315 |
|
316 |
|
317 |
|
318 | command.optionHelp = function() {
|
319 | var width = this._largestOptionLength();
|
320 |
|
321 |
|
322 | return [VantageUtil.pad("--help", width) + " " + "output usage information"]
|
323 | .concat(this.options.map(function(option) {
|
324 | return VantageUtil.pad(option.flags, width) + " " + option.description;
|
325 | }))
|
326 | .join("\n");
|
327 | };
|
328 |
|
329 |
|
330 |
|
331 |
|
332 |
|
333 |
|
334 |
|
335 |
|
336 | command._largestOptionLength = function() {
|
337 | return this.options.reduce(function(max, option) {
|
338 | return Math.max(max, option.flags.length);
|
339 | }, 0);
|
340 | };
|
341 |
|
342 |
|
343 |
|
344 |
|
345 |
|
346 |
|
347 |
|
348 |
|
349 |
|
350 | command.after = function(fn) {
|
351 | if (_.isFunction(fn)) {
|
352 | this._after = fn;
|
353 | }
|
354 | return this;
|
355 | };
|
356 |
|
357 |
|
358 |
|
359 |
|
360 |
|
361 |
|
362 |
|
363 |
|
364 |
|
365 | command._parseExpectedArgs = function(args) {
|
366 | if (!args.length) { return; }
|
367 | var self = this;
|
368 | args.forEach(function(arg) {
|
369 | var argDetails = {
|
370 | required: false,
|
371 | name: "",
|
372 | variadic: false
|
373 | };
|
374 |
|
375 | switch (arg[0]) {
|
376 | case "<":
|
377 | argDetails.required = true;
|
378 | argDetails.name = arg.slice(1, -1);
|
379 | break;
|
380 | case "[":
|
381 | argDetails.name = arg.slice(1, -1);
|
382 | break;
|
383 | }
|
384 |
|
385 | if (argDetails.name.length > 3 && argDetails.name.slice(-3) === "...") {
|
386 | argDetails.variadic = true;
|
387 | argDetails.name = argDetails.name.slice(0, -3);
|
388 | }
|
389 | if (argDetails.name) {
|
390 | self._args.push(argDetails);
|
391 | }
|
392 | });
|
393 | return;
|
394 | };
|
395 |
|
396 |
|
397 |
|
398 |
|
399 |
|
400 |
|
401 |
|
402 |
|
403 |
|
404 | function _camelcase(flag) {
|
405 | return flag.split("-").reduce(function(str, word) {
|
406 | return str + word[0].toUpperCase() + word.slice(1);
|
407 | });
|
408 | }
|
409 |
|
410 |
|
411 |
|
412 |
|
413 |
|
414 | command.__proto__ = EventEmitter.prototype;
|
415 |
|