likes
comments
collection
share

yargs 库不完全指南

作者站长头像
站长
· 阅读数 1

Yargs 通过解析参数和生成优雅的用户界面,用于构建交互式命令行工具。

使用方式

#!/usr/bin/env node
const yargs = require('yargs/yargs')
const { hideBin } = require('yargs/helpers')
const argv = yargs(hideBin(process.argv)).argv

if (argv.ships > 3 && argv.distance < 53.5) {
  console.log('Plunder more riffiwobbles!')
} else {
  console.log('Retreat from the xupptumblers!')
}

hideBin 是 [process.argv.slice(2)] 的语法糖

摘要

argv

将用户输入的参数转化为对象形式,

yargs(hideBin(process.argv)).argv

yargs(['-a', '-c', '-b', '2']).argv

yargs().parse(['-x', '1', '-y', '2'])

yargs(process.argv.slice(2)).parse()
  • 只会认定 以 - 开头的为参数,只有第一个会被记录进以该参数为键的列表中,后续的会被记录至 argv._ 中;中文也可以被识别;传入非数字会作为字符串记录进参数对象中;若参数后面没有内容则会记录 true (如下图 -e )
  • $0 记录执行的文件名
  • 只能用于顶级作用域,不能用于函数作用域中
  • 当位于 electron 等环境下,未传入参数名时,使用 hideBin 会忽略掉第一个参数,这种情况下可以使用.parse(process.argv.slice(1))进行替代

yargs 库不完全指南

array(key)

yargs(hideBin(process.argv)).array('a').argv)

将参数扁平化,由于一次只能扁平化一个参数,可以进行多次链式调用,将所有参数进行扁平化处理

  • 使用 -- 可以组织将该参数添加至数组中

yargs 库不完全指南

.command(cmd, desc, [builder], [handler])

.command(cmd, desc, [module])

.command(module)

定义模块向外暴露的命令格式

  • Cmd 是 命令 的字符串或 命令及其别名 的字符串数组。
  • desc 给每个命令提供描述。将 desc 设置为 false 可以创建隐藏命令。隐藏命令不会显示在帮助输出中,并且无法完成。
  • builder对象 标识命令接受那些参数,标识参数别名、默认值等; builder 也可以是一个函数。第一个参数为 yargs 实例,可对传入的命令进行定制;第二个参数为布尔值 helpOrVersionSet 用来标识是否设置了--help 或 --version。
  • handler 函数 根据最终获取的参数对象 分派执行具体 脚本
yargs
  .command('get', 'make a get HTTP request', {
    url: {
      alias: 'u',
      default: 'http://yargs.js.org/'
    }
  })
  .help()
  .argv

export class BuildCommand implements RspackCommand {

    async apply(cli: RspackCLI): Promise<void> {
    
        // cli.program 即 yargs

        cli.program.command(

            ["build [entry..]", "$0", "bundle", "b"],

            "run the rspack build",

            yargs =>

                commonOptions(yargs).options({

                    analyze: {

                        type: "boolean",

                        default: false,

                        describe: "analyze"

                    },

                    json: {

                        describe: "emit stats json"

                    }

            }),

            async options => {

                const logger = cli.getLogger();

                let createJsonStringifyStream;

                if (options.json) {

                    const jsonExt = await import("@discoveryjs/json-ext");

                    createJsonStringifyStream = jsonExt.default.stringifyStream;

                }


                const callback = (error, stats: Stats | MultiStats) => {

                    if (error) {

                        logger.error(error);

                        process.exit(2);

                    }

                    if (stats && stats.hasErrors()) {

                        logger.error(stats.toString({ errors: true }));

                        process.exitCode = 1;

                    }

                    if (!compiler || !stats) {

                        return;

                    }

                    const statsOptions = cli.isMultipleCompiler(compiler)

                    ? {

                        children: compiler.compilers.map(compiler =>

                            compiler.options ? compiler.options.stats : undefined)

                    }

                        : compiler.options

                        ? compiler.options.stats

                        : undefined;

                    if (options.json && createJsonStringifyStream) {

                        const handleWriteError = error => {

                            logger.error(error);

                            process.exit(2);

                        };

                    if (options.json === true) {

                        createJsonStringifyStream(stats.toJson(statsOptions as any))

                            .on("error", handleWriteError)

                            .pipe(process.stdout)

                            .on("error", handleWriteError)

                            .on("close", () => process.stdout.write("\n"));

                    } else if (typeof options.json === "string") {

                        createJsonStringifyStream(stats.toJson(statsOptions as any))

                            .on("error", handleWriteError)

                            .pipe(fs.createWriteStream(options.json))

                            .on("error", handleWriteError)

                            // Use stderr to logging

                            .on("close", () => {

                                process.stderr.write(

                                    `[rspack-cli] ${cli.colors.green(

                                    `stats are successfully stored as json to ${options.json}`

                                   )}\n`

                                );

                              });

                            }

                        } else {

                            const printedStats = stats.toString(statsOptions);

                            // Avoid extra empty line when `stats: 'none'`

                            if (printedStats) {

                                logger.raw(printedStats);

                            }

                    }

                };


                let rspackOptions = { ...options, argv: { ...options } };


                const errorHandler = (err, Stats) => {

                    callback(err, Stats);

                };


                const compiler = await cli.createCompiler(

                    rspackOptions,

                    "build",

                    errorHandler

                );


                if (cli.isWatch(compiler)) {

                    return;

                } else {

                    compiler.run(errorHandler);

                }

            }

        );

    }

}

.usage(<message|command>, [desc], [builder], [handler])

用于标识使用哪一条命令

  • 若传递 desc、builder、handler 时,则 usage 的相当于 command 命令

.scriptName($0)

设置执行的脚本名称,默认取命令执行时的文件名(process.argv[1] 或 electron app 的 process.argv[0] )

.middleware(callbacks, [applyBeforeValidation])

注册命令运行时执行的中间件,callback可以传递一个函数,也可以传递函数数组,在命令执行时这些中间件会顺序执行

  • 通过将第二个参数设置为true 可以在参数被 验证之前,解析之后 来执行
  • callback 接受两个参数:当前解析的选项对象,yargs实例
  • 修改后的argv对象最终将传递给命令的处理函数。
const mwFunc1 = argv => console.log('I\'m a middleware function');
const mwFunc2 = argv => console.log('I\'m another middleware function');

yargs
  .command('myCommand', 'some command', {}, function(argv){
    console.log('Running myCommand!');
  })
  .middleware([mwFunc1, mwFunc2]).argv;

.parseAsync([args], [context], [parseCallback])

.parseSync([args], [context], [parseCallback])

.parsed [DEPRECATED]

解析参数的三种方式

.positional(key, opt)

.positional() 类似于 .option() ,可以配置命令的位置参数。 .positional()应在命令的构建器函数中调用,在顶级作用域的 yargs 实例上不可用。

const argv = require('yargs/yargs')('run --help')
  .command('run <port> <guid>', 'run the server', (yargs) => {
    yargs.positional('guid', {
      describe: 'a unique identifier for the server',
      type: 'string'
    })
  }).argv
console.log(argv)

yargs 库不完全指南

有效的opt参数列表: positional