opal 1.6.1 → 1.7.0
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.
- 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
|
+
|