简体   繁体   English

使用 --experimental-specifier-resolution=node 时如何让 yargs 自动完成工作

[英]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.我的目标是在 Typescript/node.js 中编写一个 CLI,它使用--experimental-specifier-resolution=node ,用 yargs 编写并支持自动完成。

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)为了完成这项工作,我使用了这个entry.sh文件,这要归功于这个有用的SO anwswer (以及 package.json 中的bin: {eddy: "./entry.sh"}选项指向这个文件)

#!/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.这很好用,我可以使用 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.根据 yargs,我应该能够通过将./entry.sh completion的结果输出到~/.bashrc配置文件来获得自动完成。 However this does not seem to work.但是,这似乎不起作用。

Output from ./entry.sh completion : Output 来自./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我尝试修改completion output,但我不太了解 bash - 还没有

Update更新

Working on a reproducible example (WIP).研究可重现的示例(WIP)。 Repo is here .回购在这里

Currently one of the big differences is that npm link does not work the same in the 2 different environments.目前最大的区别之一是npm link在 2 个不同的环境中的工作方式不同。 It's only in the repo where I'm trying to reproduce that /usr/local/share/npm-global/bin/ is actually updated.只有在我试图重现/usr/local/share/npm-global/bin/实际更新的存储库中。 Currently trying to investigate this.目前正试图对此进行调查。

You can try specifying the scriptName in your entry.js file to the name of your wrapper script.您可以尝试将entry.js文件中的entry.js指定为包装脚本的名称。 This may force generation of completion name using it.这可能会强制使用它生成完成名称。 I haven't tried it but looking at the source code of yargs, it looks like the $0 parameter can be altered using scriptName , which in turn will affect how the completion-generation function generate the completion code:我没有尝试过,但是查看 yargs 的源代码,看起来$0参数可以使用scriptName进行更改,这反过来会影响完成生成 function 如何生成完成代码:

In yargs-factor.ts :yargs-factor.ts

  scriptName(scriptName: string): YargsInstance {
    this.customScriptName = true;
    this.$0 = scriptName;
    return this;
  }

In completion.ts :completion.ts中:

  generateCompletionScript($0: string, cmd: string): string {
    let script = this.zshShell
      ? templates.completionZshTemplate
      : templates.completionShTemplate;
    const name = this.shim.path.basename($0);

    // add ./ to applications not yet installed as bin.
    if ($0.match(/\.js$/)) $0 = `./${$0}`;

    script = script.replace(/{{app_name}}/g, name);
    script = script.replace(/{{completion_command}}/g, cmd);
    return script.replace(/{{app_path}}/g, $0);
  }

Also I'm not sure how the "bin" configuration works but maybe because of scriptName you'd no longer need a wrapper.另外我不确定"bin"配置是如何工作的,但可能是因为scriptName你不再需要一个包装器。

Make sure the version of yargs you use supports this.确保您使用的 yargs 版本支持这一点。

Also as a side note I thought about suggesting to modify the generated completion script directly but besides being hackish that might also still lead to the script name being unrecognized during completion.另外作为旁注,我考虑过建议直接修改生成的完成脚本,但除了是hackish之外,它还可能导致脚本名称在完成过程中无法识别。 Anyhow I just looked at the right approach first.无论如何,我只是先看看正确的方法。

The modified version would like this:修改后的版本是这样的:

_entry.sh_yargs_completions()
{
    local cur_word args type_list

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

    # ask yargs to generate completions.
    type_list=$(/path/to/entry.sh --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.sh_yargs_completions entry.sh

Another note: If the script name needs to be dynamic based on the name of its caller, you can make it identifiable through an environment variable, so in entry.sh you can declare it like this:另一个注意事项:如果脚本名称需要根据其调用者的名称是动态的,您可以通过环境变量使其可识别,因此在entry.sh中您可以这样声明:

export ENTRY_JS_SCRIPT_NAME=entry.sh
node ...

Then somewhere in entry.js , you can access the variable name through this:然后在entry.js的某个地方,您可以通过以下方式访问变量名称:

process.env.ENTRY_JS_SCRIPT_NAME

Maybe even just specify $0 or ${0##*/} whatever works:甚至可以只指定$0${0##*/}任何可行的方法:

export ENTRY_JS_SCRIPT_NAME=$0

Thanks, everyone.感谢大家。 The solution I ended up with, was 2 fold:我最终得到的解决方案是 2 倍:

  1. I added a scriptName to the yargs config我在 yargs 配置中添加了一个scriptName
  2. In the .sh file "wrapping", I used which node to probably set the --experimental-specifier-resolution=node flags..sh文件“包装”中,我使用了可能设置--experimental-specifier-resolution=node which node的节点。

test-cli.js测试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测试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 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运行npm link
  2. run eddy completion >> ~/.bashrc运行eddy completion >> ~/.bashrc
  3. source ~/.bashrc
  4. profit利润

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM