Repository: yargs/yargs-parser
Pull request #140 (2018-10-08 21:24:18)

Ensure empty string is added into argv._
A string such as "hello '' world" would parse into ["hello", undefined, "world"].
This would add a catch for that, ensuring that an empty string is pushed
instead, and adds associated tests.

bcoe: @jacksdrobinson thank you for the contribution, left a comment.

Patch:
diff --git a/.gitignore b/.gitignore
index db9974f..a7502d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ node_modules
 .DS_Store
 package-lock.json
 ./test/fixtures/package.json
+coverage
diff --git a/.travis.yml b/.travis.yml
index 1797b13..bf5a96a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,10 @@
 language: node_js
 os:
   - linux
+  - windows
 node_js:
   - "6"
   - "8"
+  - "10"
   - "node"
 after_script: npm run coverage
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 741bec6..41b5291 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,21 @@
 
 All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
 
+<a name="11.1.0"></a>
+# [11.1.0](https://github.com/yargs/yargs-parser/compare/v11.0.0...v11.1.0) (2018-11-10)
+
+
+### Bug Fixes
+
+* handling of one char alias ([#139](https://github.com/yargs/yargs-parser/issues/139)) ([ee56e31](https://github.com/yargs/yargs-parser/commit/ee56e31))
+
+
+### Features
+
+* add halt-at-non-option configuration option ([#130](https://github.com/yargs/yargs-parser/issues/130)) ([a849fce](https://github.com/yargs/yargs-parser/commit/a849fce))
+
+
+
 <a name="11.0.0"></a>
 # [11.0.0](https://github.com/yargs/yargs-parser/compare/v10.1.0...v11.0.0) (2018-10-06)
 
diff --git a/README.md b/README.md
index 1cccd3a..5847dff 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,6 @@
 [![Build Status](https://travis-ci.org/yargs/yargs-parser.svg)](https://travis-ci.org/yargs/yargs-parser)
 [![Coverage Status](https://coveralls.io/repos/yargs/yargs-parser/badge.svg?branch=)](https://coveralls.io/r/yargs/yargs-parser?branch=master)
 [![NPM version](https://img.shields.io/npm/v/yargs-parser.svg)](https://www.npmjs.com/package/yargs-parser)
-[![Windows Tests](https://img.shields.io/appveyor/ci/bcoe/yargs-parser/master.svg?label=Windows%20Tests)](https://ci.appveyor.com/project/bcoe/yargs-parser)
 [![Standard Version](https://img.shields.io/badge/release-standard%20version-brightgreen.svg)](https://github.com/conventional-changelog/standard-version)
 
 
@@ -321,6 +320,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/appveyor.yml b/appveyor.yml
deleted file mode 100644
index 396e9fb..0000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-environment:
-  matrix:
-    - nodejs_version: '6'
-    - nodejs_version: '8'
-    - nodejs_version: '10'
-install:
-  - ps: Install-Product node $env:nodejs_version
-  - set CI=true
-  - set PATH=%APPDATA%\npm;%PATH%
-  - npm install
-matrix:
-  fast_finish: true
-build: off
-version: '{build}'
-shallow_clone: true
-clone_depth: 1
-test_script:
-  - node --version
-  - npm --version
-  - npm test
diff --git a/index.js b/index.js
index d003297..69fa72d 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]
@@ -183,12 +180,12 @@ function parse (args, opts) {
     )) {
       key = arg.match(/^--?(.+)/)[1]
 
-      // array format = '--foo a b c'
-      if (checkAllAliases(key, flags.arrays) && args.length > i + 1) {
-        i = eatArray(i, key, args)
       // nargs format = '--foo a b c'
-      } else if (checkAllAliases(key, flags.nargs)) {
+      if (checkAllAliases(key, flags.nargs)) {
         i = eatNargs(i, key, args)
+      // array format = '--foo a b c'
+      } else if (checkAllAliases(key, flags.arrays) && args.length > i + 1) {
+        i = eatArray(i, key, args)
       } else {
         next = args[i + 1]
 
@@ -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)
diff --git a/lib/tokenize-arg-string.js b/lib/tokenize-arg-string.js
index 6c8d23e..73e14cd 100644
--- a/lib/tokenize-arg-string.js
+++ b/lib/tokenize-arg-string.js
@@ -25,6 +25,7 @@ module.exports = function (argString) {
     // don't split the string if we're in matching
     // opening or closing single and double quotes.
     if (c === opening) {
+      if (!args[i]) args[i] = ''
       opening = null
       continue
     } else if ((c === "'" || c === '"') && !opening) {
diff --git a/package.json b/package.json
index fda8714..fe28e11 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "yargs-parser",
-  "version": "11.0.0",
+  "version": "11.1.0",
   "description": "the mighty option parser used by yargs",
   "main": "index.js",
   "scripts": {


Test patch:
diff --git a/test/tokenize-arg-string.js b/test/tokenize-arg-string.js
index 2f7cffc..8bb6a95 100644
--- a/test/tokenize-arg-string.js
+++ b/test/tokenize-arg-string.js
@@ -32,6 +32,20 @@ describe('TokenizeArgString', function () {
     args[2].should.equal('--bar=foo bar')
   })
 
+  it('handles single quoted empty string', function () {
+    var args = tokenizeArgString('--foo \'\' --bar=\'\'')
+    args[0].should.equal('--foo')
+    args[1].should.equal('')
+    args[2].should.equal('--bar=')
+  })
+
+  it('handles double quoted empty string', function () {
+    var args = tokenizeArgString('--foo "" --bar=""')
+    args[0].should.equal('--foo')
+    args[1].should.equal('')
+    args[2].should.equal('--bar=')
+  })
+
   it('handles quoted string with embeded quotes', function () {
     var args = tokenizeArgString('--foo "hello \'world\'" --bar=\'foo "bar"\'')
     args[0].should.equal('--foo')
diff --git a/test/yargs-parser.js b/test/yargs-parser.js
index 7b52a0b..f642093 100644
--- a/test/yargs-parser.js
+++ b/test/yargs-parser.js
@@ -747,6 +747,20 @@ describe('yargs-parser', function () {
         bar: 'bar'
       })
     })
+
+    it('should load objects with first object having greatest priority', function () {
+      var argv = parser(['--foo', 'bar'], {
+        configObjects: [{
+          bar: 'baz'
+        }, {
+          bar: 'quux',
+          foo: 'spam'
+        }]
+      })
+
+      argv.should.have.property('foo', 'bar')
+      argv.should.have.property('bar', 'baz')
+    })
   })
 
   describe('dot notation', function () {
@@ -1218,6 +1232,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: {
@@ -1759,17 +1788,6 @@ describe('yargs-parser', function () {
 
       result.foo.should.eql('a')
     })
-
-    it('should be ignored if input is an array', function () {
-      var result = parser(['--foo', 'a', 'b'], {
-        array: 'foo',
-        narg: {
-          foo: 1
-        }
-      })
-
-      result.foo.should.eql(['a', 'b'])
-    })
   })
 
   describe('env vars', function () {
@@ -2499,6 +2517,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