Repository: yargs/yargs
Pull request #1425 (2019-09-16 05:23:56)

docs: update supported locales


Patch:
diff --git a/docs/api.md b/docs/api.md
index d7eeb9e..0d7f3e7 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -883,6 +883,7 @@ Ye be havin' to set the followin' argument land lubber: option
 
 Locales currently supported:
 
+* **be:** Belarusian.
 * **de:** German.
 * **en:** American English.
 * **es:** Spanish.
@@ -895,6 +896,7 @@ Locales currently supported:
 * **ko:** Korean.
 * **nb:** Norwegian Bokmål.
 * **nl:** Dutch.
+* **nn:** Norwegian Nynorsk.
 * **pirate:** American Pirate.
 * **pl:** Polish.
 * **pt:** Portuguese.
@@ -902,7 +904,8 @@ Locales currently supported:
 * **ru:** Russian.
 * **th:** Thai.
 * **tr:** Turkish.
-* **zh_CN:** Chinese.
+* **zh_CN:** Chinese (Mainland China).
+* **zh_TW:** Chinese (Taiwan).
 
 To submit a new translation for yargs:
 
@@ -1147,7 +1150,7 @@ command handler, for example.
 If the arguments have not been parsed, this property is `false`.
 
 If the arguments have been parsed, this contain detailed parsed arguments. See
-the documentation in [yargs-parser `.detailed()`][https://github.com/yargs/yargs-parser/blob/master/README.md#requireyargs-parserdetailedargs-opts]
+the documentation in [yargs-parser `.detailed()`](https://github.com/yargs/yargs-parser/blob/master/README.md#requireyargs-parserdetailedargs-opts)
 for details of this object
 
 <a name="parserConfiguration"></a>.parserConfiguration(obj)
diff --git a/lib/is-promise.js b/lib/is-promise.js
index 0516def..271d93b 100644
--- a/lib/is-promise.js
+++ b/lib/is-promise.js
@@ -1,3 +1,3 @@
 module.exports = function isPromise (maybePromise) {
-  return maybePromise instanceof Promise
+  return !!maybePromise && !!maybePromise.then && (typeof maybePromise.then === 'function')
 }
diff --git a/lib/middleware.js b/lib/middleware.js
index 9f9c196..56dab75 100644
--- a/lib/middleware.js
+++ b/lib/middleware.js
@@ -40,8 +40,7 @@ function applyMiddleware (argv, yargs, middlewares, beforeValidation) {
   const beforeValidationError = new Error('middleware cannot return a promise when applyBeforeValidation is true')
   return middlewares
     .reduce((accumulation, middleware) => {
-      if (middleware.applyBeforeValidation !== beforeValidation &&
-          !isPromise(accumulation)) {
+      if (middleware.applyBeforeValidation !== beforeValidation) {
         return accumulation
       }
 
diff --git a/lib/validation.js b/lib/validation.js
index 1a31e58..35659a3 100644
--- a/lib/validation.js
+++ b/lib/validation.js
@@ -224,43 +224,36 @@ module.exports = function validation (yargs, usage, y18n) {
     return implied
   }
 
+  function keyExists (argv, val) {
+    // convert string '1' to number 1
+    let num = Number(val)
+    val = isNaN(num) ? val : num
+
+    if (typeof val === 'number') {
+      // check length of argv._
+      val = argv._.length >= val
+    } else if (val.match(/^--no-.+/)) {
+      // check if key/value doesn't exist
+      val = val.match(/^--no-(.+)/)[1]
+      val = !argv[val]
+    } else {
+      // check if key/value exists
+      val = argv[val]
+    }
+    return val
+  }
+
   self.implications = function implications (argv) {
     const implyFail = []
 
     Object.keys(implied).forEach((key) => {
       const origKey = key
       ;(implied[key] || []).forEach((value) => {
-        let num
         let key = origKey
         const origValue = value
+        key = keyExists(argv, key)
+        value = keyExists(argv, value)
 
-        // convert string '1' to number 1
-        num = Number(key)
-        key = isNaN(num) ? key : num
-
-        if (typeof key === 'number') {
-          // check length of argv._
-          key = argv._.length >= key
-        } else if (key.match(/^--no-.+/)) {
-          // check if key doesn't exist
-          key = key.match(/^--no-(.+)/)[1]
-          key = !argv[key]
-        } else {
-          // check if key exists
-          key = argv[key]
-        }
-
-        num = Number(value)
-        value = isNaN(num) ? value : num
-
-        if (typeof value === 'number') {
-          value = argv._.length >= value
-        } else if (value.match(/^--no-.+/)) {
-          value = value.match(/^--no-(.+)/)[1]
-          value = !argv[value]
-        } else {
-          value = argv[value]
-        }
         if (key && !value) {
           implyFail.push(` ${origKey} -> ${origValue}`)
         }
diff --git a/yargs.js b/yargs.js
index a4ed28e..8280ae5 100644
--- a/yargs.js
+++ b/yargs.js
@@ -94,14 +94,17 @@ function Yargs (processArgs, cwd, parentRequire) {
       })
     })
 
-    // preserve all groups not set to local.
-    preservedGroups = Object.keys(groups).reduce((acc, groupName) => {
-      const keys = groups[groupName].filter(key => !(key in localLookup))
-      if (keys.length > 0) {
-        acc[groupName] = keys
-      }
-      return acc
-    }, {})
+    // add all groups not set to local to preserved groups
+    Object.assign(
+      preservedGroups,
+      Object.keys(groups).reduce((acc, groupName) => {
+        const keys = groups[groupName].filter(key => !(key in localLookup))
+        if (keys.length > 0) {
+          acc[groupName] = keys
+        }
+        return acc
+      }, {})
+    )
     // groups can now be reset
     groups = {}
 


Test patch:
diff --git a/test/is-promise.js b/test/is-promise.js
new file mode 100644
index 0000000..40f8662
--- /dev/null
+++ b/test/is-promise.js
@@ -0,0 +1,34 @@
+'use strict'
+/* global describe, it */
+
+const { expect } = require('chai')
+const isPromise = require('../lib/is-promise')
+
+describe('isPromise', () => {
+  it('returns `false` on non promise value', () => {
+    expect(isPromise('hello there.')).to.be.equal(false)
+  })
+
+  it('returns `true` on es6 promise', () => {
+    const esPromise = new Promise(() => {})
+    expect(isPromise(esPromise)).to.be.equal(true)
+  })
+
+  it('returns `true` on some other thenable', () => {
+    const thenable = { then: () => {} }
+    expect(isPromise(thenable)).to.be.equal(true)
+  })
+
+  it('returns `false` if some falsy value is passed', () => {
+    expect(isPromise(null)).to.be.equal(false)
+  })
+
+  it('returns `false` if passed object has no `then` property', () => {
+    expect(isPromise({})).to.be.equal(false)
+  })
+
+  it('returns `false` if `then` is not a function', () => {
+    const obj = { then: 4711 }
+    expect(isPromise(obj)).to.be.equal(false)
+  })
+})
diff --git a/test/middleware.js b/test/middleware.js
index dd6638c..5ea3189 100644
--- a/test/middleware.js
+++ b/test/middleware.js
@@ -145,6 +145,36 @@ describe('middleware', () => {
         .exitProcess(false)
         .parse()
     })
+
+    it('calls an async middleware only once for nested subcommands', (done) => {
+      let callCount = 0
+      let argv = yargs('cmd subcmd')
+        .command(
+          'cmd',
+          'cmd command',
+          function (yargs) {
+            yargs.command(
+              'subcmd',
+              'subcmd command',
+              function (yargs) {}
+            )
+          }
+        )
+        .middleware((argv) => new Promise((resolve) => {
+          callCount++
+          resolve(argv)
+        }))
+        .parse()
+
+      if (!(argv instanceof Promise)) done('argv should be a Promise')
+
+      argv
+        .then(() => {
+          callCount.should.equal(1)
+          done()
+        })
+        .catch(err => done(err))
+    })
   })
 
   // see: https://github.com/yargs/yargs/issues/1281
diff --git a/test/usage.js b/test/usage.js
index 3705271..ea25240 100644
--- a/test/usage.js
+++ b/test/usage.js
@@ -1807,6 +1807,80 @@ describe('usage tests', () => {
       ])
     })
 
+    it('should display global non empty groups for commands', () => {
+      const r = checkUsage(() => yargs(['upload', '-h'])
+        .command('upload', 'upload something', yargs => yargs
+          .option('q', {
+            type: 'boolean'
+          })
+          .wrap(null))
+        .option('i', {
+          type: 'boolean',
+          global: true
+        })
+        .option('j', {
+          type: 'boolean',
+          global: false // not global so not preserved, even though the group is
+        })
+        .group(['i', 'j'], 'Awesome Flags:')
+        .help('h')
+        .wrap(null)
+        .parse()
+      )
+
+      r.logs[0].split('\n').should.deep.equal([
+        'usage upload',
+        '',
+        'upload something',
+        '',
+        'Awesome Flags:',
+        '  -i  [boolean]',
+        '',
+        'Options:',
+        '  --version  Show version number  [boolean]',
+        '  -h         Show help  [boolean]',
+        '  -q  [boolean]'
+      ])
+    })
+
+    it('should display global non empty groups for subcommands', () => {
+      const r = checkUsage(() => yargs(['do', 'upload', '-h'])
+        .command('do', 'do something', yargs => yargs
+          .command('upload', 'upload something', yargs => yargs
+            .option('q', {
+              type: 'boolean'
+            })
+            .wrap(null))
+          .wrap(null))
+        .option('i', {
+          type: 'boolean',
+          global: true
+        })
+        .option('j', {
+          type: 'boolean',
+          global: false // not global so not preserved, even though the group is
+        })
+        .group(['i', 'j'], 'Awesome Flags:')
+        .help('h')
+        .wrap(null)
+        .parse()
+      )
+
+      r.logs[0].split('\n').should.deep.equal([
+        'usage do upload',
+        '',
+        'upload something',
+        '',
+        'Awesome Flags:',
+        '  -i  [boolean]',
+        '',
+        'Options:',
+        '  --version  Show version number  [boolean]',
+        '  -h         Show help  [boolean]',
+        '  -q  [boolean]'
+      ])
+    })
+
     it('should list a module command only once', () => {
       const r = checkUsage(() => yargs('--help')
         .command('upload', 'upload something', {