Repository: yargs/yargs-parser
Pull request #130 (2018-08-18 15:10:18)

Add halt-at-non-option configuration option
Fixes https://github.com/yargs/yargs/issues/946

wmertens: @bcoe any chance of a quick merge? It's fully downwards-compatible…
bcoe: @wmertens @Qard this feature feels a little bit magical to me. It's not really stopping on an unknown argument, it's essentially stopping parsing as soon as a positional argument is hit rather than a flag argument right?

In the cases where I've needed to split command line arguments (usually because one bin is instrumenting another bin) I've used a helper like this:

https://github.com/bcoe/c8/blob/master/lib/parse-args.js#L60

I wonder if instead of adding this additional parsing option, we could find a way to formalize this helper in yargs? I _think_ this is the use-case we're all trying to solve.

I'm swamped with open-source projects these days, so don't get to all my GitHub issues, perhaps I could convince you both to join me in slack here:

http://devtoolscommunity.herokuapp.com/

☝️ we've been using this slack to discuss JavaScript tooling related issues.

Patch:
diff --git a/README.md b/README.md
index 1cccd3a..ca92658 100644
--- a/README.md
+++ b/README.md
@@ -321,6 +321,27 @@ node example.js -a 1 -c 2
 { _: [], a: 1, b: undefined, c: 2 }
 ```
 
+### halt at non-option
+
+* default: `false`.
+* key: `halt-at-non-option`.
+
+Should parsing stop at the first text argument? This is similar to how e.g. `ssh` parses its command line.
+
+_If disabled:_
+
+```sh
+node example.js -a run b -x y
+{ _: [ 'run', 'b', 'y' ], a: true, x: true }
+```
+
+_If enabled:_
+
+```sh
+node example.js -a run b -x y
+{ _: [ 'run', 'b', '-x', 'y' ], a: true }
+```
+
 ## Special Thanks
 
 The yargs project evolves from optimist and minimist. It owes its
diff --git a/index.js b/index.js
index d003297..917189a 100644
--- a/index.js
+++ b/index.js
@@ -22,7 +22,8 @@ function parse (args, opts) {
     'flatten-duplicate-arrays': true,
     'populate--': false,
     'combine-arrays': false,
-    'set-placeholder-key': false
+    'set-placeholder-key': false,
+    'halt-at-non-option': false
   }, opts.configuration)
   var defaults = opts.default || {}
   var configObjects = opts.configObjects || []
@@ -139,10 +140,6 @@ function parse (args, opts) {
   })
 
   var notFlags = []
-  if (args.indexOf('--') !== -1) {
-    notFlags = args.slice(args.indexOf('--') + 1)
-    args = args.slice(0, args.indexOf('--'))
-  }
 
   for (var i = 0; i < args.length; i++) {
     var arg = args[i]
@@ -299,6 +296,12 @@ function parse (args, opts) {
           }
         }
       }
+    } else if (arg === '--') {
+      notFlags = args.slice(i + 1)
+      break
+    } else if (configuration['halt-at-non-option']) {
+      notFlags = args.slice(i)
+      break
     } else {
       argv._.push(maybeCoerceNumber('_', arg))
     }
@@ -696,7 +699,7 @@ function parse (args, opts) {
         })
         // For "--optionName", also set argv['option-name']
         flags.aliases[key].concat(key).forEach(function (x) {
-          if (/[A-Z]/.test(x) && configuration['camel-case-expansion']) {
+          if (x.length > 1 && /[A-Z]/.test(x) && configuration['camel-case-expansion']) {
             var c = decamelize(x, '-')
             if (c !== key && flags.aliases[key].indexOf(c) === -1) {
               flags.aliases[key].push(c)


Test patch:
diff --git a/test/yargs-parser.js b/test/yargs-parser.js
index 7b52a0b..a1d17de 100644
--- a/test/yargs-parser.js
+++ b/test/yargs-parser.js
@@ -1218,6 +1218,21 @@ describe('yargs-parser', function () {
         result.should.have.property('someOption', 'asdf')
       })
 
+      it('should not apply camel-case logic to 1-character options', function () {
+        var result = parser(['-p', 'hello'], {
+          alias: {
+            p: 'parallel',
+            P: 'parallel-series'
+          }
+        })
+
+        result.should.not.have.property('P', 'hello')
+        result.should.not.have.property('parallel-series', 'hello')
+        result.should.not.have.property('parallelSeries', 'hello')
+        result.should.have.property('parallel', 'hello')
+        result.should.have.property('p', 'hello')
+      })
+
       it('should provide aliases of options with dashes as camelCase properties', function () {
         var result = parser([], {
           alias: {
@@ -2499,6 +2514,39 @@ describe('yargs-parser', function () {
         parsed.should.not.have.property('a.b')
       })
     })
+
+    describe('halt-at-non-option', function () {
+      it('gets the entire rest of line', function () {
+        var parse = parser(['--foo', './file.js', '--foo', '--bar'], {
+          configuration: { 'halt-at-non-option': true },
+          boolean: ['foo', 'bar']
+        })
+        parse.should.deep.equal({ foo: true, _: ['./file.js', '--foo', '--bar'] })
+      })
+
+      it('is not influenced by --', function () {
+        var parse = parser(
+          ['--foo', './file.js', '--foo', '--', 'barbar', '--bar'],
+          { configuration: { 'halt-at-non-option': true }, boolean: ['foo', 'bar'] }
+        )
+        parse.should.deep.equal({
+          foo: true,
+          _: ['./file.js', '--foo', '--', 'barbar', '--bar']
+        })
+      })
+
+      it('is not influenced by unknown options', function () {
+        var parse = parser(
+          ['-v', '--long', 'arg', './file.js', '--foo', '--', 'barbar'],
+          { configuration: { 'halt-at-non-option': true }, boolean: ['foo'] }
+        )
+        parse.should.deep.equal({
+          v: true,
+          long: 'arg',
+          _: ['./file.js', '--foo', '--', 'barbar']
+        })
+      })
+    })
   })
 
   // addresses: https://github.com/yargs/yargs-parser/issues/41