opal 1.6.1 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
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
+