[英]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 倍:
scriptName
to the yargs configscriptName
.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:安装自动补全的步骤:
npm link
npm link
eddy completion >> ~/.bashrc
eddy completion >> ~/.bashrc
source ~/.bashrc
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.