Repository: yargs/yargs-parser
Pull request #249 (2020-03-01 01:28:37)

feat: introduce greedy-arrays config, for specifying whether arrays consume multiple positionals
I'm convinced that the behavior of the `array` type is too aggressive, what folks actually want, is the ability to:

1. default an argument to an array type, if only provided once.
2. have support for typed arrays, e.g., both an `array` and a `string`.

This PR introduces the config option `greedy-arrays`, I think in `yargs-parser@18` we should consider making this setting default to `false`.


Patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
new file mode 100644
index 0000000..00dea34
--- /dev/null
+++ b/.github/workflows/ci.yaml
@@ -0,0 +1,42 @@
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+name: ci
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        node: [8, 10, 12, 13]
+    steps:
+      - uses: actions/checkout@v1
+      - uses: actions/setup-node@v1
+        with:
+          node-version: ${{ matrix.node }}
+      - run: node --version
+      - run: npm install
+      - run: npm test
+  windows:
+    runs-on: windows-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions/setup-node@v1
+        with:
+          node-version: 12
+      - run: npm install
+      - run: npm test
+  coverage:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v1
+      - uses: actions/setup-node@v1
+        with:
+          node-version: 12
+      - run: npm install
+      - run: npm test
+      - run: npm run coverage
+    env:
+      COVERALLS_REPO_TOKEN: "${{ secrets.COVERALLS_REPO_TOKEN }}"
+      COVERALLS_GIT_BRANCH: "${{ github.ref }}"
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 73a542d..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-language: node_js
-os:
-  - linux
-  - windows
-node_js:
-  - "8"
-  - "10"
-  - "12"
-  - "13"
-
-jobs:
-  include:
-    - stage: coverage
-      node_js: "13"
-      script:
-        - npm t
-        - npm run coverage
diff --git a/README.md b/README.md
index 1678664..6e9ea79 100644
--- a/README.md
+++ b/README.md
@@ -265,6 +265,27 @@ node example.js -x 1 2 -x 3 4
 { _: [], x: [[1, 2], [3, 4]] }
 ```
 
+### greedy arrays
+
+* default: `true`
+* key: `greedy-arrays`
+
+Should arrays consume more than one positional argument following their flag.
+
+```sh
+node example --arr 1 2
+{ _[], arr: [1, 2] }
+```
+
+_if disabled:_
+
+```sh
+node example --arr 1 2
+{ _[2], arr: [1] }
+```
+
+**Note: in `v18.0.0` we are considering defaulting greedy arrays to `false`.**
+
 ### nargs eats options
 
 * default: `false`
diff --git a/index.js b/index.js
index 0e7448c..542f681 100644
--- a/index.js
+++ b/index.js
@@ -13,22 +13,23 @@ function parse (args, opts) {
   // aliases might have transitive relationships, normalize this.
   const aliases = combineAliases(Object.assign(Object.create(null), opts.alias))
   const configuration = Object.assign({
-    'short-option-groups': true,
+    'boolean-negation': true,
     'camel-case-expansion': true,
+    'combine-arrays': false,
     'dot-notation': true,
-    'parse-numbers': true,
-    'boolean-negation': true,
-    'negation-prefix': 'no-',
     'duplicate-arguments-array': true,
     'flatten-duplicate-arrays': true,
+    'greedy-arrays': true,
+    'halt-at-non-option': false,
+    'nargs-eats-options': false,
+    'negation-prefix': 'no-',
+    'parse-numbers': true,
     'populate--': false,
-    'combine-arrays': false,
     'set-placeholder-key': false,
-    'halt-at-non-option': false,
+    'short-option-groups': true,
     'strip-aliased': false,
     'strip-dashed': false,
-    'unknown-options-as-args': false,
-    'nargs-eats-options': false
+    'unknown-options-as-args': false
   }, opts.configuration)
   const defaults = Object.assign(Object.create(null), opts.default)
   const configObjects = opts.configObjects || []
@@ -412,6 +413,7 @@ function parse (args, opts) {
         if (/^-/.test(next) && !negative.test(next) && !isUnknownOptionAsArg(next)) break
         i = ii
         argsToSet.push(processValue(key, next))
+        if (!configuration['greedy-arrays']) break
       }
     }
 


Test patch:
diff --git a/test/yargs-parser.js b/test/yargs-parser.js
index 6b22360..8160562 100644
--- a/test/yargs-parser.js
+++ b/test/yargs-parser.js
@@ -3588,4 +3588,40 @@ describe('yargs-parser', function () {
       parse.error.message.should.equal('Not enough arguments following: a')
     })
   })
+
+  describe('greedy-arrays=false', () => {
+    it('does not consume more than one argument after array option', () => {
+      const argv = parser(['--arr', 'foo', 'bar'], {
+        array: 'arr',
+        configuration: {
+          'greedy-arrays': false
+        }
+      })
+      argv.arr.should.eql(['foo'])
+      argv._.should.eql(['bar'])
+    })
+
+    it('places argument into array when specified multiple times', () => {
+      const argv = parser(['--arr', 99, 'foo', '--arr', 'hello', 'bar'], {
+        array: 'arr',
+        configuration: {
+          'greedy-arrays': false
+        }
+      })
+      argv.arr.should.eql([99, 'hello'])
+      argv._.should.eql(['foo', 'bar'])
+    })
+
+    it('places boolean arguments into array when specified multiple times', () => {
+      const argv = parser(['--arr', 101, '--arr', 102, '--arr', 'false'], {
+        array: 'arr',
+        boolean: 'arr',
+        configuration: {
+          'greedy-arrays': false
+        }
+      })
+      argv.arr.should.eql([true, true, false])
+      argv._.should.eql([101, 102])
+    })
+  })
 })