Repository: yargs/yargs
Pull request #1385 (2019-07-29 22:24:52)

Allow completionCommand to be set via showCompletionScript
### Problem
When using `showCompletionScript` the variable `completionCommand` is `null` resulting in a bad installation instruction. For example:

`yargs.showCompletionScript('/usr/local/bin/my-app');`
```shell
###-begin-my-app-completions-###
#
# yargs command completion script
#
# Installation: /usr/local/bin/my-app null >> ~/.bashrc
#    or /usr/local/bin/my-app null >> ~/.bash_profile on OSX.
#
_yargs_completions()
{
    local cur_word args type_list

    cur_word="${COMP_WORDS[COMP_CWORD]}"
    args=("${COMP_WORDS[@]}")

    # ask yargs to generate completions.
    type_list=$(/usr/local/bin/my-app --get-yargs-completions "${args[@]}")

    COMPREPLY=( $(compgen -W "${type_list}" -- ${cur_word}) )

    # if no match was found, fall back to filename completion
    if [ ${#COMPREPLY[@]} -eq 0 ]; then
      COMPREPLY=()
    fi

    return 0
}
complete -o default -F _yargs_completions my-app
###-end-my-app-completions-###
```

Notice that `null` is printed for the command.

It's possible to set the `completionCommand` variable by calling `yargs.completion()` but this will also log the completion script resulting in two scripts being logged when used in conjunction with `showCompletionScript`:

`yargs.completion().showCompletionScript('/usr/local/bin/my-app');`
```shell
###-begin-my-app-completions-###
#
# yargs command completion script
#
# Installation: /usr/local/bin/my-app completion >> ~/.bashrc
#    or /usr/local/bin/my-app completion >> ~/.bash_profile on OSX.
#
_yargs_completions()
{
    local cur_word args type_list

    cur_word="${COMP_WORDS[COMP_CWORD]}"
    args=("${COMP_WORDS[@]}")

    # ask yargs to generate completions.
    type_list=$(/usr/local/bin/my-app --get-yargs-completions "${args[@]}")

    COMPREPLY=( $(compgen -W "${type_list}" -- ${cur_word}) )

    # if no match was found, fall back to filename completion
    if [ ${#COMPREPLY[@]} -eq 0 ]; then
      COMPREPLY=()
    fi

    return 0
}
complete -o default -F _yargs_completions my-app
###-end-my-app-completions-###

###-begin-my-app-completions-###
#
# yargs command completion script
#
# Installation: my-app completion >> ~/.bashrc
#    or my-app completion >> ~/.bash_profile on OSX.
#
_yargs_completions()
{
    local cur_word args type_list

    cur_word="${COMP_WORDS[COMP_CWORD]}"
    args=("${COMP_WORDS[@]}")

    # ask yargs to generate completions.
    type_list=$(my-app --get-yargs-completions "${args[@]}")

    COMPREPLY=( $(compgen -W "${type_list}" -- ${cur_word}) )

    # if no match was found, fall back to filename completion
    if [ ${#COMPREPLY[@]} -eq 0 ]; then
      COMPREPLY=()
    fi

    return 0
}
complete -o default -F _yargs_completions my-app
###-end-my-app-completions-###
```

In this case the first completion script output is correct but the second one is incorrect.

### Fix
Add a second parameter to `showCompletionScript` that allows the developer to set the completion command variable.

`yargs.showCompletionScript('/usr/local/bin/my-app', 'completion');`
```shell
###-begin-my-app-completions-###
#
# yargs command completion script
#
# Installation: /usr/local/bin/my-app completion >> ~/.bashrc
#    or /usr/local/bin/my-app completion >> ~/.bash_profile on OSX.
#
_yargs_completions()
{
    local cur_word args type_list

    cur_word="${COMP_WORDS[COMP_CWORD]}"
    args=("${COMP_WORDS[@]}")

    # ask yargs to generate completions.
    type_list=$(/usr/local/bin/my-app --get-yargs-completions "${args[@]}")

    COMPREPLY=( $(compgen -W "${type_list}" -- ${cur_word}) )

    # if no match was found, fall back to filename completion
    if [ ${#COMPREPLY[@]} -eq 0 ]; then
      COMPREPLY=()
    fi

    return 0
}
complete -o default -F _yargs_completions my-app
###-end-my-app-completions-###
```

It will default to "completion" in the same way that `completion()` does.


Patch:
diff --git a/lib/usage.js b/lib/usage.js
index 98d1508..fed6d9f 100644
--- a/lib/usage.js
+++ b/lib/usage.js
@@ -122,9 +122,9 @@ module.exports = function usage (yargs, y18n) {
   }
   self.getDescriptions = () => descriptions
 
-  let epilog
+  let epilogs = []
   self.epilog = (msg) => {
-    epilog = msg
+    epilogs.push(msg)
   }
 
   let wrapSet = false
@@ -345,8 +345,8 @@ module.exports = function usage (yargs, y18n) {
     }
 
     // the usage string.
-    if (epilog) {
-      const e = epilog.replace(/\$0/g, base$0)
+    if (epilogs.length > 0) {
+      const e = epilogs.map(epilog => epilog.replace(/\$0/g, base$0)).join('\n')
       ui.div(`${e}\n`)
     }
 
@@ -505,7 +505,7 @@ module.exports = function usage (yargs, y18n) {
     failureOutput = false
     usages = []
     usageDisabled = false
-    epilog = undefined
+    epilogs = []
     examples = []
     commands = []
     descriptions = objFilter(descriptions, (k, v) => !localLookup[k])
@@ -520,7 +520,7 @@ module.exports = function usage (yargs, y18n) {
     frozen.failureOutput = failureOutput
     frozen.usages = usages
     frozen.usageDisabled = usageDisabled
-    frozen.epilog = epilog
+    frozen.epilogs = epilogs
     frozen.examples = examples
     frozen.commands = commands
     frozen.descriptions = descriptions
@@ -531,7 +531,7 @@ module.exports = function usage (yargs, y18n) {
     failureOutput = frozen.failureOutput
     usages = frozen.usages
     usageDisabled = frozen.usageDisabled
-    epilog = frozen.epilog
+    epilogs = frozen.epilogs
     examples = frozen.examples
     commands = frozen.commands
     descriptions = frozen.descriptions
diff --git a/yargs.js b/yargs.js
index 67f9d11..9519c8c 100644
--- a/yargs.js
+++ b/yargs.js
@@ -908,7 +908,7 @@ function Yargs (processArgs, cwd, parentRequire) {
     }
 
     // register the completion command.
-    completionCommand = cmd || 'completion'
+    completionCommand = cmd || completionCommand || 'completion'
     if (!desc && desc !== false) {
       desc = 'generate completion script'
     }
@@ -920,9 +920,10 @@ function Yargs (processArgs, cwd, parentRequire) {
     return self
   }
 
-  self.showCompletionScript = function ($0) {
-    argsert('[string]', [$0], arguments.length)
+  self.showCompletionScript = function ($0, cmd) {
+    argsert('[string] [string]', [$0, cmd], arguments.length)
     $0 = $0 || self.$0
+    completionCommand = cmd || completionCommand || 'completion'
     _logger.log(completion.generateCompletionScript($0, completionCommand))
     return self
   }


Test patch:
diff --git a/test/completion.js b/test/completion.js
index 7625b8e..4324fc5 100644
--- a/test/completion.js
+++ b/test/completion.js
@@ -158,6 +158,13 @@ describe('Completion', () => {
 
       r.logs[0].should.match(/\.\/test.js --get-yargs-completions/)
     })
+
+    it('allows $0 and cmd to be set', () => {
+      const r = checkUsage(() => yargs([])
+        .showCompletionScript('/path/to/my/app', 'show-completions-script'))
+
+      r.logs[0].should.match(/Installation: \/path\/to\/my\/app show-completions-script/)
+    })
   })
 
   describe('completion()', () => {
diff --git a/test/usage.js b/test/usage.js
index 20be98b..f3c9725 100644
--- a/test/usage.js
+++ b/test/usage.js
@@ -1943,6 +1943,28 @@ describe('usage tests', () => {
       ])
     })
 
+    it('supports multiple epilogs', () => {
+      const r = checkUsage(() => yargs('')
+        .epilog('for more info view the manual at http://example.com')
+        .epilog('you can also find us on slack at http://devtoolscommunity.herokuapp.com')
+        .epilog('keep up to date by reading our blog at http://yargs.js.org/blog.html')
+        .demand('y')
+        .wrap(null)
+        .parse()
+      )
+
+      r.errors.join('\n').split(/\n+/).should.deep.equal([
+        'Options:',
+        '  --help     Show help  [boolean]',
+        '  --version  Show version number  [boolean]',
+        '  -y  [required]',
+        'for more info view the manual at http://example.com',
+        'you can also find us on slack at http://devtoolscommunity.herokuapp.com',
+        'keep up to date by reading our blog at http://yargs.js.org/blog.html',
+        'Missing required argument: y'
+      ])
+    })
+
     it('replaces $0 in epilog string', () => {
       const r = checkUsage(() => yargs('')
         .epilog("Try '$0 --long-help' for more information")