Repository: yargs/yargs
Pull request #1221 (2018-10-02 16:40:56)

fix: hide `hidden` options from help output even if they are in a group
```js
require('yargs')
.options({
  foo: {
    describe: 'FOO',
    group: 'Hidden',
    hidden: true
  },
  baz: {
    describe: 'BAZ',
    group: 'Hidden',
    hidden: true
  }
})
  .help()
  .argv
```

Before:
```bash
Hidden
  --foo  FOO
  --baz  BAZ

Options:
  --version  Show version number                                       [boolean]
  --help     Show help                                                 [boolean]
```

After:
```bash
Options:
  --version  Show version number                                       [boolean]
  --help     Show help                                                 [boolean]
```

pvdlg: For some reason `standard` doesn't return errors locally but it returns a bunch of indentation errors on the CI.... No idea why....So I have to trigger multiple CI runs to fix those linting errors...

Patch:
diff --git a/lib/usage.js b/lib/usage.js
index 6451c98..9be8a99 100644
--- a/lib/usage.js
+++ b/lib/usage.js
@@ -161,13 +161,7 @@ module.exports = function usage (yargs, y18n) {
     keys = keys.concat(Object.keys(demandedOptions))
     keys = keys.concat(Object.keys(demandedCommands))
     keys = keys.concat(Object.keys(options.default))
-    keys = keys.filter(key => {
-      if (options.hiddenOptions.indexOf(key) < 0) {
-        return true
-      } else if (yargs.parsed.argv[options.showHiddenOpt]) {
-        return true
-      }
-    })
+    keys = keys.filter(filterHiddenOptions)
     keys = Object.keys(keys.reduce((acc, key) => {
       if (key !== '_') acc[key] = true
       return acc
@@ -251,11 +245,9 @@ module.exports = function usage (yargs, y18n) {
     Object.keys(groups).forEach((groupName) => {
       if (!groups[groupName].length) return
 
-      ui.div(__(groupName))
-
       // if we've grouped the key 'f', but 'f' aliases 'foobar',
       // normalizedKeys should contain only 'foobar'.
-      const normalizedKeys = groups[groupName].map((key) => {
+      const normalizedKeys = groups[groupName].filter(filterHiddenOptions).map((key) => {
         if (~aliasKeys.indexOf(key)) return key
         for (let i = 0, aliasKey; (aliasKey = aliasKeys[i]) !== undefined; i++) {
           if (~(options.alias[aliasKey] || []).indexOf(key)) return aliasKey
@@ -263,6 +255,10 @@ module.exports = function usage (yargs, y18n) {
         return key
       })
 
+      if (normalizedKeys.length < 1) return
+
+      ui.div(__(groupName))
+
       // actually generate the switches string --foo, -f, --bar.
       const switches = normalizedKeys.reduce((acc, key) => {
         acc[key] = [ key ].concat(options.alias[key] || [])
@@ -420,6 +416,10 @@ module.exports = function usage (yargs, y18n) {
     return groupedKeys
   }
 
+  function filterHiddenOptions (key) {
+    return yargs.getOptions().hiddenOptions.indexOf(key) < 0 || yargs.parsed.argv[yargs.getOptions().showHiddenOpt]
+  }
+
   self.showHelp = (level) => {
     const logger = yargs._getLoggerInstance()
     if (!level) level = 'error'
diff --git a/package.json b/package.json
index 6e97d53..21d2661 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,7 @@
   ],
   "dependencies": {
     "cliui": "^4.0.0",
-    "decamelize": "^2.0.0",
+    "decamelize": "^1.2.0",
     "find-up": "^3.0.0",
     "get-caller-file": "^1.0.1",
     "os-locale": "^3.0.0",


Test patch:
diff --git a/test/usage.js b/test/usage.js
index 6cfad3e..bcc216c 100644
--- a/test/usage.js
+++ b/test/usage.js
@@ -2853,6 +2853,61 @@ describe('usage tests', () => {
         '  --bar'
       ])
     })
+    it('--help should display all options except for hidden ones even in a group', () => {
+      const r = checkUsage(() => yargs('--help')
+        .options({
+          foo: {
+            describe: 'FOO',
+            hidden: true
+          },
+          bar: {},
+          baz: {
+            describe: 'BAZ',
+            group: 'Hidden:',
+            hidden: true
+          }
+        })
+        .parse()
+      )
+
+      r.logs[0].split('\n').should.deep.equal([
+        'Options:',
+        '  --help     Show help                                                 [boolean]',
+        '  --version  Show version number                                       [boolean]',
+        '  --bar'
+      ])
+    })
+    it('--help should display all groups except for ones with only hidden options', () => {
+      const r = checkUsage(() => yargs('--help')
+        .options({
+          foo: {
+            describe: 'FOO',
+            group: 'Hidden:',
+            hidden: true
+          },
+          bar: {},
+          baz: {
+            describe: 'BAZ',
+            group: 'Hidden:',
+            hidden: true
+          },
+          qux: {
+            group: 'Shown:'
+          }
+        })
+        .parse()
+      )
+
+      r.logs[0].split('\n').should.deep.equal([
+        'Shown:',
+        '  --qux',
+        '',
+        'Options:',
+        '  --help     Show help                                                 [boolean]',
+        '  --version  Show version number                                       [boolean]',
+        '  --bar'
+      ])
+    })
     it('--help should display all options (including hidden ones) with --show-hidden', () => {
       const r = checkUsage(() => yargs('--help --show-hidden --mama ama')
         .options({
@@ -2877,6 +2932,41 @@ describe('usage tests', () => {
         '  --baz      BAZ'
       ])
     })
+    it('--help should display all groups (including ones with only hidden options) with --show-hidden', () => {
+      const r = checkUsage(() => yargs('--help --show-hidden')
+        .options({
+          foo: {
+            describe: 'FOO',
+            group: 'Hidden:',
+            hidden: true
+          },
+          bar: {},
+          baz: {
+            describe: 'BAZ',
+            group: 'Hidden:',
+            hidden: true
+          },
+          qux: {
+            group: 'Shown:'
+          }
+        })
+        .argv
+      )
+
+      r.logs[0].split('\n').should.deep.equal([
+        'Hidden:',
+        '  --foo  FOO',
+        '  --baz  BAZ',
+        '',
+        'Shown:',
+        '  --qux',
+        '',
+        'Options:',
+        '  --help     Show help                                                 [boolean]',
+        '  --version  Show version number                                       [boolean]',
+        '  --bar'
+      ])
+    })
     it('--help should display --custom-show-hidden', () => {
       const r = checkUsage(() => yargs('--help')
         .options({