spacedocs 0.0.2 → 0.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.
- data/README.md +6 -2
- data/{source → lib}/class.html.haml +1 -4
- data/{source → lib}/index.html.haml +2 -2
- data/{dox → lib/node_modules/dox}/History.md +0 -0
- data/{dox → lib/node_modules/dox}/Makefile +0 -0
- data/{dox → lib/node_modules/dox}/Readme.md +0 -0
- data/{dox → lib/node_modules/dox}/bin/dox +0 -0
- data/{dox → lib/node_modules/dox}/index.js +0 -0
- data/{dox → lib/node_modules/dox}/lib/dox.js +1 -10
- data/{dox → lib/node_modules/dox}/lib/utils.js +0 -0
- data/lib/node_modules/dox/node_modules/commander/History.md +99 -0
- data/lib/node_modules/dox/node_modules/commander/Makefile +7 -0
- data/lib/node_modules/dox/node_modules/commander/Readme.md +263 -0
- data/lib/node_modules/dox/node_modules/commander/index.js +2 -0
- data/lib/node_modules/dox/node_modules/commander/lib/commander.js +992 -0
- data/lib/node_modules/dox/node_modules/commander/package.json +38 -0
- data/lib/node_modules/dox/node_modules/github-flavored-markdown/README.md +17 -0
- data/lib/node_modules/dox/node_modules/github-flavored-markdown/_config.yml +3 -0
- data/lib/node_modules/dox/node_modules/github-flavored-markdown/_layouts/default.html +77 -0
- data/lib/node_modules/dox/node_modules/github-flavored-markdown/code.rb +67 -0
- data/lib/node_modules/dox/node_modules/github-flavored-markdown/images/gfm.png +0 -0
- data/lib/node_modules/dox/node_modules/github-flavored-markdown/index.md +78 -0
- data/lib/node_modules/dox/node_modules/github-flavored-markdown/package.json +27 -0
- data/lib/node_modules/dox/node_modules/github-flavored-markdown/preview.md +27 -0
- data/lib/node_modules/dox/node_modules/github-flavored-markdown/sample_content.html +169 -0
- data/lib/node_modules/dox/node_modules/github-flavored-markdown/scripts/preview.js +18 -0
- data/lib/node_modules/dox/node_modules/github-flavored-markdown/scripts/showdown.js +1414 -0
- data/lib/node_modules/dox/node_modules/github-flavored-markdown/stylesheets/screen.css +20 -0
- data/lib/node_modules/dox/package.json +43 -0
- data/lib/spacedocs.rb +42 -53
- data/lib/{assets/stylesheets/spacedocs/docs.css.sass → spacedocs.sass} +0 -0
- data/lib/spacedocs/version.rb +1 -1
- metadata +116 -25
- data/dox/package.json +0 -16
- data/dox/test/dox.test.js +0 -287
- data/dox/test/fixtures/a.js +0 -12
- data/dox/test/fixtures/b.js +0 -26
- data/dox/test/fixtures/c.js +0 -266
- data/dox/test/fixtures/d.js +0 -15
- data/dox/test/fixtures/titles.js +0 -14
- data/lib/spacedocs/engine.rb +0 -6
@@ -0,0 +1,992 @@
|
|
1
|
+
|
2
|
+
/*!
|
3
|
+
* commander
|
4
|
+
* Copyright(c) 2011 TJ Holowaychuk <tj@vision-media.ca>
|
5
|
+
* MIT Licensed
|
6
|
+
*/
|
7
|
+
|
8
|
+
/**
|
9
|
+
* Module dependencies.
|
10
|
+
*/
|
11
|
+
|
12
|
+
var EventEmitter = require('events').EventEmitter
|
13
|
+
, path = require('path')
|
14
|
+
, tty = require('tty')
|
15
|
+
, basename = path.basename;
|
16
|
+
|
17
|
+
/**
|
18
|
+
* Expose the root command.
|
19
|
+
*/
|
20
|
+
|
21
|
+
exports = module.exports = new Command;
|
22
|
+
|
23
|
+
/**
|
24
|
+
* Expose `Command`.
|
25
|
+
*/
|
26
|
+
|
27
|
+
exports.Command = Command;
|
28
|
+
|
29
|
+
/**
|
30
|
+
* Expose `Option`.
|
31
|
+
*/
|
32
|
+
|
33
|
+
exports.Option = Option;
|
34
|
+
|
35
|
+
/**
|
36
|
+
* Initialize a new `Option` with the given `flags` and `description`.
|
37
|
+
*
|
38
|
+
* @param {String} flags
|
39
|
+
* @param {String} description
|
40
|
+
* @api public
|
41
|
+
*/
|
42
|
+
|
43
|
+
function Option(flags, description) {
|
44
|
+
this.flags = flags;
|
45
|
+
this.required = ~flags.indexOf('<');
|
46
|
+
this.optional = ~flags.indexOf('[');
|
47
|
+
this.bool = !~flags.indexOf('-no-');
|
48
|
+
flags = flags.split(/[ ,|]+/);
|
49
|
+
if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift();
|
50
|
+
this.long = flags.shift();
|
51
|
+
this.description = description;
|
52
|
+
}
|
53
|
+
|
54
|
+
/**
|
55
|
+
* Return option name.
|
56
|
+
*
|
57
|
+
* @return {String}
|
58
|
+
* @api private
|
59
|
+
*/
|
60
|
+
|
61
|
+
Option.prototype.name = function(){
|
62
|
+
return this.long
|
63
|
+
.replace('--', '')
|
64
|
+
.replace('no-', '');
|
65
|
+
};
|
66
|
+
|
67
|
+
/**
|
68
|
+
* Check if `arg` matches the short or long flag.
|
69
|
+
*
|
70
|
+
* @param {String} arg
|
71
|
+
* @return {Boolean}
|
72
|
+
* @api private
|
73
|
+
*/
|
74
|
+
|
75
|
+
Option.prototype.is = function(arg){
|
76
|
+
return arg == this.short
|
77
|
+
|| arg == this.long;
|
78
|
+
};
|
79
|
+
|
80
|
+
/**
|
81
|
+
* Initialize a new `Command`.
|
82
|
+
*
|
83
|
+
* @param {String} name
|
84
|
+
* @api public
|
85
|
+
*/
|
86
|
+
|
87
|
+
function Command(name) {
|
88
|
+
this.commands = [];
|
89
|
+
this.options = [];
|
90
|
+
this.args = [];
|
91
|
+
this.name = name;
|
92
|
+
}
|
93
|
+
|
94
|
+
/**
|
95
|
+
* Inherit from `EventEmitter.prototype`.
|
96
|
+
*/
|
97
|
+
|
98
|
+
Command.prototype.__proto__ = EventEmitter.prototype;
|
99
|
+
|
100
|
+
/**
|
101
|
+
* Add command `name`.
|
102
|
+
*
|
103
|
+
* The `.action()` callback is invoked when the
|
104
|
+
* command `name` is specified via __ARGV__,
|
105
|
+
* and the remaining arguments are applied to the
|
106
|
+
* function for access.
|
107
|
+
*
|
108
|
+
* When the `name` is "*" an un-matched command
|
109
|
+
* will be passed as the first arg, followed by
|
110
|
+
* the rest of __ARGV__ remaining.
|
111
|
+
*
|
112
|
+
* Examples:
|
113
|
+
*
|
114
|
+
* program
|
115
|
+
* .version('0.0.1')
|
116
|
+
* .option('-C, --chdir <path>', 'change the working directory')
|
117
|
+
* .option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')
|
118
|
+
* .option('-T, --no-tests', 'ignore test hook')
|
119
|
+
*
|
120
|
+
* program
|
121
|
+
* .command('setup')
|
122
|
+
* .description('run remote setup commands')
|
123
|
+
* .action(function(){
|
124
|
+
* console.log('setup');
|
125
|
+
* });
|
126
|
+
*
|
127
|
+
* program
|
128
|
+
* .command('exec <cmd>')
|
129
|
+
* .description('run the given remote command')
|
130
|
+
* .action(function(cmd){
|
131
|
+
* console.log('exec "%s"', cmd);
|
132
|
+
* });
|
133
|
+
*
|
134
|
+
* program
|
135
|
+
* .command('*')
|
136
|
+
* .description('deploy the given env')
|
137
|
+
* .action(function(env){
|
138
|
+
* console.log('deploying "%s"', env);
|
139
|
+
* });
|
140
|
+
*
|
141
|
+
* program.parse(process.argv);
|
142
|
+
*
|
143
|
+
* @param {String} name
|
144
|
+
* @return {Command} the new command
|
145
|
+
* @api public
|
146
|
+
*/
|
147
|
+
|
148
|
+
Command.prototype.command = function(name){
|
149
|
+
var args = name.split(/ +/);
|
150
|
+
var cmd = new Command(args.shift());
|
151
|
+
this.commands.push(cmd);
|
152
|
+
cmd.parseExpectedArgs(args);
|
153
|
+
cmd.parent = this;
|
154
|
+
return cmd;
|
155
|
+
};
|
156
|
+
|
157
|
+
/**
|
158
|
+
* Parse expected `args`.
|
159
|
+
*
|
160
|
+
* For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`.
|
161
|
+
*
|
162
|
+
* @param {Array} args
|
163
|
+
* @return {Command} for chaining
|
164
|
+
* @api public
|
165
|
+
*/
|
166
|
+
|
167
|
+
Command.prototype.parseExpectedArgs = function(args){
|
168
|
+
if (!args.length) return;
|
169
|
+
var self = this;
|
170
|
+
args.forEach(function(arg){
|
171
|
+
switch (arg[0]) {
|
172
|
+
case '<':
|
173
|
+
self.args.push({ required: true, name: arg.slice(1, -1) });
|
174
|
+
break;
|
175
|
+
case '[':
|
176
|
+
self.args.push({ required: false, name: arg.slice(1, -1) });
|
177
|
+
break;
|
178
|
+
}
|
179
|
+
});
|
180
|
+
return this;
|
181
|
+
};
|
182
|
+
|
183
|
+
/**
|
184
|
+
* Register callback `fn` for the command.
|
185
|
+
*
|
186
|
+
* Examples:
|
187
|
+
*
|
188
|
+
* program
|
189
|
+
* .command('help')
|
190
|
+
* .description('display verbose help')
|
191
|
+
* .action(function(){
|
192
|
+
* // output help here
|
193
|
+
* });
|
194
|
+
*
|
195
|
+
* @param {Function} fn
|
196
|
+
* @return {Command} for chaining
|
197
|
+
* @api public
|
198
|
+
*/
|
199
|
+
|
200
|
+
Command.prototype.action = function(fn){
|
201
|
+
var self = this;
|
202
|
+
this.parent.on(this.name, function(args, unknown){
|
203
|
+
// Parse any so-far unknown options
|
204
|
+
unknown = unknown || [];
|
205
|
+
var parsed = self.parseOptions(unknown);
|
206
|
+
|
207
|
+
// Output help if necessary
|
208
|
+
outputHelpIfNecessary(self, parsed.unknown);
|
209
|
+
|
210
|
+
// If there are still any unknown options, then we simply
|
211
|
+
// die, unless someone asked for help, in which case we give it
|
212
|
+
// to them, and then we die.
|
213
|
+
if (parsed.unknown.length > 0) {
|
214
|
+
self.unknownOption(parsed.unknown[0]);
|
215
|
+
}
|
216
|
+
|
217
|
+
self.args.forEach(function(arg, i){
|
218
|
+
if (arg.required && null == args[i]) {
|
219
|
+
self.missingArgument(arg.name);
|
220
|
+
}
|
221
|
+
});
|
222
|
+
|
223
|
+
// Always append ourselves to the end of the arguments,
|
224
|
+
// to make sure we match the number of arguments the user
|
225
|
+
// expects
|
226
|
+
if (self.args.length) {
|
227
|
+
args[self.args.length] = self;
|
228
|
+
} else {
|
229
|
+
args.push(self);
|
230
|
+
}
|
231
|
+
|
232
|
+
fn.apply(this, args);
|
233
|
+
});
|
234
|
+
return this;
|
235
|
+
};
|
236
|
+
|
237
|
+
/**
|
238
|
+
* Define option with `flags`, `description` and optional
|
239
|
+
* coercion `fn`.
|
240
|
+
*
|
241
|
+
* The `flags` string should contain both the short and long flags,
|
242
|
+
* separated by comma, a pipe or space. The following are all valid
|
243
|
+
* all will output this way when `--help` is used.
|
244
|
+
*
|
245
|
+
* "-p, --pepper"
|
246
|
+
* "-p|--pepper"
|
247
|
+
* "-p --pepper"
|
248
|
+
*
|
249
|
+
* Examples:
|
250
|
+
*
|
251
|
+
* // simple boolean defaulting to false
|
252
|
+
* program.option('-p, --pepper', 'add pepper');
|
253
|
+
*
|
254
|
+
* --pepper
|
255
|
+
* program.pepper
|
256
|
+
* // => Boolean
|
257
|
+
*
|
258
|
+
* // simple boolean defaulting to false
|
259
|
+
* program.option('-C, --no-cheese', 'remove cheese');
|
260
|
+
*
|
261
|
+
* program.cheese
|
262
|
+
* // => true
|
263
|
+
*
|
264
|
+
* --no-cheese
|
265
|
+
* program.cheese
|
266
|
+
* // => true
|
267
|
+
*
|
268
|
+
* // required argument
|
269
|
+
* program.option('-C, --chdir <path>', 'change the working directory');
|
270
|
+
*
|
271
|
+
* --chdir /tmp
|
272
|
+
* program.chdir
|
273
|
+
* // => "/tmp"
|
274
|
+
*
|
275
|
+
* // optional argument
|
276
|
+
* program.option('-c, --cheese [type]', 'add cheese [marble]');
|
277
|
+
*
|
278
|
+
* @param {String} flags
|
279
|
+
* @param {String} description
|
280
|
+
* @param {Function|Mixed} fn or default
|
281
|
+
* @param {Mixed} defaultValue
|
282
|
+
* @return {Command} for chaining
|
283
|
+
* @api public
|
284
|
+
*/
|
285
|
+
|
286
|
+
Command.prototype.option = function(flags, description, fn, defaultValue){
|
287
|
+
var self = this
|
288
|
+
, option = new Option(flags, description)
|
289
|
+
, oname = option.name()
|
290
|
+
, name = camelcase(oname);
|
291
|
+
|
292
|
+
// default as 3rd arg
|
293
|
+
if ('function' != typeof fn) defaultValue = fn, fn = null;
|
294
|
+
|
295
|
+
// preassign default value only for --no-*, [optional], or <required>
|
296
|
+
if (false == option.bool || option.optional || option.required) {
|
297
|
+
// when --no-* we make sure default is true
|
298
|
+
if (false == option.bool) defaultValue = true;
|
299
|
+
// preassign only if we have a default
|
300
|
+
if (undefined !== defaultValue) self[name] = defaultValue;
|
301
|
+
}
|
302
|
+
|
303
|
+
// register the option
|
304
|
+
this.options.push(option);
|
305
|
+
|
306
|
+
// when it's passed assign the value
|
307
|
+
// and conditionally invoke the callback
|
308
|
+
this.on(oname, function(val){
|
309
|
+
// coercion
|
310
|
+
if (null != val && fn) val = fn(val);
|
311
|
+
|
312
|
+
// unassigned or bool
|
313
|
+
if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) {
|
314
|
+
// if no value, bool true, and we have a default, then use it!
|
315
|
+
if (null == val) {
|
316
|
+
self[name] = option.bool
|
317
|
+
? defaultValue || true
|
318
|
+
: false;
|
319
|
+
} else {
|
320
|
+
self[name] = val;
|
321
|
+
}
|
322
|
+
} else if (null !== val) {
|
323
|
+
// reassign
|
324
|
+
self[name] = val;
|
325
|
+
}
|
326
|
+
});
|
327
|
+
|
328
|
+
return this;
|
329
|
+
};
|
330
|
+
|
331
|
+
/**
|
332
|
+
* Parse `argv`, settings options and invoking commands when defined.
|
333
|
+
*
|
334
|
+
* @param {Array} argv
|
335
|
+
* @return {Command} for chaining
|
336
|
+
* @api public
|
337
|
+
*/
|
338
|
+
|
339
|
+
Command.prototype.parse = function(argv){
|
340
|
+
// store raw args
|
341
|
+
this.rawArgs = argv;
|
342
|
+
|
343
|
+
// guess name
|
344
|
+
if (!this.name) this.name = basename(argv[1]);
|
345
|
+
|
346
|
+
// process argv
|
347
|
+
var parsed = this.parseOptions(this.normalize(argv.slice(2)));
|
348
|
+
this.args = parsed.args;
|
349
|
+
return this.parseArgs(this.args, parsed.unknown);
|
350
|
+
};
|
351
|
+
|
352
|
+
/**
|
353
|
+
* Normalize `args`, splitting joined short flags. For example
|
354
|
+
* the arg "-abc" is equivalent to "-a -b -c".
|
355
|
+
*
|
356
|
+
* @param {Array} args
|
357
|
+
* @return {Array}
|
358
|
+
* @api private
|
359
|
+
*/
|
360
|
+
|
361
|
+
Command.prototype.normalize = function(args){
|
362
|
+
var ret = []
|
363
|
+
, arg;
|
364
|
+
|
365
|
+
for (var i = 0, len = args.length; i < len; ++i) {
|
366
|
+
arg = args[i];
|
367
|
+
if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) {
|
368
|
+
arg.slice(1).split('').forEach(function(c){
|
369
|
+
ret.push('-' + c);
|
370
|
+
});
|
371
|
+
} else {
|
372
|
+
ret.push(arg);
|
373
|
+
}
|
374
|
+
}
|
375
|
+
|
376
|
+
return ret;
|
377
|
+
};
|
378
|
+
|
379
|
+
/**
|
380
|
+
* Parse command `args`.
|
381
|
+
*
|
382
|
+
* When listener(s) are available those
|
383
|
+
* callbacks are invoked, otherwise the "*"
|
384
|
+
* event is emitted and those actions are invoked.
|
385
|
+
*
|
386
|
+
* @param {Array} args
|
387
|
+
* @return {Command} for chaining
|
388
|
+
* @api private
|
389
|
+
*/
|
390
|
+
|
391
|
+
Command.prototype.parseArgs = function(args, unknown){
|
392
|
+
var cmds = this.commands
|
393
|
+
, len = cmds.length
|
394
|
+
, name;
|
395
|
+
|
396
|
+
if (args.length) {
|
397
|
+
name = args[0];
|
398
|
+
if (this.listeners(name).length) {
|
399
|
+
this.emit(args.shift(), args, unknown);
|
400
|
+
} else {
|
401
|
+
this.emit('*', args);
|
402
|
+
}
|
403
|
+
} else {
|
404
|
+
outputHelpIfNecessary(this, unknown);
|
405
|
+
|
406
|
+
// If there were no args and we have unknown options,
|
407
|
+
// then they are extraneous and we need to error.
|
408
|
+
if (unknown.length > 0) {
|
409
|
+
this.unknownOption(unknown[0]);
|
410
|
+
}
|
411
|
+
}
|
412
|
+
|
413
|
+
return this;
|
414
|
+
};
|
415
|
+
|
416
|
+
/**
|
417
|
+
* Return an option matching `arg` if any.
|
418
|
+
*
|
419
|
+
* @param {String} arg
|
420
|
+
* @return {Option}
|
421
|
+
* @api private
|
422
|
+
*/
|
423
|
+
|
424
|
+
Command.prototype.optionFor = function(arg){
|
425
|
+
for (var i = 0, len = this.options.length; i < len; ++i) {
|
426
|
+
if (this.options[i].is(arg)) {
|
427
|
+
return this.options[i];
|
428
|
+
}
|
429
|
+
}
|
430
|
+
};
|
431
|
+
|
432
|
+
/**
|
433
|
+
* Parse options from `argv` returning `argv`
|
434
|
+
* void of these options.
|
435
|
+
*
|
436
|
+
* @param {Array} argv
|
437
|
+
* @return {Array}
|
438
|
+
* @api public
|
439
|
+
*/
|
440
|
+
|
441
|
+
Command.prototype.parseOptions = function(argv){
|
442
|
+
var args = []
|
443
|
+
, len = argv.length
|
444
|
+
, literal
|
445
|
+
, option
|
446
|
+
, arg;
|
447
|
+
|
448
|
+
var unknownOptions = [];
|
449
|
+
|
450
|
+
// parse options
|
451
|
+
for (var i = 0; i < len; ++i) {
|
452
|
+
arg = argv[i];
|
453
|
+
|
454
|
+
// literal args after --
|
455
|
+
if ('--' == arg) {
|
456
|
+
literal = true;
|
457
|
+
continue;
|
458
|
+
}
|
459
|
+
|
460
|
+
if (literal) {
|
461
|
+
args.push(arg);
|
462
|
+
continue;
|
463
|
+
}
|
464
|
+
|
465
|
+
// find matching Option
|
466
|
+
option = this.optionFor(arg);
|
467
|
+
|
468
|
+
// option is defined
|
469
|
+
if (option) {
|
470
|
+
// requires arg
|
471
|
+
if (option.required) {
|
472
|
+
arg = argv[++i];
|
473
|
+
if (null == arg) return this.optionMissingArgument(option);
|
474
|
+
if ('-' == arg[0]) return this.optionMissingArgument(option, arg);
|
475
|
+
this.emit(option.name(), arg);
|
476
|
+
// optional arg
|
477
|
+
} else if (option.optional) {
|
478
|
+
arg = argv[i+1];
|
479
|
+
if (null == arg || '-' == arg[0]) {
|
480
|
+
arg = null;
|
481
|
+
} else {
|
482
|
+
++i;
|
483
|
+
}
|
484
|
+
this.emit(option.name(), arg);
|
485
|
+
// bool
|
486
|
+
} else {
|
487
|
+
this.emit(option.name());
|
488
|
+
}
|
489
|
+
continue;
|
490
|
+
}
|
491
|
+
|
492
|
+
// looks like an option
|
493
|
+
if (arg.length > 1 && '-' == arg[0]) {
|
494
|
+
unknownOptions.push(arg);
|
495
|
+
|
496
|
+
// If the next argument looks like it might be
|
497
|
+
// an argument for this option, we pass it on.
|
498
|
+
// If it isn't, then it'll simply be ignored
|
499
|
+
if (argv[i+1] && '-' != argv[i+1][0]) {
|
500
|
+
unknownOptions.push(argv[++i]);
|
501
|
+
}
|
502
|
+
continue;
|
503
|
+
}
|
504
|
+
|
505
|
+
// arg
|
506
|
+
args.push(arg);
|
507
|
+
}
|
508
|
+
|
509
|
+
return { args: args, unknown: unknownOptions };
|
510
|
+
};
|
511
|
+
|
512
|
+
/**
|
513
|
+
* Argument `name` is missing.
|
514
|
+
*
|
515
|
+
* @param {String} name
|
516
|
+
* @api private
|
517
|
+
*/
|
518
|
+
|
519
|
+
Command.prototype.missingArgument = function(name){
|
520
|
+
console.error();
|
521
|
+
console.error(" error: missing required argument `%s'", name);
|
522
|
+
console.error();
|
523
|
+
process.exit(1);
|
524
|
+
};
|
525
|
+
|
526
|
+
/**
|
527
|
+
* `Option` is missing an argument, but received `flag` or nothing.
|
528
|
+
*
|
529
|
+
* @param {String} option
|
530
|
+
* @param {String} flag
|
531
|
+
* @api private
|
532
|
+
*/
|
533
|
+
|
534
|
+
Command.prototype.optionMissingArgument = function(option, flag){
|
535
|
+
console.error();
|
536
|
+
if (flag) {
|
537
|
+
console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag);
|
538
|
+
} else {
|
539
|
+
console.error(" error: option `%s' argument missing", option.flags);
|
540
|
+
}
|
541
|
+
console.error();
|
542
|
+
process.exit(1);
|
543
|
+
};
|
544
|
+
|
545
|
+
/**
|
546
|
+
* Unknown option `flag`.
|
547
|
+
*
|
548
|
+
* @param {String} flag
|
549
|
+
* @api private
|
550
|
+
*/
|
551
|
+
|
552
|
+
Command.prototype.unknownOption = function(flag){
|
553
|
+
console.error();
|
554
|
+
console.error(" error: unknown option `%s'", flag);
|
555
|
+
console.error();
|
556
|
+
process.exit(1);
|
557
|
+
};
|
558
|
+
|
559
|
+
/**
|
560
|
+
* Set the program version to `str`.
|
561
|
+
*
|
562
|
+
* This method auto-registers the "-V, --version" flag
|
563
|
+
* which will print the version number when passed.
|
564
|
+
*
|
565
|
+
* @param {String} str
|
566
|
+
* @param {String} flags
|
567
|
+
* @return {Command} for chaining
|
568
|
+
* @api public
|
569
|
+
*/
|
570
|
+
|
571
|
+
Command.prototype.version = function(str, flags){
|
572
|
+
if (0 == arguments.length) return this._version;
|
573
|
+
this._version = str;
|
574
|
+
flags = flags || '-V, --version';
|
575
|
+
this.option(flags, 'output the version number');
|
576
|
+
this.on('version', function(){
|
577
|
+
console.log(str);
|
578
|
+
process.exit(0);
|
579
|
+
});
|
580
|
+
return this;
|
581
|
+
};
|
582
|
+
|
583
|
+
/**
|
584
|
+
* Set the description `str`.
|
585
|
+
*
|
586
|
+
* @param {String} str
|
587
|
+
* @return {String|Command}
|
588
|
+
* @api public
|
589
|
+
*/
|
590
|
+
|
591
|
+
Command.prototype.description = function(str){
|
592
|
+
if (0 == arguments.length) return this._description;
|
593
|
+
this._description = str;
|
594
|
+
return this;
|
595
|
+
};
|
596
|
+
|
597
|
+
/**
|
598
|
+
* Set / get the command usage `str`.
|
599
|
+
*
|
600
|
+
* @param {String} str
|
601
|
+
* @return {String|Command}
|
602
|
+
* @api public
|
603
|
+
*/
|
604
|
+
|
605
|
+
Command.prototype.usage = function(str){
|
606
|
+
var args = this.args.map(function(arg){
|
607
|
+
return arg.required
|
608
|
+
? '<' + arg.name + '>'
|
609
|
+
: '[' + arg.name + ']';
|
610
|
+
});
|
611
|
+
|
612
|
+
var usage = '[options'
|
613
|
+
+ (this.commands.length ? '] [command' : '')
|
614
|
+
+ ']'
|
615
|
+
+ (this.args.length ? ' ' + args : '');
|
616
|
+
if (0 == arguments.length) return this._usage || usage;
|
617
|
+
this._usage = str;
|
618
|
+
|
619
|
+
return this;
|
620
|
+
};
|
621
|
+
|
622
|
+
/**
|
623
|
+
* Return the largest option length.
|
624
|
+
*
|
625
|
+
* @return {Number}
|
626
|
+
* @api private
|
627
|
+
*/
|
628
|
+
|
629
|
+
Command.prototype.largestOptionLength = function(){
|
630
|
+
return this.options.reduce(function(max, option){
|
631
|
+
return Math.max(max, option.flags.length);
|
632
|
+
}, 0);
|
633
|
+
};
|
634
|
+
|
635
|
+
/**
|
636
|
+
* Return help for options.
|
637
|
+
*
|
638
|
+
* @return {String}
|
639
|
+
* @api private
|
640
|
+
*/
|
641
|
+
|
642
|
+
Command.prototype.optionHelp = function(){
|
643
|
+
var width = this.largestOptionLength();
|
644
|
+
|
645
|
+
// Prepend the help information
|
646
|
+
return [pad('-h, --help', width) + ' ' + 'output usage information']
|
647
|
+
.concat(this.options.map(function(option){
|
648
|
+
return pad(option.flags, width)
|
649
|
+
+ ' ' + option.description;
|
650
|
+
}))
|
651
|
+
.join('\n');
|
652
|
+
};
|
653
|
+
|
654
|
+
/**
|
655
|
+
* Return command help documentation.
|
656
|
+
*
|
657
|
+
* @return {String}
|
658
|
+
* @api private
|
659
|
+
*/
|
660
|
+
|
661
|
+
Command.prototype.commandHelp = function(){
|
662
|
+
if (!this.commands.length) return '';
|
663
|
+
return [
|
664
|
+
''
|
665
|
+
, ' Commands:'
|
666
|
+
, ''
|
667
|
+
, this.commands.map(function(cmd){
|
668
|
+
var args = cmd.args.map(function(arg){
|
669
|
+
return arg.required
|
670
|
+
? '<' + arg.name + '>'
|
671
|
+
: '[' + arg.name + ']';
|
672
|
+
}).join(' ');
|
673
|
+
|
674
|
+
return cmd.name
|
675
|
+
+ (cmd.options.length
|
676
|
+
? ' [options]'
|
677
|
+
: '') + ' ' + args
|
678
|
+
+ (cmd.description()
|
679
|
+
? '\n' + cmd.description()
|
680
|
+
: '');
|
681
|
+
}).join('\n\n').replace(/^/gm, ' ')
|
682
|
+
, ''
|
683
|
+
].join('\n');
|
684
|
+
};
|
685
|
+
|
686
|
+
/**
|
687
|
+
* Return program help documentation.
|
688
|
+
*
|
689
|
+
* @return {String}
|
690
|
+
* @api private
|
691
|
+
*/
|
692
|
+
|
693
|
+
Command.prototype.helpInformation = function(){
|
694
|
+
return [
|
695
|
+
''
|
696
|
+
, ' Usage: ' + this.name + ' ' + this.usage()
|
697
|
+
, '' + this.commandHelp()
|
698
|
+
, ' Options:'
|
699
|
+
, ''
|
700
|
+
, '' + this.optionHelp().replace(/^/gm, ' ')
|
701
|
+
, ''
|
702
|
+
, ''
|
703
|
+
].join('\n');
|
704
|
+
};
|
705
|
+
|
706
|
+
/**
|
707
|
+
* Prompt for a `Number`.
|
708
|
+
*
|
709
|
+
* @param {String} str
|
710
|
+
* @param {Function} fn
|
711
|
+
* @api private
|
712
|
+
*/
|
713
|
+
|
714
|
+
Command.prototype.promptForNumber = function(str, fn){
|
715
|
+
var self = this;
|
716
|
+
this.promptSingleLine(str, function parseNumber(val){
|
717
|
+
val = Number(val);
|
718
|
+
if (isNaN(val)) return self.promptSingleLine(str + '(must be a number) ', parseNumber);
|
719
|
+
fn(val);
|
720
|
+
});
|
721
|
+
};
|
722
|
+
|
723
|
+
/**
|
724
|
+
* Prompt for a `Date`.
|
725
|
+
*
|
726
|
+
* @param {String} str
|
727
|
+
* @param {Function} fn
|
728
|
+
* @api private
|
729
|
+
*/
|
730
|
+
|
731
|
+
Command.prototype.promptForDate = function(str, fn){
|
732
|
+
var self = this;
|
733
|
+
this.promptSingleLine(str, function parseDate(val){
|
734
|
+
val = new Date(val);
|
735
|
+
if (isNaN(val.getTime())) return self.promptSingleLine(str + '(must be a date) ', parseDate);
|
736
|
+
fn(val);
|
737
|
+
});
|
738
|
+
};
|
739
|
+
|
740
|
+
/**
|
741
|
+
* Single-line prompt.
|
742
|
+
*
|
743
|
+
* @param {String} str
|
744
|
+
* @param {Function} fn
|
745
|
+
* @api private
|
746
|
+
*/
|
747
|
+
|
748
|
+
Command.prototype.promptSingleLine = function(str, fn){
|
749
|
+
if ('function' == typeof arguments[2]) {
|
750
|
+
return this['promptFor' + (fn.name || fn)](str, arguments[2]);
|
751
|
+
}
|
752
|
+
|
753
|
+
process.stdout.write(str);
|
754
|
+
process.stdin.setEncoding('utf8');
|
755
|
+
process.stdin.once('data', function(val){
|
756
|
+
fn(val.trim());
|
757
|
+
}).resume();
|
758
|
+
};
|
759
|
+
|
760
|
+
/**
|
761
|
+
* Multi-line prompt.
|
762
|
+
*
|
763
|
+
* @param {String} str
|
764
|
+
* @param {Function} fn
|
765
|
+
* @api private
|
766
|
+
*/
|
767
|
+
|
768
|
+
Command.prototype.promptMultiLine = function(str, fn){
|
769
|
+
var buf = [];
|
770
|
+
console.log(str);
|
771
|
+
process.stdin.setEncoding('utf8');
|
772
|
+
process.stdin.on('data', function(val){
|
773
|
+
if ('\n' == val || '\r\n' == val) {
|
774
|
+
process.stdin.removeAllListeners('data');
|
775
|
+
fn(buf.join('\n'));
|
776
|
+
} else {
|
777
|
+
buf.push(val.trimRight());
|
778
|
+
}
|
779
|
+
}).resume();
|
780
|
+
};
|
781
|
+
|
782
|
+
/**
|
783
|
+
* Prompt `str` and callback `fn(val)`
|
784
|
+
*
|
785
|
+
* Commander supports single-line and multi-line prompts.
|
786
|
+
* To issue a single-line prompt simply add white-space
|
787
|
+
* to the end of `str`, something like "name: ", whereas
|
788
|
+
* for a multi-line prompt omit this "description:".
|
789
|
+
*
|
790
|
+
*
|
791
|
+
* Examples:
|
792
|
+
*
|
793
|
+
* program.prompt('Username: ', function(name){
|
794
|
+
* console.log('hi %s', name);
|
795
|
+
* });
|
796
|
+
*
|
797
|
+
* program.prompt('Description:', function(desc){
|
798
|
+
* console.log('description was "%s"', desc.trim());
|
799
|
+
* });
|
800
|
+
*
|
801
|
+
* @param {String} str
|
802
|
+
* @param {Function} fn
|
803
|
+
* @api public
|
804
|
+
*/
|
805
|
+
|
806
|
+
Command.prototype.prompt = function(str, fn){
|
807
|
+
if (/ $/.test(str)) return this.promptSingleLine.apply(this, arguments);
|
808
|
+
this.promptMultiLine(str, fn);
|
809
|
+
};
|
810
|
+
|
811
|
+
/**
|
812
|
+
* Prompt for password with `str`, `mask` char and callback `fn(val)`.
|
813
|
+
*
|
814
|
+
* The mask string defaults to '', aka no output is
|
815
|
+
* written while typing, you may want to use "*" etc.
|
816
|
+
*
|
817
|
+
* Examples:
|
818
|
+
*
|
819
|
+
* program.password('Password: ', function(pass){
|
820
|
+
* console.log('got "%s"', pass);
|
821
|
+
* process.stdin.destroy();
|
822
|
+
* });
|
823
|
+
*
|
824
|
+
* program.password('Password: ', '*', function(pass){
|
825
|
+
* console.log('got "%s"', pass);
|
826
|
+
* process.stdin.destroy();
|
827
|
+
* });
|
828
|
+
*
|
829
|
+
* @param {String} str
|
830
|
+
* @param {String} mask
|
831
|
+
* @param {Function} fn
|
832
|
+
* @api public
|
833
|
+
*/
|
834
|
+
|
835
|
+
Command.prototype.password = function(str, mask, fn){
|
836
|
+
var self = this
|
837
|
+
, buf = '';
|
838
|
+
|
839
|
+
// default mask
|
840
|
+
if ('function' == typeof mask) {
|
841
|
+
fn = mask;
|
842
|
+
mask = '';
|
843
|
+
}
|
844
|
+
|
845
|
+
process.stdin.resume();
|
846
|
+
tty.setRawMode(true);
|
847
|
+
process.stdout.write(str);
|
848
|
+
|
849
|
+
// keypress
|
850
|
+
process.stdin.on('keypress', function(c, key){
|
851
|
+
if (key && 'enter' == key.name) {
|
852
|
+
console.log();
|
853
|
+
process.stdin.removeAllListeners('keypress');
|
854
|
+
tty.setRawMode(false);
|
855
|
+
if (!buf.trim().length) return self.password(str, mask, fn);
|
856
|
+
fn(buf);
|
857
|
+
return;
|
858
|
+
}
|
859
|
+
|
860
|
+
if (key && key.ctrl && 'c' == key.name) {
|
861
|
+
console.log('%s', buf);
|
862
|
+
process.exit();
|
863
|
+
}
|
864
|
+
|
865
|
+
process.stdout.write(mask);
|
866
|
+
buf += c;
|
867
|
+
}).resume();
|
868
|
+
};
|
869
|
+
|
870
|
+
/**
|
871
|
+
* Confirmation prompt with `str` and callback `fn(bool)`
|
872
|
+
*
|
873
|
+
* Examples:
|
874
|
+
*
|
875
|
+
* program.confirm('continue? ', function(ok){
|
876
|
+
* console.log(' got %j', ok);
|
877
|
+
* process.stdin.destroy();
|
878
|
+
* });
|
879
|
+
*
|
880
|
+
* @param {String} str
|
881
|
+
* @param {Function} fn
|
882
|
+
* @api public
|
883
|
+
*/
|
884
|
+
|
885
|
+
|
886
|
+
Command.prototype.confirm = function(str, fn){
|
887
|
+
var self = this;
|
888
|
+
this.prompt(str, function(ok){
|
889
|
+
if (!ok.trim()) {
|
890
|
+
return self.confirm(str, fn);
|
891
|
+
}
|
892
|
+
fn(parseBool(ok));
|
893
|
+
});
|
894
|
+
};
|
895
|
+
|
896
|
+
/**
|
897
|
+
* Choice prompt with `list` of items and callback `fn(index, item)`
|
898
|
+
*
|
899
|
+
* Examples:
|
900
|
+
*
|
901
|
+
* var list = ['tobi', 'loki', 'jane', 'manny', 'luna'];
|
902
|
+
*
|
903
|
+
* console.log('Choose the coolest pet:');
|
904
|
+
* program.choose(list, function(i){
|
905
|
+
* console.log('you chose %d "%s"', i, list[i]);
|
906
|
+
* process.stdin.destroy();
|
907
|
+
* });
|
908
|
+
*
|
909
|
+
* @param {Array} list
|
910
|
+
* @param {Function} fn
|
911
|
+
* @api public
|
912
|
+
*/
|
913
|
+
|
914
|
+
Command.prototype.choose = function(list, fn){
|
915
|
+
var self = this;
|
916
|
+
|
917
|
+
list.forEach(function(item, i){
|
918
|
+
console.log(' %d) %s', i + 1, item);
|
919
|
+
});
|
920
|
+
|
921
|
+
function again() {
|
922
|
+
self.prompt(' : ', function(val){
|
923
|
+
val = parseInt(val, 10) - 1;
|
924
|
+
if (null == list[val]) {
|
925
|
+
again();
|
926
|
+
} else {
|
927
|
+
fn(val, list[val]);
|
928
|
+
}
|
929
|
+
});
|
930
|
+
}
|
931
|
+
|
932
|
+
again();
|
933
|
+
};
|
934
|
+
|
935
|
+
/**
|
936
|
+
* Camel-case the given `flag`
|
937
|
+
*
|
938
|
+
* @param {String} flag
|
939
|
+
* @return {String}
|
940
|
+
* @api private
|
941
|
+
*/
|
942
|
+
|
943
|
+
function camelcase(flag) {
|
944
|
+
return flag.split('-').reduce(function(str, word){
|
945
|
+
return str + word[0].toUpperCase() + word.slice(1);
|
946
|
+
});
|
947
|
+
}
|
948
|
+
|
949
|
+
/**
|
950
|
+
* Parse a boolean `str`.
|
951
|
+
*
|
952
|
+
* @param {String} str
|
953
|
+
* @return {Boolean}
|
954
|
+
* @api private
|
955
|
+
*/
|
956
|
+
|
957
|
+
function parseBool(str) {
|
958
|
+
return /^y|yes|ok|true$/i.test(str);
|
959
|
+
}
|
960
|
+
|
961
|
+
/**
|
962
|
+
* Pad `str` to `width`.
|
963
|
+
*
|
964
|
+
* @param {String} str
|
965
|
+
* @param {Number} width
|
966
|
+
* @return {String}
|
967
|
+
* @api private
|
968
|
+
*/
|
969
|
+
|
970
|
+
function pad(str, width) {
|
971
|
+
var len = Math.max(0, width - str.length);
|
972
|
+
return str + Array(len + 1).join(' ');
|
973
|
+
}
|
974
|
+
|
975
|
+
/**
|
976
|
+
* Output help information if necessary
|
977
|
+
*
|
978
|
+
* @param {Command} command to output help for
|
979
|
+
* @param {Array} array of options to search for -h or --help
|
980
|
+
* @api private
|
981
|
+
*/
|
982
|
+
|
983
|
+
function outputHelpIfNecessary(cmd, options) {
|
984
|
+
options = options || [];
|
985
|
+
for (var i = 0; i < options.length; i++) {
|
986
|
+
if (options[i] == '--help' || options[i] == '-h') {
|
987
|
+
process.stdout.write(cmd.helpInformation());
|
988
|
+
cmd.emit('--help');
|
989
|
+
process.exit(0);
|
990
|
+
}
|
991
|
+
}
|
992
|
+
}
|