Search code examples
node.jsbashyargs

How to get yargs auto-complete working, when using --experimental-specifier-resolution=node


My objective is to write a CLI in Typescript/node.js, that uses --experimental-specifier-resolution=node, written in yargs with support for autocompletion.

To make this work, I use this entry.sh file, thanks to this helpful SO anwswer (and the bin: {eddy: "./entry.sh"} options in package.json points to this file)

#!/usr/bin/env bash

full_path=$(realpath $0)
dir_path=$(dirname $full_path)
script_path="$dir_path/dist/src/cli/entry.js"

# Path is made thanks to: https://code-maven.com/bash-shell-relative-path
# Combined with knowledge from: https://stackoverflow.com/questions/68111434/how-to-run-node-js-cli-with-experimental-specifier-resolution-node

/usr/bin/env node --experimental-specifier-resolution=node $script_path "$@"

This works great, and I can use the CLI. However, autocompletion does not work. According to yargs I should be able to get autocompletion by outputting the result from ./entry.sh completion to the ~/.bashrc profile. However this does not seem to work.

Output from ./entry.sh completion:

###-begin-entry.js-completions-###
#
# yargs command completion script
#
# Installation: ./dist/src/cli/entry.js completion >> ~/.bashrc
#    or ./dist/src/cli/entry.js completion >> ~/.bash_profile on OSX.
#
_entry.js_yargs_completions()
{
    local cur_word args type_list

    cur_word="${COMP_WORDS[COMP_CWORD]}"
    args=("${COMP_WORDS[@]}")

    # ask yargs to generate completions.
    type_list=$(./dist/src/cli/entry.js --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 _entry.js_yargs_completions entry.js
###-end-entry.js-completions-###

I tried modifying the completion output, but I don't really understand bash - just yet 😅

Update

Working on a reproducible example (WIP). Repo is here.

Currently one of the big differences is that npm link does not work the same in the 2 different environments. It's only in the repo where I'm trying to reproduce that /usr/local/share/npm-global/bin/ is actually updated. Currently trying to investigate this.


Solution

  • Thanks, everyone. The solution I ended up with, was 2 fold:

    1. I added a scriptName to the yargs config
    2. In the .sh file "wrapping", I used which node to probably set the --experimental-specifier-resolution=node flags.

    test-cli.js

    #!/usr/bin/env node
    
    import yargs from 'yargs'
    import { hideBin } from 'yargs/helpers'
    import { someOtherModule } from './some-other-module';
    
    someOtherModule();
    
    yargs(hideBin(process.argv))
      .command('curl <url>', 'fetch the contents of the URL', () => {}, (argv) => {
        console.info(argv)
      })
      .command('curlAgain <url>', 'fetch the contents of the URL', () => {}, (argv) => {
        console.info(argv)
      })
      .demandCommand(1)
      .help()
      .completion()
      .scriptName('eddy') // <== Added thanks to konsolebox
      .parse()
    

    test-cli.sh

    #!/usr/bin/env bash
    
    full_path="$(realpath "$0")"
    dir_path="$(dirname $full_path)"
    script_path="$dir_path/test-cli.js"
    
    node_path="$(which node)" # <== Makes it work on github codespaces 😅
    
    $node_path --experimental-specifier-resolution=node $script_path "$@"
    

    package.json

    {
      "name": "lets-reproduce",
      "type": "module",
      "dependencies": {
        "yargs": "^17.3.1"
      },
      "bin": {
        "eddy": "./test-cli.sh"
      }
    }
    

    Steps to install autocompletion:

    1. run npm link
    2. run eddy completion >> ~/.bashrc
    3. source ~/.bashrc
    4. profit 😅🔥