opal 1.6.1 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/build.yml +17 -0
- data/CHANGELOG.md +35 -1
- data/Gemfile +1 -0
- data/HACKING.md +47 -26
- data/benchmark/benchmarks +415 -103
- data/benchmark/bm_call_overhead.yml +28 -0
- data/benchmark/run.rb +61 -40
- data/docs/cdp_common.json +3364 -0
- data/docs/cdp_common.md +18 -0
- data/docs/{headless_chrome.md → headless_browsers.md} +31 -12
- data/lib/opal/ast/builder.rb +1 -1
- data/lib/opal/builder.rb +6 -1
- data/lib/opal/builder_processors.rb +5 -3
- data/lib/opal/cache.rb +1 -7
- data/lib/opal/cli_options.rb +72 -58
- data/lib/opal/cli_runners/chrome.rb +47 -9
- data/lib/opal/cli_runners/chrome_cdp_interface.rb +238 -112
- data/lib/opal/cli_runners/compiler.rb +146 -13
- data/lib/opal/cli_runners/deno.rb +32 -0
- data/lib/opal/cli_runners/firefox.rb +350 -0
- data/lib/opal/cli_runners/firefox_cdp_interface.rb +212 -0
- data/lib/opal/cli_runners/node_modules/.bin/chrome-remote-interface.cmd +17 -0
- data/lib/opal/cli_runners/node_modules/.bin/chrome-remote-interface.ps1 +28 -0
- data/lib/opal/cli_runners/node_modules/.package-lock.json +41 -0
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/LICENSE +1 -1
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/README.md +322 -182
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/bin/client.js +99 -114
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/chrome-remote-interface.js +1 -11
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/index.js +16 -11
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/api.js +41 -33
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/chrome.js +224 -214
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/devtools.js +71 -191
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/external-request.js +26 -6
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/protocol.json +20788 -9049
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/websocket-wrapper.js +10 -3
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/package.json +59 -123
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/webpack.config.js +25 -32
- data/lib/opal/cli_runners/node_modules/commander/History.md +298 -0
- data/lib/opal/cli_runners/node_modules/commander/LICENSE +22 -0
- data/lib/opal/cli_runners/node_modules/commander/Readme.md +217 -61
- data/lib/opal/cli_runners/node_modules/commander/index.js +431 -145
- data/lib/opal/cli_runners/node_modules/commander/package.json +16 -79
- data/lib/opal/cli_runners/node_modules/ws/README.md +334 -98
- data/lib/opal/cli_runners/node_modules/ws/browser.js +8 -0
- data/lib/opal/cli_runners/node_modules/ws/index.js +5 -10
- data/lib/opal/cli_runners/node_modules/ws/lib/buffer-util.js +129 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/constants.js +10 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/event-target.js +184 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/extension.js +223 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/limiter.js +55 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/permessage-deflate.js +518 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/receiver.js +607 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/sender.js +409 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/stream.js +180 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/validation.js +104 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/websocket-server.js +447 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/websocket.js +1195 -0
- data/lib/opal/cli_runners/node_modules/ws/package.json +40 -106
- data/lib/opal/cli_runners/package-lock.json +62 -0
- data/lib/opal/cli_runners/package.json +1 -1
- data/lib/opal/cli_runners.rb +26 -4
- data/lib/opal/nodes/args/prepare_post_args.rb +2 -2
- data/lib/opal/nodes/def.rb +8 -8
- data/lib/opal/nodes/iter.rb +12 -12
- data/lib/opal/nodes/logic.rb +1 -1
- data/lib/opal/nodes/masgn.rb +2 -2
- data/lib/opal/parser/with_ruby_lexer.rb +1 -1
- data/lib/opal/paths.rb +14 -0
- data/lib/opal/rewriter.rb +2 -0
- data/lib/opal/rewriters/forward_args.rb +52 -4
- data/lib/opal/rewriters/targeted_patches.rb +94 -0
- data/lib/opal/version.rb +1 -1
- data/opal/corelib/basic_object.rb +1 -1
- data/opal/corelib/boolean.rb +2 -2
- data/opal/corelib/class.rb +11 -0
- data/opal/corelib/constants.rb +3 -3
- data/opal/corelib/enumerable.rb +4 -0
- data/opal/corelib/enumerator.rb +1 -1
- data/opal/corelib/hash.rb +2 -2
- data/opal/corelib/helpers.rb +1 -1
- data/opal/corelib/kernel.rb +3 -3
- data/opal/corelib/method.rb +1 -1
- data/opal/corelib/module.rb +29 -8
- data/opal/corelib/proc.rb +7 -5
- data/opal/corelib/runtime.js +141 -78
- data/opal/corelib/set.rb +252 -0
- data/opal/corelib/string.rb +2 -1
- data/opal/corelib/time.rb +2 -2
- data/opal/opal.rb +1 -0
- data/opal.gemspec +1 -0
- data/spec/filters/bugs/array.rb +22 -13
- data/spec/filters/bugs/base64.rb +5 -5
- data/spec/filters/bugs/basicobject.rb +16 -8
- data/spec/filters/bugs/bigdecimal.rb +161 -160
- data/spec/filters/bugs/binding.rb +10 -10
- data/spec/filters/bugs/class.rb +8 -8
- data/spec/filters/bugs/complex.rb +2 -1
- data/spec/filters/bugs/date.rb +79 -81
- data/spec/filters/bugs/datetime.rb +29 -29
- data/spec/filters/bugs/delegate.rb +1 -3
- data/spec/filters/bugs/encoding.rb +69 -69
- data/spec/filters/bugs/enumerable.rb +22 -20
- data/spec/filters/bugs/enumerator.rb +88 -85
- data/spec/filters/bugs/exception.rb +46 -40
- data/spec/filters/bugs/file.rb +32 -32
- data/spec/filters/bugs/float.rb +26 -21
- data/spec/filters/bugs/freeze.rb +88 -0
- data/spec/filters/bugs/hash.rb +39 -38
- data/spec/filters/bugs/integer.rb +57 -44
- data/spec/filters/bugs/io.rb +1 -1
- data/spec/filters/bugs/kernel.rb +349 -269
- data/spec/filters/bugs/language.rb +220 -188
- data/spec/filters/bugs/main.rb +5 -3
- data/spec/filters/bugs/marshal.rb +38 -38
- data/spec/filters/bugs/math.rb +2 -1
- data/spec/filters/bugs/method.rb +73 -62
- data/spec/filters/bugs/module.rb +163 -143
- data/spec/filters/bugs/numeric.rb +6 -6
- data/spec/filters/bugs/objectspace.rb +16 -16
- data/spec/filters/bugs/openstruct.rb +1 -1
- data/spec/filters/bugs/pack_unpack.rb +51 -51
- data/spec/filters/bugs/pathname.rb +7 -7
- data/spec/filters/bugs/proc.rb +63 -63
- data/spec/filters/bugs/random.rb +7 -6
- data/spec/filters/bugs/range.rb +12 -9
- data/spec/filters/bugs/rational.rb +8 -7
- data/spec/filters/bugs/regexp.rb +49 -48
- data/spec/filters/bugs/ruby-32.rb +56 -0
- data/spec/filters/bugs/set.rb +30 -30
- data/spec/filters/bugs/singleton.rb +4 -4
- data/spec/filters/bugs/string.rb +187 -99
- data/spec/filters/bugs/stringio.rb +7 -0
- data/spec/filters/bugs/stringscanner.rb +68 -68
- data/spec/filters/bugs/struct.rb +11 -9
- data/spec/filters/bugs/symbol.rb +1 -1
- data/spec/filters/bugs/time.rb +78 -63
- data/spec/filters/bugs/trace_point.rb +4 -4
- data/spec/filters/bugs/unboundmethod.rb +32 -17
- data/spec/filters/bugs/warnings.rb +8 -12
- data/spec/filters/unsupported/array.rb +24 -107
- data/spec/filters/unsupported/basicobject.rb +12 -12
- data/spec/filters/unsupported/bignum.rb +27 -52
- data/spec/filters/unsupported/class.rb +1 -2
- data/spec/filters/unsupported/delegator.rb +3 -3
- data/spec/filters/unsupported/enumerable.rb +2 -9
- data/spec/filters/unsupported/enumerator.rb +2 -11
- data/spec/filters/unsupported/file.rb +1 -1
- data/spec/filters/unsupported/float.rb +28 -47
- data/spec/filters/unsupported/hash.rb +8 -14
- data/spec/filters/unsupported/integer.rb +75 -91
- data/spec/filters/unsupported/kernel.rb +17 -35
- data/spec/filters/unsupported/language.rb +11 -19
- data/spec/filters/unsupported/marshal.rb +22 -41
- data/spec/filters/unsupported/matchdata.rb +28 -52
- data/spec/filters/unsupported/math.rb +1 -1
- data/spec/filters/unsupported/privacy.rb +229 -285
- data/spec/filters/unsupported/range.rb +1 -5
- data/spec/filters/unsupported/regexp.rb +40 -66
- data/spec/filters/unsupported/set.rb +2 -2
- data/spec/filters/unsupported/singleton.rb +4 -4
- data/spec/filters/unsupported/string.rb +305 -508
- data/spec/filters/unsupported/struct.rb +3 -4
- data/spec/filters/unsupported/symbol.rb +15 -18
- data/spec/filters/unsupported/thread.rb +1 -7
- data/spec/filters/unsupported/time.rb +159 -202
- data/spec/filters/unsupported/usage_of_files.rb +170 -259
- data/spec/lib/builder_spec.rb +4 -4
- data/spec/lib/rewriters/forward_args_spec.rb +32 -12
- data/spec/mspec-opal/runner.rb +2 -0
- data/spec/ruby_specs +4 -0
- data/stdlib/deno/base.rb +28 -0
- data/stdlib/deno/file.rb +340 -0
- data/stdlib/{headless_chrome.rb → headless_browser/base.rb} +1 -1
- data/stdlib/headless_browser/file.rb +15 -0
- data/stdlib/headless_browser.rb +4 -0
- data/stdlib/native.rb +1 -1
- data/stdlib/nodejs/file.rb +5 -0
- data/stdlib/opal/platform.rb +8 -6
- data/stdlib/opal-platform.rb +14 -8
- data/stdlib/set.rb +1 -258
- data/tasks/benchmarking.rake +62 -19
- data/tasks/performance.rake +1 -1
- data/tasks/testing.rake +5 -3
- data/test/nodejs/test_file.rb +29 -10
- data/test/opal/http_server.rb +28 -11
- data/test/opal/unsupported_and_bugs.rb +2 -1
- metadata +89 -50
- data/lib/opal/cli_runners/node_modules/ultron/LICENSE +0 -22
- data/lib/opal/cli_runners/node_modules/ultron/index.js +0 -138
- data/lib/opal/cli_runners/node_modules/ultron/package.json +0 -112
- data/lib/opal/cli_runners/node_modules/ws/SECURITY.md +0 -33
- data/lib/opal/cli_runners/node_modules/ws/lib/BufferUtil.fallback.js +0 -56
- data/lib/opal/cli_runners/node_modules/ws/lib/BufferUtil.js +0 -15
- data/lib/opal/cli_runners/node_modules/ws/lib/ErrorCodes.js +0 -28
- data/lib/opal/cli_runners/node_modules/ws/lib/EventTarget.js +0 -158
- data/lib/opal/cli_runners/node_modules/ws/lib/Extensions.js +0 -69
- data/lib/opal/cli_runners/node_modules/ws/lib/PerMessageDeflate.js +0 -339
- data/lib/opal/cli_runners/node_modules/ws/lib/Receiver.js +0 -520
- data/lib/opal/cli_runners/node_modules/ws/lib/Sender.js +0 -438
- data/lib/opal/cli_runners/node_modules/ws/lib/Validation.fallback.js +0 -9
- data/lib/opal/cli_runners/node_modules/ws/lib/Validation.js +0 -17
- data/lib/opal/cli_runners/node_modules/ws/lib/WebSocket.js +0 -705
- data/lib/opal/cli_runners/node_modules/ws/lib/WebSocketServer.js +0 -336
- data/spec/filters/bugs/boolean.rb +0 -3
- data/spec/filters/bugs/matrix.rb +0 -3
- data/spec/filters/unsupported/fixnum.rb +0 -15
- data/spec/filters/unsupported/freeze.rb +0 -102
- data/spec/filters/unsupported/pathname.rb +0 -4
- data/spec/filters/unsupported/proc.rb +0 -4
- data/spec/filters/unsupported/random.rb +0 -5
- data/spec/filters/unsupported/taint.rb +0 -162
@@ -1,21 +1,19 @@
|
|
1
|
-
|
2
1
|
/**
|
3
2
|
* Module dependencies.
|
4
3
|
*/
|
5
4
|
|
6
5
|
var EventEmitter = require('events').EventEmitter;
|
7
6
|
var spawn = require('child_process').spawn;
|
8
|
-
var fs = require('fs');
|
9
|
-
var exists = fs.existsSync;
|
10
7
|
var path = require('path');
|
11
8
|
var dirname = path.dirname;
|
12
9
|
var basename = path.basename;
|
10
|
+
var fs = require('fs');
|
13
11
|
|
14
12
|
/**
|
15
13
|
* Expose the root command.
|
16
14
|
*/
|
17
15
|
|
18
|
-
exports = module.exports = new Command;
|
16
|
+
exports = module.exports = new Command();
|
19
17
|
|
20
18
|
/**
|
21
19
|
* Expose `Command`.
|
@@ -55,7 +53,7 @@ function Option(flags, description) {
|
|
55
53
|
* @api private
|
56
54
|
*/
|
57
55
|
|
58
|
-
Option.prototype.name = function(){
|
56
|
+
Option.prototype.name = function() {
|
59
57
|
return this.long
|
60
58
|
.replace('--', '')
|
61
59
|
.replace('no-', '');
|
@@ -69,9 +67,8 @@ Option.prototype.name = function(){
|
|
69
67
|
* @api private
|
70
68
|
*/
|
71
69
|
|
72
|
-
Option.prototype.is = function(arg){
|
73
|
-
return arg == this.short
|
74
|
-
|| arg == this.long;
|
70
|
+
Option.prototype.is = function(arg) {
|
71
|
+
return arg == this.short || arg == this.long;
|
75
72
|
};
|
76
73
|
|
77
74
|
/**
|
@@ -84,9 +81,10 @@ Option.prototype.is = function(arg){
|
|
84
81
|
function Command(name) {
|
85
82
|
this.commands = [];
|
86
83
|
this.options = [];
|
87
|
-
this._execs =
|
84
|
+
this._execs = {};
|
85
|
+
this._allowUnknownOption = false;
|
88
86
|
this._args = [];
|
89
|
-
this._name = name;
|
87
|
+
this._name = name || '';
|
90
88
|
}
|
91
89
|
|
92
90
|
/**
|
@@ -114,49 +112,79 @@ Command.prototype.__proto__ = EventEmitter.prototype;
|
|
114
112
|
* .option('-C, --chdir <path>', 'change the working directory')
|
115
113
|
* .option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')
|
116
114
|
* .option('-T, --no-tests', 'ignore test hook')
|
117
|
-
*
|
115
|
+
*
|
118
116
|
* program
|
119
117
|
* .command('setup')
|
120
118
|
* .description('run remote setup commands')
|
121
|
-
* .action(function(){
|
119
|
+
* .action(function() {
|
122
120
|
* console.log('setup');
|
123
121
|
* });
|
124
|
-
*
|
122
|
+
*
|
125
123
|
* program
|
126
124
|
* .command('exec <cmd>')
|
127
125
|
* .description('run the given remote command')
|
128
|
-
* .action(function(cmd){
|
126
|
+
* .action(function(cmd) {
|
129
127
|
* console.log('exec "%s"', cmd);
|
130
128
|
* });
|
131
|
-
*
|
129
|
+
*
|
130
|
+
* program
|
131
|
+
* .command('teardown <dir> [otherDirs...]')
|
132
|
+
* .description('run teardown commands')
|
133
|
+
* .action(function(dir, otherDirs) {
|
134
|
+
* console.log('dir "%s"', dir);
|
135
|
+
* if (otherDirs) {
|
136
|
+
* otherDirs.forEach(function (oDir) {
|
137
|
+
* console.log('dir "%s"', oDir);
|
138
|
+
* });
|
139
|
+
* }
|
140
|
+
* });
|
141
|
+
*
|
132
142
|
* program
|
133
143
|
* .command('*')
|
134
144
|
* .description('deploy the given env')
|
135
|
-
* .action(function(env){
|
145
|
+
* .action(function(env) {
|
136
146
|
* console.log('deploying "%s"', env);
|
137
147
|
* });
|
138
|
-
*
|
148
|
+
*
|
139
149
|
* program.parse(process.argv);
|
140
150
|
*
|
141
151
|
* @param {String} name
|
142
|
-
* @param {String} [desc]
|
152
|
+
* @param {String} [desc] for git-style sub-commands
|
143
153
|
* @return {Command} the new command
|
144
154
|
* @api public
|
145
155
|
*/
|
146
156
|
|
147
|
-
Command.prototype.command = function(name, desc){
|
157
|
+
Command.prototype.command = function(name, desc, opts) {
|
158
|
+
opts = opts || {};
|
148
159
|
var args = name.split(/ +/);
|
149
160
|
var cmd = new Command(args.shift());
|
150
|
-
|
151
|
-
if (desc)
|
152
|
-
|
161
|
+
|
162
|
+
if (desc) {
|
163
|
+
cmd.description(desc);
|
164
|
+
this.executables = true;
|
165
|
+
this._execs[cmd._name] = true;
|
166
|
+
if (opts.isDefault) this.defaultExecutable = cmd._name;
|
167
|
+
}
|
168
|
+
|
169
|
+
cmd._noHelp = !!opts.noHelp;
|
153
170
|
this.commands.push(cmd);
|
154
171
|
cmd.parseExpectedArgs(args);
|
155
172
|
cmd.parent = this;
|
173
|
+
|
156
174
|
if (desc) return this;
|
157
175
|
return cmd;
|
158
176
|
};
|
159
177
|
|
178
|
+
/**
|
179
|
+
* Define argument syntax for the top-level command.
|
180
|
+
*
|
181
|
+
* @api public
|
182
|
+
*/
|
183
|
+
|
184
|
+
Command.prototype.arguments = function (desc) {
|
185
|
+
return this.parseExpectedArgs(desc.split(/ +/));
|
186
|
+
};
|
187
|
+
|
160
188
|
/**
|
161
189
|
* Add an implicit `help [cmd]` subcommand
|
162
190
|
* which invokes `--help` for the given command.
|
@@ -178,18 +206,33 @@ Command.prototype.addImplicitHelpCommand = function() {
|
|
178
206
|
* @api public
|
179
207
|
*/
|
180
208
|
|
181
|
-
Command.prototype.parseExpectedArgs = function(args){
|
209
|
+
Command.prototype.parseExpectedArgs = function(args) {
|
182
210
|
if (!args.length) return;
|
183
211
|
var self = this;
|
184
|
-
args.forEach(function(arg){
|
212
|
+
args.forEach(function(arg) {
|
213
|
+
var argDetails = {
|
214
|
+
required: false,
|
215
|
+
name: '',
|
216
|
+
variadic: false
|
217
|
+
};
|
218
|
+
|
185
219
|
switch (arg[0]) {
|
186
220
|
case '<':
|
187
|
-
|
221
|
+
argDetails.required = true;
|
222
|
+
argDetails.name = arg.slice(1, -1);
|
188
223
|
break;
|
189
224
|
case '[':
|
190
|
-
|
225
|
+
argDetails.name = arg.slice(1, -1);
|
191
226
|
break;
|
192
227
|
}
|
228
|
+
|
229
|
+
if (argDetails.name.length > 3 && argDetails.name.slice(-3) === '...') {
|
230
|
+
argDetails.variadic = true;
|
231
|
+
argDetails.name = argDetails.name.slice(0, -3);
|
232
|
+
}
|
233
|
+
if (argDetails.name) {
|
234
|
+
self._args.push(argDetails);
|
235
|
+
}
|
193
236
|
});
|
194
237
|
return this;
|
195
238
|
};
|
@@ -202,7 +245,7 @@ Command.prototype.parseExpectedArgs = function(args){
|
|
202
245
|
* program
|
203
246
|
* .command('help')
|
204
247
|
* .description('display verbose help')
|
205
|
-
* .action(function(){
|
248
|
+
* .action(function() {
|
206
249
|
* // output help here
|
207
250
|
* });
|
208
251
|
*
|
@@ -211,32 +254,40 @@ Command.prototype.parseExpectedArgs = function(args){
|
|
211
254
|
* @api public
|
212
255
|
*/
|
213
256
|
|
214
|
-
Command.prototype.action = function(fn){
|
257
|
+
Command.prototype.action = function(fn) {
|
215
258
|
var self = this;
|
216
|
-
|
259
|
+
var listener = function(args, unknown) {
|
217
260
|
// Parse any so-far unknown options
|
261
|
+
args = args || [];
|
218
262
|
unknown = unknown || [];
|
263
|
+
|
219
264
|
var parsed = self.parseOptions(unknown);
|
220
|
-
|
265
|
+
|
221
266
|
// Output help if necessary
|
222
267
|
outputHelpIfNecessary(self, parsed.unknown);
|
223
|
-
|
224
|
-
// If there are still any unknown options, then we simply
|
268
|
+
|
269
|
+
// If there are still any unknown options, then we simply
|
225
270
|
// die, unless someone asked for help, in which case we give it
|
226
271
|
// to them, and then we die.
|
227
|
-
if (parsed.unknown.length > 0) {
|
272
|
+
if (parsed.unknown.length > 0) {
|
228
273
|
self.unknownOption(parsed.unknown[0]);
|
229
274
|
}
|
230
|
-
|
275
|
+
|
231
276
|
// Leftover arguments need to be pushed back. Fixes issue #56
|
232
277
|
if (parsed.args.length) args = parsed.args.concat(args);
|
233
|
-
|
234
|
-
self._args.forEach(function(arg, i){
|
278
|
+
|
279
|
+
self._args.forEach(function(arg, i) {
|
235
280
|
if (arg.required && null == args[i]) {
|
236
281
|
self.missingArgument(arg.name);
|
282
|
+
} else if (arg.variadic) {
|
283
|
+
if (i !== self._args.length - 1) {
|
284
|
+
self.variadicArgNotLast(arg.name);
|
285
|
+
}
|
286
|
+
|
287
|
+
args[i] = args.splice(i);
|
237
288
|
}
|
238
289
|
});
|
239
|
-
|
290
|
+
|
240
291
|
// Always append ourselves to the end of the arguments,
|
241
292
|
// to make sure we match the number of arguments the user
|
242
293
|
// expects
|
@@ -245,15 +296,19 @@ Command.prototype.action = function(fn){
|
|
245
296
|
} else {
|
246
297
|
args.push(self);
|
247
298
|
}
|
248
|
-
|
249
|
-
fn.apply(
|
250
|
-
}
|
299
|
+
|
300
|
+
fn.apply(self, args);
|
301
|
+
};
|
302
|
+
var parent = this.parent || this;
|
303
|
+
var name = parent === this ? '*' : this._name;
|
304
|
+
parent.on('command:' + name, listener);
|
305
|
+
if (this._alias) parent.on('command:' + this._alias, listener);
|
251
306
|
return this;
|
252
307
|
};
|
253
308
|
|
254
309
|
/**
|
255
310
|
* Define option with `flags`, `description` and optional
|
256
|
-
* coercion `fn`.
|
311
|
+
* coercion `fn`.
|
257
312
|
*
|
258
313
|
* The `flags` string should contain both the short and long flags,
|
259
314
|
* separated by comma, a pipe or space. The following are all valid
|
@@ -272,7 +327,7 @@ Command.prototype.action = function(fn){
|
|
272
327
|
* program.pepper
|
273
328
|
* // => Boolean
|
274
329
|
*
|
275
|
-
* // simple boolean defaulting to
|
330
|
+
* // simple boolean defaulting to true
|
276
331
|
* program.option('-C, --no-cheese', 'remove cheese');
|
277
332
|
*
|
278
333
|
* program.cheese
|
@@ -280,7 +335,7 @@ Command.prototype.action = function(fn){
|
|
280
335
|
*
|
281
336
|
* --no-cheese
|
282
337
|
* program.cheese
|
283
|
-
* // =>
|
338
|
+
* // => false
|
284
339
|
*
|
285
340
|
* // required argument
|
286
341
|
* program.option('-C, --chdir <path>', 'change the working directory');
|
@@ -294,20 +349,32 @@ Command.prototype.action = function(fn){
|
|
294
349
|
*
|
295
350
|
* @param {String} flags
|
296
351
|
* @param {String} description
|
297
|
-
* @param {Function
|
298
|
-
* @param {
|
352
|
+
* @param {Function|*} [fn] or default
|
353
|
+
* @param {*} [defaultValue]
|
299
354
|
* @return {Command} for chaining
|
300
355
|
* @api public
|
301
356
|
*/
|
302
357
|
|
303
|
-
Command.prototype.option = function(flags, description, fn, defaultValue){
|
358
|
+
Command.prototype.option = function(flags, description, fn, defaultValue) {
|
304
359
|
var self = this
|
305
360
|
, option = new Option(flags, description)
|
306
361
|
, oname = option.name()
|
307
362
|
, name = camelcase(oname);
|
308
363
|
|
309
364
|
// default as 3rd arg
|
310
|
-
if (
|
365
|
+
if (typeof fn != 'function') {
|
366
|
+
if (fn instanceof RegExp) {
|
367
|
+
var regex = fn;
|
368
|
+
fn = function(val, def) {
|
369
|
+
var m = regex.exec(val);
|
370
|
+
return m ? m[0] : def;
|
371
|
+
}
|
372
|
+
}
|
373
|
+
else {
|
374
|
+
defaultValue = fn;
|
375
|
+
fn = null;
|
376
|
+
}
|
377
|
+
}
|
311
378
|
|
312
379
|
// preassign default value only for --no-*, [optional], or <required>
|
313
380
|
if (false == option.bool || option.optional || option.required) {
|
@@ -322,9 +389,11 @@ Command.prototype.option = function(flags, description, fn, defaultValue){
|
|
322
389
|
|
323
390
|
// when it's passed assign the value
|
324
391
|
// and conditionally invoke the callback
|
325
|
-
this.on(oname, function(val){
|
392
|
+
this.on('option:' + oname, function(val) {
|
326
393
|
// coercion
|
327
|
-
if (null
|
394
|
+
if (null !== val && fn) val = fn(val, undefined === self[name]
|
395
|
+
? defaultValue
|
396
|
+
: self[name]);
|
328
397
|
|
329
398
|
// unassigned or bool
|
330
399
|
if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) {
|
@@ -345,6 +414,18 @@ Command.prototype.option = function(flags, description, fn, defaultValue){
|
|
345
414
|
return this;
|
346
415
|
};
|
347
416
|
|
417
|
+
/**
|
418
|
+
* Allow unknown options on the command line.
|
419
|
+
*
|
420
|
+
* @param {Boolean} arg if `true` or omitted, no error will be thrown
|
421
|
+
* for unknown options.
|
422
|
+
* @api public
|
423
|
+
*/
|
424
|
+
Command.prototype.allowUnknownOption = function(arg) {
|
425
|
+
this._allowUnknownOption = arguments.length === 0 || arg;
|
426
|
+
return this;
|
427
|
+
};
|
428
|
+
|
348
429
|
/**
|
349
430
|
* Parse `argv`, settings options and invoking commands when defined.
|
350
431
|
*
|
@@ -353,7 +434,7 @@ Command.prototype.option = function(flags, description, fn, defaultValue){
|
|
353
434
|
* @api public
|
354
435
|
*/
|
355
436
|
|
356
|
-
Command.prototype.parse = function(argv){
|
437
|
+
Command.prototype.parse = function(argv) {
|
357
438
|
// implicit help
|
358
439
|
if (this.executables) this.addImplicitHelpCommand();
|
359
440
|
|
@@ -361,17 +442,42 @@ Command.prototype.parse = function(argv){
|
|
361
442
|
this.rawArgs = argv;
|
362
443
|
|
363
444
|
// guess name
|
364
|
-
this._name = this._name || basename(argv[1]);
|
445
|
+
this._name = this._name || basename(argv[1], '.js');
|
446
|
+
|
447
|
+
// github-style sub-commands with no sub-command
|
448
|
+
if (this.executables && argv.length < 3 && !this.defaultExecutable) {
|
449
|
+
// this user needs help
|
450
|
+
argv.push('--help');
|
451
|
+
}
|
365
452
|
|
366
453
|
// process argv
|
367
454
|
var parsed = this.parseOptions(this.normalize(argv.slice(2)));
|
368
455
|
var args = this.args = parsed.args;
|
369
|
-
|
456
|
+
|
370
457
|
var result = this.parseArgs(this.args, parsed.unknown);
|
371
458
|
|
372
459
|
// executable sub-commands
|
373
460
|
var name = result.args[0];
|
374
|
-
|
461
|
+
|
462
|
+
var aliasCommand = null;
|
463
|
+
// check alias of sub commands
|
464
|
+
if (name) {
|
465
|
+
aliasCommand = this.commands.filter(function(command) {
|
466
|
+
return command.alias() === name;
|
467
|
+
})[0];
|
468
|
+
}
|
469
|
+
|
470
|
+
if (this._execs[name] && typeof this._execs[name] != "function") {
|
471
|
+
return this.executeSubCommand(argv, args, parsed.unknown);
|
472
|
+
} else if (aliasCommand) {
|
473
|
+
// is alias of a subCommand
|
474
|
+
args[0] = aliasCommand._name;
|
475
|
+
return this.executeSubCommand(argv, args, parsed.unknown);
|
476
|
+
} else if (this.defaultExecutable) {
|
477
|
+
// use the default subcommand
|
478
|
+
args.unshift(this.defaultExecutable);
|
479
|
+
return this.executeSubCommand(argv, args, parsed.unknown);
|
480
|
+
}
|
375
481
|
|
376
482
|
return result;
|
377
483
|
};
|
@@ -398,23 +504,71 @@ Command.prototype.executeSubCommand = function(argv, args, unknown) {
|
|
398
504
|
}
|
399
505
|
|
400
506
|
// executable
|
401
|
-
var
|
402
|
-
|
507
|
+
var f = argv[1];
|
508
|
+
// name of the subcommand, link `pm-install`
|
509
|
+
var bin = basename(f, '.js') + '-' + args[0];
|
510
|
+
|
403
511
|
|
404
|
-
//
|
405
|
-
|
512
|
+
// In case of globally installed, get the base dir where executable
|
513
|
+
// subcommand file should be located at
|
514
|
+
var baseDir
|
515
|
+
, link = fs.lstatSync(f).isSymbolicLink() ? fs.readlinkSync(f) : f;
|
516
|
+
|
517
|
+
// when symbolink is relative path
|
518
|
+
if (link !== f && link.charAt(0) !== '/') {
|
519
|
+
link = path.join(dirname(f), link)
|
520
|
+
}
|
521
|
+
baseDir = dirname(link);
|
522
|
+
|
523
|
+
// prefer local `./<bin>` to bin in the $PATH
|
524
|
+
var localBin = path.join(baseDir, bin);
|
525
|
+
|
526
|
+
// whether bin file is a js script with explicit `.js` extension
|
527
|
+
var isExplicitJS = false;
|
528
|
+
if (exists(localBin + '.js')) {
|
529
|
+
bin = localBin + '.js';
|
530
|
+
isExplicitJS = true;
|
531
|
+
} else if (exists(localBin)) {
|
532
|
+
bin = localBin;
|
533
|
+
}
|
406
534
|
|
407
|
-
// run it
|
408
535
|
args = args.slice(1);
|
409
|
-
|
410
|
-
proc
|
536
|
+
|
537
|
+
var proc;
|
538
|
+
if (process.platform !== 'win32') {
|
539
|
+
if (isExplicitJS) {
|
540
|
+
args.unshift(bin);
|
541
|
+
// add executable arguments to spawn
|
542
|
+
args = (process.execArgv || []).concat(args);
|
543
|
+
|
544
|
+
proc = spawn('node', args, { stdio: 'inherit', customFds: [0, 1, 2] });
|
545
|
+
} else {
|
546
|
+
proc = spawn(bin, args, { stdio: 'inherit', customFds: [0, 1, 2] });
|
547
|
+
}
|
548
|
+
} else {
|
549
|
+
args.unshift(bin);
|
550
|
+
proc = spawn(process.execPath, args, { stdio: 'inherit'});
|
551
|
+
}
|
552
|
+
|
553
|
+
var signals = ['SIGUSR1', 'SIGUSR2', 'SIGTERM', 'SIGINT', 'SIGHUP'];
|
554
|
+
signals.forEach(function(signal) {
|
555
|
+
process.on(signal, function(){
|
556
|
+
if ((proc.killed === false) && (proc.exitCode === null)){
|
557
|
+
proc.kill(signal);
|
558
|
+
}
|
559
|
+
});
|
560
|
+
});
|
561
|
+
proc.on('close', process.exit.bind(process));
|
562
|
+
proc.on('error', function(err) {
|
411
563
|
if (err.code == "ENOENT") {
|
412
564
|
console.error('\n %s(1) does not exist, try --help\n', bin);
|
413
565
|
} else if (err.code == "EACCES") {
|
414
566
|
console.error('\n %s(1) not executable. try chmod or run with root\n', bin);
|
415
567
|
}
|
568
|
+
process.exit(1);
|
416
569
|
});
|
417
570
|
|
571
|
+
// Store the reference to the child process
|
418
572
|
this.runningCommand = proc;
|
419
573
|
};
|
420
574
|
|
@@ -428,7 +582,7 @@ Command.prototype.executeSubCommand = function(argv, args, unknown) {
|
|
428
582
|
* @api private
|
429
583
|
*/
|
430
584
|
|
431
|
-
Command.prototype.normalize = function(args){
|
585
|
+
Command.prototype.normalize = function(args) {
|
432
586
|
var ret = []
|
433
587
|
, arg
|
434
588
|
, lastOpt
|
@@ -436,12 +590,18 @@ Command.prototype.normalize = function(args){
|
|
436
590
|
|
437
591
|
for (var i = 0, len = args.length; i < len; ++i) {
|
438
592
|
arg = args[i];
|
439
|
-
i > 0
|
440
|
-
|
441
|
-
|
442
|
-
|
593
|
+
if (i > 0) {
|
594
|
+
lastOpt = this.optionFor(args[i-1]);
|
595
|
+
}
|
596
|
+
|
597
|
+
if (arg === '--') {
|
598
|
+
// Honor option terminator
|
599
|
+
ret = ret.concat(args.slice(i));
|
600
|
+
break;
|
601
|
+
} else if (lastOpt && lastOpt.required) {
|
602
|
+
ret.push(arg);
|
443
603
|
} else if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) {
|
444
|
-
arg.slice(1).split('').forEach(function(c){
|
604
|
+
arg.slice(1).split('').forEach(function(c) {
|
445
605
|
ret.push('-' + c);
|
446
606
|
});
|
447
607
|
} else if (/^--/.test(arg) && ~(index = arg.indexOf('='))) {
|
@@ -466,24 +626,22 @@ Command.prototype.normalize = function(args){
|
|
466
626
|
* @api private
|
467
627
|
*/
|
468
628
|
|
469
|
-
Command.prototype.parseArgs = function(args, unknown){
|
470
|
-
var
|
471
|
-
, len = cmds.length
|
472
|
-
, name;
|
629
|
+
Command.prototype.parseArgs = function(args, unknown) {
|
630
|
+
var name;
|
473
631
|
|
474
632
|
if (args.length) {
|
475
633
|
name = args[0];
|
476
|
-
if (this.listeners(name).length) {
|
477
|
-
this.emit(args.shift(), args, unknown);
|
634
|
+
if (this.listeners('command:' + name).length) {
|
635
|
+
this.emit('command:' + args.shift(), args, unknown);
|
478
636
|
} else {
|
479
|
-
this.emit('
|
637
|
+
this.emit('command:*', args);
|
480
638
|
}
|
481
639
|
} else {
|
482
640
|
outputHelpIfNecessary(this, unknown);
|
483
|
-
|
641
|
+
|
484
642
|
// If there were no args and we have unknown options,
|
485
643
|
// then they are extraneous and we need to error.
|
486
|
-
if (unknown.length > 0) {
|
644
|
+
if (unknown.length > 0) {
|
487
645
|
this.unknownOption(unknown[0]);
|
488
646
|
}
|
489
647
|
}
|
@@ -499,7 +657,7 @@ Command.prototype.parseArgs = function(args, unknown){
|
|
499
657
|
* @api private
|
500
658
|
*/
|
501
659
|
|
502
|
-
Command.prototype.optionFor = function(arg){
|
660
|
+
Command.prototype.optionFor = function(arg) {
|
503
661
|
for (var i = 0, len = this.options.length; i < len; ++i) {
|
504
662
|
if (this.options[i].is(arg)) {
|
505
663
|
return this.options[i];
|
@@ -516,7 +674,7 @@ Command.prototype.optionFor = function(arg){
|
|
516
674
|
* @api public
|
517
675
|
*/
|
518
676
|
|
519
|
-
Command.prototype.parseOptions = function(argv){
|
677
|
+
Command.prototype.parseOptions = function(argv) {
|
520
678
|
var args = []
|
521
679
|
, len = argv.length
|
522
680
|
, literal
|
@@ -530,13 +688,13 @@ Command.prototype.parseOptions = function(argv){
|
|
530
688
|
arg = argv[i];
|
531
689
|
|
532
690
|
// literal args after --
|
533
|
-
if (
|
534
|
-
|
691
|
+
if (literal) {
|
692
|
+
args.push(arg);
|
535
693
|
continue;
|
536
694
|
}
|
537
695
|
|
538
|
-
if (
|
539
|
-
|
696
|
+
if ('--' == arg) {
|
697
|
+
literal = true;
|
540
698
|
continue;
|
541
699
|
}
|
542
700
|
|
@@ -549,7 +707,7 @@ Command.prototype.parseOptions = function(argv){
|
|
549
707
|
if (option.required) {
|
550
708
|
arg = argv[++i];
|
551
709
|
if (null == arg) return this.optionMissingArgument(option);
|
552
|
-
this.emit(option.name(), arg);
|
710
|
+
this.emit('option:' + option.name(), arg);
|
553
711
|
// optional arg
|
554
712
|
} else if (option.optional) {
|
555
713
|
arg = argv[i+1];
|
@@ -558,18 +716,18 @@ Command.prototype.parseOptions = function(argv){
|
|
558
716
|
} else {
|
559
717
|
++i;
|
560
718
|
}
|
561
|
-
this.emit(option.name(), arg);
|
719
|
+
this.emit('option:' + option.name(), arg);
|
562
720
|
// bool
|
563
721
|
} else {
|
564
|
-
this.emit(option.name());
|
722
|
+
this.emit('option:' + option.name());
|
565
723
|
}
|
566
724
|
continue;
|
567
725
|
}
|
568
|
-
|
726
|
+
|
569
727
|
// looks like an option
|
570
728
|
if (arg.length > 1 && '-' == arg[0]) {
|
571
729
|
unknownOptions.push(arg);
|
572
|
-
|
730
|
+
|
573
731
|
// If the next argument looks like it might be
|
574
732
|
// an argument for this option, we pass it on.
|
575
733
|
// If it isn't, then it'll simply be ignored
|
@@ -578,14 +736,31 @@ Command.prototype.parseOptions = function(argv){
|
|
578
736
|
}
|
579
737
|
continue;
|
580
738
|
}
|
581
|
-
|
739
|
+
|
582
740
|
// arg
|
583
741
|
args.push(arg);
|
584
742
|
}
|
585
|
-
|
743
|
+
|
586
744
|
return { args: args, unknown: unknownOptions };
|
587
745
|
};
|
588
746
|
|
747
|
+
/**
|
748
|
+
* Return an object containing options as key-value pairs
|
749
|
+
*
|
750
|
+
* @return {Object}
|
751
|
+
* @api public
|
752
|
+
*/
|
753
|
+
Command.prototype.opts = function() {
|
754
|
+
var result = {}
|
755
|
+
, len = this.options.length;
|
756
|
+
|
757
|
+
for (var i = 0 ; i < len; i++) {
|
758
|
+
var key = camelcase(this.options[i].name());
|
759
|
+
result[key] = key === 'version' ? this._version : this[key];
|
760
|
+
}
|
761
|
+
return result;
|
762
|
+
};
|
763
|
+
|
589
764
|
/**
|
590
765
|
* Argument `name` is missing.
|
591
766
|
*
|
@@ -593,7 +768,7 @@ Command.prototype.parseOptions = function(argv){
|
|
593
768
|
* @api private
|
594
769
|
*/
|
595
770
|
|
596
|
-
Command.prototype.missingArgument = function(name){
|
771
|
+
Command.prototype.missingArgument = function(name) {
|
597
772
|
console.error();
|
598
773
|
console.error(" error: missing required argument `%s'", name);
|
599
774
|
console.error();
|
@@ -608,7 +783,7 @@ Command.prototype.missingArgument = function(name){
|
|
608
783
|
* @api private
|
609
784
|
*/
|
610
785
|
|
611
|
-
Command.prototype.optionMissingArgument = function(option, flag){
|
786
|
+
Command.prototype.optionMissingArgument = function(option, flag) {
|
612
787
|
console.error();
|
613
788
|
if (flag) {
|
614
789
|
console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag);
|
@@ -626,13 +801,27 @@ Command.prototype.optionMissingArgument = function(option, flag){
|
|
626
801
|
* @api private
|
627
802
|
*/
|
628
803
|
|
629
|
-
Command.prototype.unknownOption = function(flag){
|
804
|
+
Command.prototype.unknownOption = function(flag) {
|
805
|
+
if (this._allowUnknownOption) return;
|
630
806
|
console.error();
|
631
807
|
console.error(" error: unknown option `%s'", flag);
|
632
808
|
console.error();
|
633
809
|
process.exit(1);
|
634
810
|
};
|
635
811
|
|
812
|
+
/**
|
813
|
+
* Variadic argument with `name` is not the last argument as required.
|
814
|
+
*
|
815
|
+
* @param {String} name
|
816
|
+
* @api private
|
817
|
+
*/
|
818
|
+
|
819
|
+
Command.prototype.variadicArgNotLast = function(name) {
|
820
|
+
console.error();
|
821
|
+
console.error(" error: variadic arguments must be last `%s'", name);
|
822
|
+
console.error();
|
823
|
+
process.exit(1);
|
824
|
+
};
|
636
825
|
|
637
826
|
/**
|
638
827
|
* Set the program version to `str`.
|
@@ -641,37 +830,57 @@ Command.prototype.unknownOption = function(flag){
|
|
641
830
|
* which will print the version number when passed.
|
642
831
|
*
|
643
832
|
* @param {String} str
|
644
|
-
* @param {String} flags
|
833
|
+
* @param {String} [flags]
|
645
834
|
* @return {Command} for chaining
|
646
835
|
* @api public
|
647
836
|
*/
|
648
837
|
|
649
|
-
Command.prototype.version = function(str, flags){
|
838
|
+
Command.prototype.version = function(str, flags) {
|
650
839
|
if (0 == arguments.length) return this._version;
|
651
840
|
this._version = str;
|
652
841
|
flags = flags || '-V, --version';
|
653
842
|
this.option(flags, 'output the version number');
|
654
|
-
this.on('version', function(){
|
655
|
-
|
843
|
+
this.on('option:version', function() {
|
844
|
+
process.stdout.write(str + '\n');
|
656
845
|
process.exit(0);
|
657
846
|
});
|
658
847
|
return this;
|
659
848
|
};
|
660
849
|
|
661
850
|
/**
|
662
|
-
* Set the description `str`.
|
851
|
+
* Set the description to `str`.
|
663
852
|
*
|
664
853
|
* @param {String} str
|
665
854
|
* @return {String|Command}
|
666
855
|
* @api public
|
667
856
|
*/
|
668
857
|
|
669
|
-
Command.prototype.description = function(str){
|
670
|
-
if (0
|
858
|
+
Command.prototype.description = function(str) {
|
859
|
+
if (0 === arguments.length) return this._description;
|
671
860
|
this._description = str;
|
672
861
|
return this;
|
673
862
|
};
|
674
863
|
|
864
|
+
/**
|
865
|
+
* Set an alias for the command
|
866
|
+
*
|
867
|
+
* @param {String} alias
|
868
|
+
* @return {String|Command}
|
869
|
+
* @api public
|
870
|
+
*/
|
871
|
+
|
872
|
+
Command.prototype.alias = function(alias) {
|
873
|
+
var command = this;
|
874
|
+
if(this.commands.length !== 0) {
|
875
|
+
command = this.commands[this.commands.length - 1]
|
876
|
+
}
|
877
|
+
|
878
|
+
if (arguments.length === 0) return command._alias;
|
879
|
+
|
880
|
+
command._alias = alias;
|
881
|
+
return this;
|
882
|
+
};
|
883
|
+
|
675
884
|
/**
|
676
885
|
* Set / get the command usage `str`.
|
677
886
|
*
|
@@ -680,17 +889,14 @@ Command.prototype.description = function(str){
|
|
680
889
|
* @api public
|
681
890
|
*/
|
682
891
|
|
683
|
-
Command.prototype.usage = function(str){
|
684
|
-
var args = this._args.map(function(arg){
|
685
|
-
return arg
|
686
|
-
? '<' + arg.name + '>'
|
687
|
-
: '[' + arg.name + ']';
|
892
|
+
Command.prototype.usage = function(str) {
|
893
|
+
var args = this._args.map(function(arg) {
|
894
|
+
return humanReadableArgName(arg);
|
688
895
|
});
|
689
896
|
|
690
|
-
var usage = '[options'
|
691
|
-
+ (this.commands.length ? '
|
692
|
-
+ '
|
693
|
-
+ (this._args.length ? ' ' + args : '');
|
897
|
+
var usage = '[options]'
|
898
|
+
+ (this.commands.length ? ' [command]' : '')
|
899
|
+
+ (this._args.length ? ' ' + args.join(' ') : '');
|
694
900
|
|
695
901
|
if (0 == arguments.length) return this._usage || usage;
|
696
902
|
this._usage = str;
|
@@ -698,6 +904,20 @@ Command.prototype.usage = function(str){
|
|
698
904
|
return this;
|
699
905
|
};
|
700
906
|
|
907
|
+
/**
|
908
|
+
* Get or set the name of the command
|
909
|
+
*
|
910
|
+
* @param {String} str
|
911
|
+
* @return {String|Command}
|
912
|
+
* @api public
|
913
|
+
*/
|
914
|
+
|
915
|
+
Command.prototype.name = function(str) {
|
916
|
+
if (0 === arguments.length) return this._name;
|
917
|
+
this._name = str;
|
918
|
+
return this;
|
919
|
+
};
|
920
|
+
|
701
921
|
/**
|
702
922
|
* Return the largest option length.
|
703
923
|
*
|
@@ -705,8 +925,8 @@ Command.prototype.usage = function(str){
|
|
705
925
|
* @api private
|
706
926
|
*/
|
707
927
|
|
708
|
-
Command.prototype.largestOptionLength = function(){
|
709
|
-
return this.options.reduce(function(max, option){
|
928
|
+
Command.prototype.largestOptionLength = function() {
|
929
|
+
return this.options.reduce(function(max, option) {
|
710
930
|
return Math.max(max, option.flags.length);
|
711
931
|
}, 0);
|
712
932
|
};
|
@@ -718,15 +938,13 @@ Command.prototype.largestOptionLength = function(){
|
|
718
938
|
* @api private
|
719
939
|
*/
|
720
940
|
|
721
|
-
Command.prototype.optionHelp = function(){
|
941
|
+
Command.prototype.optionHelp = function() {
|
722
942
|
var width = this.largestOptionLength();
|
723
|
-
|
724
|
-
//
|
725
|
-
return
|
726
|
-
|
727
|
-
|
728
|
-
+ ' ' + option.description;
|
729
|
-
}))
|
943
|
+
|
944
|
+
// Append the help information
|
945
|
+
return this.options.map(function(option) {
|
946
|
+
return pad(option.flags, width) + ' ' + option.description;
|
947
|
+
}).concat([pad('-h, --help', width) + ' ' + 'output usage information'])
|
730
948
|
.join('\n');
|
731
949
|
};
|
732
950
|
|
@@ -737,26 +955,36 @@ Command.prototype.optionHelp = function(){
|
|
737
955
|
* @api private
|
738
956
|
*/
|
739
957
|
|
740
|
-
Command.prototype.commandHelp = function(){
|
958
|
+
Command.prototype.commandHelp = function() {
|
741
959
|
if (!this.commands.length) return '';
|
960
|
+
|
961
|
+
var commands = this.commands.filter(function(cmd) {
|
962
|
+
return !cmd._noHelp;
|
963
|
+
}).map(function(cmd) {
|
964
|
+
var args = cmd._args.map(function(arg) {
|
965
|
+
return humanReadableArgName(arg);
|
966
|
+
}).join(' ');
|
967
|
+
|
968
|
+
return [
|
969
|
+
cmd._name
|
970
|
+
+ (cmd._alias ? '|' + cmd._alias : '')
|
971
|
+
+ (cmd.options.length ? ' [options]' : '')
|
972
|
+
+ ' ' + args
|
973
|
+
, cmd._description
|
974
|
+
];
|
975
|
+
});
|
976
|
+
|
977
|
+
var width = commands.reduce(function(max, command) {
|
978
|
+
return Math.max(max, command[0].length);
|
979
|
+
}, 0);
|
980
|
+
|
742
981
|
return [
|
743
|
-
|
982
|
+
''
|
744
983
|
, ' Commands:'
|
745
984
|
, ''
|
746
|
-
,
|
747
|
-
var
|
748
|
-
|
749
|
-
? '<' + arg.name + '>'
|
750
|
-
: '[' + arg.name + ']';
|
751
|
-
}).join(' ');
|
752
|
-
|
753
|
-
return pad(cmd._name
|
754
|
-
+ (cmd.options.length
|
755
|
-
? ' [options]'
|
756
|
-
: '') + ' ' + args, 22)
|
757
|
-
+ (cmd.description()
|
758
|
-
? ' ' + cmd.description()
|
759
|
-
: '');
|
985
|
+
, commands.map(function(cmd) {
|
986
|
+
var desc = cmd[1] ? ' ' + cmd[1] : '';
|
987
|
+
return pad(cmd[0], width) + desc;
|
760
988
|
}).join('\n').replace(/^/gm, ' ')
|
761
989
|
, ''
|
762
990
|
].join('\n');
|
@@ -769,17 +997,42 @@ Command.prototype.commandHelp = function(){
|
|
769
997
|
* @api private
|
770
998
|
*/
|
771
999
|
|
772
|
-
Command.prototype.helpInformation = function(){
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
1000
|
+
Command.prototype.helpInformation = function() {
|
1001
|
+
var desc = [];
|
1002
|
+
if (this._description) {
|
1003
|
+
desc = [
|
1004
|
+
' ' + this._description
|
1005
|
+
, ''
|
1006
|
+
];
|
1007
|
+
}
|
1008
|
+
|
1009
|
+
var cmdName = this._name;
|
1010
|
+
if (this._alias) {
|
1011
|
+
cmdName = cmdName + '|' + this._alias;
|
1012
|
+
}
|
1013
|
+
var usage = [
|
1014
|
+
''
|
1015
|
+
,' Usage: ' + cmdName + ' ' + this.usage()
|
1016
|
+
, ''
|
1017
|
+
];
|
1018
|
+
|
1019
|
+
var cmds = [];
|
1020
|
+
var commandHelp = this.commandHelp();
|
1021
|
+
if (commandHelp) cmds = [commandHelp];
|
1022
|
+
|
1023
|
+
var options = [
|
1024
|
+
''
|
777
1025
|
, ' Options:'
|
778
1026
|
, ''
|
779
1027
|
, '' + this.optionHelp().replace(/^/gm, ' ')
|
780
1028
|
, ''
|
781
|
-
|
782
|
-
|
1029
|
+
];
|
1030
|
+
|
1031
|
+
return usage
|
1032
|
+
.concat(desc)
|
1033
|
+
.concat(options)
|
1034
|
+
.concat(cmds)
|
1035
|
+
.join('\n');
|
783
1036
|
};
|
784
1037
|
|
785
1038
|
/**
|
@@ -788,8 +1041,13 @@ Command.prototype.helpInformation = function(){
|
|
788
1041
|
* @api public
|
789
1042
|
*/
|
790
1043
|
|
791
|
-
Command.prototype.outputHelp = function(){
|
792
|
-
|
1044
|
+
Command.prototype.outputHelp = function(cb) {
|
1045
|
+
if (!cb) {
|
1046
|
+
cb = function(passthru) {
|
1047
|
+
return passthru;
|
1048
|
+
}
|
1049
|
+
}
|
1050
|
+
process.stdout.write(cb(this.helpInformation()));
|
793
1051
|
this.emit('--help');
|
794
1052
|
};
|
795
1053
|
|
@@ -799,8 +1057,8 @@ Command.prototype.outputHelp = function(){
|
|
799
1057
|
* @api public
|
800
1058
|
*/
|
801
1059
|
|
802
|
-
Command.prototype.help = function(){
|
803
|
-
this.outputHelp();
|
1060
|
+
Command.prototype.help = function(cb) {
|
1061
|
+
this.outputHelp(cb);
|
804
1062
|
process.exit();
|
805
1063
|
};
|
806
1064
|
|
@@ -813,7 +1071,7 @@ Command.prototype.help = function(){
|
|
813
1071
|
*/
|
814
1072
|
|
815
1073
|
function camelcase(flag) {
|
816
|
-
return flag.split('-').reduce(function(str, word){
|
1074
|
+
return flag.split('-').reduce(function(str, word) {
|
817
1075
|
return str + word[0].toUpperCase() + word.slice(1);
|
818
1076
|
});
|
819
1077
|
}
|
@@ -849,3 +1107,31 @@ function outputHelpIfNecessary(cmd, options) {
|
|
849
1107
|
}
|
850
1108
|
}
|
851
1109
|
}
|
1110
|
+
|
1111
|
+
/**
|
1112
|
+
* Takes an argument an returns its human readable equivalent for help usage.
|
1113
|
+
*
|
1114
|
+
* @param {Object} arg
|
1115
|
+
* @return {String}
|
1116
|
+
* @api private
|
1117
|
+
*/
|
1118
|
+
|
1119
|
+
function humanReadableArgName(arg) {
|
1120
|
+
var nameOutput = arg.name + (arg.variadic === true ? '...' : '');
|
1121
|
+
|
1122
|
+
return arg.required
|
1123
|
+
? '<' + nameOutput + '>'
|
1124
|
+
: '[' + nameOutput + ']'
|
1125
|
+
}
|
1126
|
+
|
1127
|
+
// for versions before node v0.8 when there weren't `fs.existsSync`
|
1128
|
+
function exists(file) {
|
1129
|
+
try {
|
1130
|
+
if (fs.statSync(file).isFile()) {
|
1131
|
+
return true;
|
1132
|
+
}
|
1133
|
+
} catch (e) {
|
1134
|
+
return false;
|
1135
|
+
}
|
1136
|
+
}
|
1137
|
+
|