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.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +17 -0
  3. data/CHANGELOG.md +35 -1
  4. data/Gemfile +1 -0
  5. data/HACKING.md +47 -26
  6. data/benchmark/benchmarks +415 -103
  7. data/benchmark/bm_call_overhead.yml +28 -0
  8. data/benchmark/run.rb +61 -40
  9. data/docs/cdp_common.json +3364 -0
  10. data/docs/cdp_common.md +18 -0
  11. data/docs/{headless_chrome.md → headless_browsers.md} +31 -12
  12. data/lib/opal/ast/builder.rb +1 -1
  13. data/lib/opal/builder.rb +6 -1
  14. data/lib/opal/builder_processors.rb +5 -3
  15. data/lib/opal/cache.rb +1 -7
  16. data/lib/opal/cli_options.rb +72 -58
  17. data/lib/opal/cli_runners/chrome.rb +47 -9
  18. data/lib/opal/cli_runners/chrome_cdp_interface.rb +238 -112
  19. data/lib/opal/cli_runners/compiler.rb +146 -13
  20. data/lib/opal/cli_runners/deno.rb +32 -0
  21. data/lib/opal/cli_runners/firefox.rb +350 -0
  22. data/lib/opal/cli_runners/firefox_cdp_interface.rb +212 -0
  23. data/lib/opal/cli_runners/node_modules/.bin/chrome-remote-interface.cmd +17 -0
  24. data/lib/opal/cli_runners/node_modules/.bin/chrome-remote-interface.ps1 +28 -0
  25. data/lib/opal/cli_runners/node_modules/.package-lock.json +41 -0
  26. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/LICENSE +1 -1
  27. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/README.md +322 -182
  28. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/bin/client.js +99 -114
  29. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/chrome-remote-interface.js +1 -11
  30. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/index.js +16 -11
  31. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/api.js +41 -33
  32. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/chrome.js +224 -214
  33. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/devtools.js +71 -191
  34. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/external-request.js +26 -6
  35. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/protocol.json +20788 -9049
  36. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/websocket-wrapper.js +10 -3
  37. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/package.json +59 -123
  38. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/webpack.config.js +25 -32
  39. data/lib/opal/cli_runners/node_modules/commander/History.md +298 -0
  40. data/lib/opal/cli_runners/node_modules/commander/LICENSE +22 -0
  41. data/lib/opal/cli_runners/node_modules/commander/Readme.md +217 -61
  42. data/lib/opal/cli_runners/node_modules/commander/index.js +431 -145
  43. data/lib/opal/cli_runners/node_modules/commander/package.json +16 -79
  44. data/lib/opal/cli_runners/node_modules/ws/README.md +334 -98
  45. data/lib/opal/cli_runners/node_modules/ws/browser.js +8 -0
  46. data/lib/opal/cli_runners/node_modules/ws/index.js +5 -10
  47. data/lib/opal/cli_runners/node_modules/ws/lib/buffer-util.js +129 -0
  48. data/lib/opal/cli_runners/node_modules/ws/lib/constants.js +10 -0
  49. data/lib/opal/cli_runners/node_modules/ws/lib/event-target.js +184 -0
  50. data/lib/opal/cli_runners/node_modules/ws/lib/extension.js +223 -0
  51. data/lib/opal/cli_runners/node_modules/ws/lib/limiter.js +55 -0
  52. data/lib/opal/cli_runners/node_modules/ws/lib/permessage-deflate.js +518 -0
  53. data/lib/opal/cli_runners/node_modules/ws/lib/receiver.js +607 -0
  54. data/lib/opal/cli_runners/node_modules/ws/lib/sender.js +409 -0
  55. data/lib/opal/cli_runners/node_modules/ws/lib/stream.js +180 -0
  56. data/lib/opal/cli_runners/node_modules/ws/lib/validation.js +104 -0
  57. data/lib/opal/cli_runners/node_modules/ws/lib/websocket-server.js +447 -0
  58. data/lib/opal/cli_runners/node_modules/ws/lib/websocket.js +1195 -0
  59. data/lib/opal/cli_runners/node_modules/ws/package.json +40 -106
  60. data/lib/opal/cli_runners/package-lock.json +62 -0
  61. data/lib/opal/cli_runners/package.json +1 -1
  62. data/lib/opal/cli_runners.rb +26 -4
  63. data/lib/opal/nodes/args/prepare_post_args.rb +2 -2
  64. data/lib/opal/nodes/def.rb +8 -8
  65. data/lib/opal/nodes/iter.rb +12 -12
  66. data/lib/opal/nodes/logic.rb +1 -1
  67. data/lib/opal/nodes/masgn.rb +2 -2
  68. data/lib/opal/parser/with_ruby_lexer.rb +1 -1
  69. data/lib/opal/paths.rb +14 -0
  70. data/lib/opal/rewriter.rb +2 -0
  71. data/lib/opal/rewriters/forward_args.rb +52 -4
  72. data/lib/opal/rewriters/targeted_patches.rb +94 -0
  73. data/lib/opal/version.rb +1 -1
  74. data/opal/corelib/basic_object.rb +1 -1
  75. data/opal/corelib/boolean.rb +2 -2
  76. data/opal/corelib/class.rb +11 -0
  77. data/opal/corelib/constants.rb +3 -3
  78. data/opal/corelib/enumerable.rb +4 -0
  79. data/opal/corelib/enumerator.rb +1 -1
  80. data/opal/corelib/hash.rb +2 -2
  81. data/opal/corelib/helpers.rb +1 -1
  82. data/opal/corelib/kernel.rb +3 -3
  83. data/opal/corelib/method.rb +1 -1
  84. data/opal/corelib/module.rb +29 -8
  85. data/opal/corelib/proc.rb +7 -5
  86. data/opal/corelib/runtime.js +141 -78
  87. data/opal/corelib/set.rb +252 -0
  88. data/opal/corelib/string.rb +2 -1
  89. data/opal/corelib/time.rb +2 -2
  90. data/opal/opal.rb +1 -0
  91. data/opal.gemspec +1 -0
  92. data/spec/filters/bugs/array.rb +22 -13
  93. data/spec/filters/bugs/base64.rb +5 -5
  94. data/spec/filters/bugs/basicobject.rb +16 -8
  95. data/spec/filters/bugs/bigdecimal.rb +161 -160
  96. data/spec/filters/bugs/binding.rb +10 -10
  97. data/spec/filters/bugs/class.rb +8 -8
  98. data/spec/filters/bugs/complex.rb +2 -1
  99. data/spec/filters/bugs/date.rb +79 -81
  100. data/spec/filters/bugs/datetime.rb +29 -29
  101. data/spec/filters/bugs/delegate.rb +1 -3
  102. data/spec/filters/bugs/encoding.rb +69 -69
  103. data/spec/filters/bugs/enumerable.rb +22 -20
  104. data/spec/filters/bugs/enumerator.rb +88 -85
  105. data/spec/filters/bugs/exception.rb +46 -40
  106. data/spec/filters/bugs/file.rb +32 -32
  107. data/spec/filters/bugs/float.rb +26 -21
  108. data/spec/filters/bugs/freeze.rb +88 -0
  109. data/spec/filters/bugs/hash.rb +39 -38
  110. data/spec/filters/bugs/integer.rb +57 -44
  111. data/spec/filters/bugs/io.rb +1 -1
  112. data/spec/filters/bugs/kernel.rb +349 -269
  113. data/spec/filters/bugs/language.rb +220 -188
  114. data/spec/filters/bugs/main.rb +5 -3
  115. data/spec/filters/bugs/marshal.rb +38 -38
  116. data/spec/filters/bugs/math.rb +2 -1
  117. data/spec/filters/bugs/method.rb +73 -62
  118. data/spec/filters/bugs/module.rb +163 -143
  119. data/spec/filters/bugs/numeric.rb +6 -6
  120. data/spec/filters/bugs/objectspace.rb +16 -16
  121. data/spec/filters/bugs/openstruct.rb +1 -1
  122. data/spec/filters/bugs/pack_unpack.rb +51 -51
  123. data/spec/filters/bugs/pathname.rb +7 -7
  124. data/spec/filters/bugs/proc.rb +63 -63
  125. data/spec/filters/bugs/random.rb +7 -6
  126. data/spec/filters/bugs/range.rb +12 -9
  127. data/spec/filters/bugs/rational.rb +8 -7
  128. data/spec/filters/bugs/regexp.rb +49 -48
  129. data/spec/filters/bugs/ruby-32.rb +56 -0
  130. data/spec/filters/bugs/set.rb +30 -30
  131. data/spec/filters/bugs/singleton.rb +4 -4
  132. data/spec/filters/bugs/string.rb +187 -99
  133. data/spec/filters/bugs/stringio.rb +7 -0
  134. data/spec/filters/bugs/stringscanner.rb +68 -68
  135. data/spec/filters/bugs/struct.rb +11 -9
  136. data/spec/filters/bugs/symbol.rb +1 -1
  137. data/spec/filters/bugs/time.rb +78 -63
  138. data/spec/filters/bugs/trace_point.rb +4 -4
  139. data/spec/filters/bugs/unboundmethod.rb +32 -17
  140. data/spec/filters/bugs/warnings.rb +8 -12
  141. data/spec/filters/unsupported/array.rb +24 -107
  142. data/spec/filters/unsupported/basicobject.rb +12 -12
  143. data/spec/filters/unsupported/bignum.rb +27 -52
  144. data/spec/filters/unsupported/class.rb +1 -2
  145. data/spec/filters/unsupported/delegator.rb +3 -3
  146. data/spec/filters/unsupported/enumerable.rb +2 -9
  147. data/spec/filters/unsupported/enumerator.rb +2 -11
  148. data/spec/filters/unsupported/file.rb +1 -1
  149. data/spec/filters/unsupported/float.rb +28 -47
  150. data/spec/filters/unsupported/hash.rb +8 -14
  151. data/spec/filters/unsupported/integer.rb +75 -91
  152. data/spec/filters/unsupported/kernel.rb +17 -35
  153. data/spec/filters/unsupported/language.rb +11 -19
  154. data/spec/filters/unsupported/marshal.rb +22 -41
  155. data/spec/filters/unsupported/matchdata.rb +28 -52
  156. data/spec/filters/unsupported/math.rb +1 -1
  157. data/spec/filters/unsupported/privacy.rb +229 -285
  158. data/spec/filters/unsupported/range.rb +1 -5
  159. data/spec/filters/unsupported/regexp.rb +40 -66
  160. data/spec/filters/unsupported/set.rb +2 -2
  161. data/spec/filters/unsupported/singleton.rb +4 -4
  162. data/spec/filters/unsupported/string.rb +305 -508
  163. data/spec/filters/unsupported/struct.rb +3 -4
  164. data/spec/filters/unsupported/symbol.rb +15 -18
  165. data/spec/filters/unsupported/thread.rb +1 -7
  166. data/spec/filters/unsupported/time.rb +159 -202
  167. data/spec/filters/unsupported/usage_of_files.rb +170 -259
  168. data/spec/lib/builder_spec.rb +4 -4
  169. data/spec/lib/rewriters/forward_args_spec.rb +32 -12
  170. data/spec/mspec-opal/runner.rb +2 -0
  171. data/spec/ruby_specs +4 -0
  172. data/stdlib/deno/base.rb +28 -0
  173. data/stdlib/deno/file.rb +340 -0
  174. data/stdlib/{headless_chrome.rb → headless_browser/base.rb} +1 -1
  175. data/stdlib/headless_browser/file.rb +15 -0
  176. data/stdlib/headless_browser.rb +4 -0
  177. data/stdlib/native.rb +1 -1
  178. data/stdlib/nodejs/file.rb +5 -0
  179. data/stdlib/opal/platform.rb +8 -6
  180. data/stdlib/opal-platform.rb +14 -8
  181. data/stdlib/set.rb +1 -258
  182. data/tasks/benchmarking.rake +62 -19
  183. data/tasks/performance.rake +1 -1
  184. data/tasks/testing.rake +5 -3
  185. data/test/nodejs/test_file.rb +29 -10
  186. data/test/opal/http_server.rb +28 -11
  187. data/test/opal/unsupported_and_bugs.rb +2 -1
  188. metadata +89 -50
  189. data/lib/opal/cli_runners/node_modules/ultron/LICENSE +0 -22
  190. data/lib/opal/cli_runners/node_modules/ultron/index.js +0 -138
  191. data/lib/opal/cli_runners/node_modules/ultron/package.json +0 -112
  192. data/lib/opal/cli_runners/node_modules/ws/SECURITY.md +0 -33
  193. data/lib/opal/cli_runners/node_modules/ws/lib/BufferUtil.fallback.js +0 -56
  194. data/lib/opal/cli_runners/node_modules/ws/lib/BufferUtil.js +0 -15
  195. data/lib/opal/cli_runners/node_modules/ws/lib/ErrorCodes.js +0 -28
  196. data/lib/opal/cli_runners/node_modules/ws/lib/EventTarget.js +0 -158
  197. data/lib/opal/cli_runners/node_modules/ws/lib/Extensions.js +0 -69
  198. data/lib/opal/cli_runners/node_modules/ws/lib/PerMessageDeflate.js +0 -339
  199. data/lib/opal/cli_runners/node_modules/ws/lib/Receiver.js +0 -520
  200. data/lib/opal/cli_runners/node_modules/ws/lib/Sender.js +0 -438
  201. data/lib/opal/cli_runners/node_modules/ws/lib/Validation.fallback.js +0 -9
  202. data/lib/opal/cli_runners/node_modules/ws/lib/Validation.js +0 -17
  203. data/lib/opal/cli_runners/node_modules/ws/lib/WebSocket.js +0 -705
  204. data/lib/opal/cli_runners/node_modules/ws/lib/WebSocketServer.js +0 -336
  205. data/spec/filters/bugs/boolean.rb +0 -3
  206. data/spec/filters/bugs/matrix.rb +0 -3
  207. data/spec/filters/unsupported/fixnum.rb +0 -15
  208. data/spec/filters/unsupported/freeze.rb +0 -102
  209. data/spec/filters/unsupported/pathname.rb +0 -4
  210. data/spec/filters/unsupported/proc.rb +0 -4
  211. data/spec/filters/unsupported/random.rb +0 -5
  212. 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
- if (desc) cmd.description(desc);
151
- if (desc) this.executables = true;
152
- if (desc) this._execs[cmd._name] = true;
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
- self._args.push({ required: true, name: arg.slice(1, -1) });
221
+ argDetails.required = true;
222
+ argDetails.name = arg.slice(1, -1);
188
223
  break;
189
224
  case '[':
190
- self._args.push({ required: false, name: arg.slice(1, -1) });
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
- this.parent.on(this._name, function(args, unknown){
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(this, args);
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 false
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
- * // => true
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|Mixed} fn or default
298
- * @param {Mixed} defaultValue
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 ('function' != typeof fn) defaultValue = fn, fn = null;
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 != val && fn) val = fn(val);
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
- if (this._execs[name]) return this.executeSubCommand(argv, args, parsed.unknown);
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 dir = dirname(argv[1]);
402
- var bin = basename(argv[1]) + '-' + args[0];
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
- // check for ./<bin> first
405
- var local = path.join(dir, bin);
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
- var proc = spawn(local, args, { stdio: 'inherit', customFds: [0, 1, 2] });
410
- proc.on('error', function(err){
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 && (lastOpt = this.optionFor(args[i-1]));
440
-
441
- if (lastOpt && lastOpt.required) {
442
- ret.push(arg);
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 cmds = this.commands
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('*', args);
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 ('--' == arg) {
534
- literal = true;
691
+ if (literal) {
692
+ args.push(arg);
535
693
  continue;
536
694
  }
537
695
 
538
- if (literal) {
539
- args.push(arg);
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
- console.log(str);
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 == arguments.length) return this._description;
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.required
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 ? '] [command' : '')
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
- // Prepend the help information
725
- return [pad('-h, --help', width) + ' ' + 'output usage information']
726
- .concat(this.options.map(function(option){
727
- return pad(option.flags, width)
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
- , this.commands.map(function(cmd){
747
- var args = cmd._args.map(function(arg){
748
- return arg.required
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
- return [
774
- ''
775
- , ' Usage: ' + this._name + ' ' + this.usage()
776
- , '' + this.commandHelp()
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
- ].join('\n');
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
- process.stdout.write(this.helpInformation());
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
+