【文章简介】:你想快速搭建 如 vue-cli、create-vue 那样的炫酷脚手架吗?本文将讲解以下内容:
Commander.JS是一个在NodeJS 环境下便捷地用于构建搞质量命令行工具的库,vue-cli 等很多命令行工具都是由它构建。本文详细讲解Commander.JS的相关概念,并通过案例介绍Commander.JS的使用方法。
inquirer.js是一个实现交互式命令行界面的NodeJS库,文本还介绍了如何通过inquirer.js来实现交互式命令行界面。
此外,本文还介绍了一些其他的小工具,如:
kolorist,在终端中使用色彩;boxen,用于在终端中创建方框;通过阅读本文,能够迅速帮助你掌握搭建脚手架的常用基本工具,让你拥有迅速搭建自己的专属脚手架的基本能力!
注:本文部分内容仍在编辑,预计本月内完工 (202208)
Commander.js是一个在NodeJS 环境下便捷地用于构建搞质量命令行工具的库,vue-cli 等很多命令行工具都是由它构建。inquirer.js 是一个实现交互式命令行界面的 NodeJS 库,通过使用该库能够帮我们实现命令界面的交互式。kolorist 是一个
Commander.js 搭建命令工行工具npm install commander
# 或者:
yarn add commander
const { program } = require('commander');
const { Command } = require('commander');
const program = new Command();
方式1中直接引入 commander 库中暴露(export)的变量 program 是 Command对象的实例,不需要再使用new来创建 Command对象的实例了,但对象名字只能是 program。
方式2中直接导入了 Command 对象,你需要手动创建它的实例:
const program = new Command();
这种方法下当然你可以将 Command 对象命名为其他的合法标识符,而不一定是program。

新建一个 NodeJS 项目,默认初始化项目信息:
npm init -y
commander.jsnpm install commander
建立文件eg1.js,输入以下内容:
// eg1.js
const { Command } = require('commander');
const program = new Command();
program
.name('字符串工具')
.description('一些JavaScript字符串实用程序的 命令行工具')
.version('0.0.1');
program.command('split')
.description('将字符串拆分成子字符串,并显示为数组')
.argument('' , '要拆分的字符串')
.option('--first', '仅显示第一个子字符串')
.option('-s, --separator ' , '分隔符字符', ',')
.action((str, options) => {
const limit = options.first ? 1 : undefined;
console.log(str.split(options.separator, limit));
});
program.parse();
现在在该项目的根目录下打开终端。在终端中测试我们的命令行工具脚本。
node .\eg1.js -V
Out[]:
0.0.1
【评注】:可以看到,这个版本号就是上面代码块第8行.version('0.0.1');所指定的版本号。
node .\eg1.js -h
Out[]:
Usage: 字符串工具 [options] [command]
一些JavaScript字符串实用程序的 命令行工具
Options:
-V, --version output the version number
-h, --help display help for command
Commands:
split [options] 将字符串拆分成子字符串,并显示为数组
help [command] display help for command
【评注】:可以看到,当前你的 CLI 模块一共有两个选项,分别是 -V 和 -h 这是模块级选项,不需要你手动定义,由 commander.js 内置提供。除了你自定义的命令split 外,还有一个名为help的命令,它同样是由 commander.js 内置提供的。
node .\eg1.js split -h
Out[]:
Usage: 字符串工具 split [options]
将字符串拆分成子字符串,并显示为数组
Arguments:
string 要拆分的字符串
Options:
--first 仅显示第一个子字符串
-s, --separator 分隔符字符 (default: ",")
-h, --help display help for command
在脚本中,我们通过 .command(’split‘) 定义了一个名为split的命令。
下面这个例子使用空格 作为分隔符:
node .\eg1.js split --separator=" " "hello, my name is Jack!"
Out[]:
[ 'hello,', 'my', 'name', 'is', 'Jack!' ]
下面这个例子指点逗号(,)作为分隔符:
node .\eg1.js split --separator="," "hello, my name is Jack!"
Out[]:
[ 'hello', ' my name is Jack!' ]
以下是在 powershell 窗口实际运行的截图:

在 3.2 小节中,我们介绍了引入commander的两种方式,默认情况下,使用program作为实例对象的变量名。
在本例(【例1】) 我们使用program构建了我们的第一个命令行工具。可以看到,这个例子中唯一的自定义命令split就是通过program.command()声明的的。
变量 program 是一个 Command 实例,而command()是其下的一个实例方法,仍返回的Command:
Command.command(nameAndArgs: string, opts?: CommandOptions | undefined): Command
其详细的接口描述如下:
command(nameAndArgs: string, opts?: CommandOptions): ReturnType<this['createCommand']>;
/**
* 定义一个命令,在单独的可执行文件中实现。
*
* @remarks
* 命令描述作为第二个参数提供给 `.command`.
*
* @param nameAndArgs - 命令名和参数, args是 `` 或者 `[optional]` ,last也可以是`variadic...`
* @param description - 可执行命令的描述
* @param opts - 配置选项
* @returns 用于链接的 “this” 的 command
*/
例如:
program
.command('start ' , 'start named service')
.command('stop [service]', 'stop named service, or all if no name supplied');
该方法还有一个重载
command(nameAndArgs: string, description: string, opts?: ExecutableCommandOptions): this;
/**
* 创建新的独立 command 的工厂例程。
*
* 有关创建附加子命令的信息,请参见`.command()',该子命令使用此例程来创建命令。您可以覆盖 createCommand 来定制子命令。
*/
通过.name()方法可以用于声明你的命令行工具的版本号,例如在本例中:
program
.name('字符串工具')
这样以后,我们在帮助文档中就可以看到你的命令行工具最开始的
Usage: 字符串工具 [options] [command]
通过.version()方法可以用于声明你的命令行工具的版本号,用户在使用时,通过 3-3-1-4-1 小节 中的方法来来确定自己使用的工具脚本。例如:
program
.version('0.0.1');
表示当前的版本号为0.0.1。
这部分当前正在编辑中
更多功能以及详细的接口语法格式,建议仔细阅读接口声明文件。在本章节中,给出了主要功能类的中文接口声明。
Command 类 接口声明export class Command {
args: string[];
processedArgs: any[];
commands: Command[];
parent: Command | null;
constructor(name?: string);
/**
* 将程序版本设置为 `str`
*
* 该方法自动注册 "-V, --version" 标志,当传递时,该标志将打印版本号。
*
* 您可以选择提供标志和描述来覆盖默认值。
*/
version(str: string, flags?: string, description?: string): this;
/**
* 定义一个命令,使用 action 处理程序实现。
*
* @remarks
* 命令描述是使用 `.description` 提供的,而不是作为 `.command` 的参数。
*
* @example
* ```ts
* program
* .command('clone [destination]')
* .description('clone a repository into a newly created directory')
* .action((source, destination) => {
* console.log('clone command called');
* });
* ```
*
* @param nameAndArgs - 命令名和参数,args 是 `` 或者 `[optional]` 和 last 也可以是 `variadic...`
* @param opts - 配置选项
* @returns new command
*/
command(nameAndArgs: string, opts?: CommandOptions): ReturnType<this['createCommand']>;
/**
* 定义一个命令,在单独的可执行文件中实现。
*
* @remarks
* 命令描述作为第二个参数提供给 `.command`。
*
* @example
* ```ts
* program
* .command('start ', 'start named service')
* .command('stop [service]', 'stop named service, or all if no name supplied');
* ```
*
* @param nameAndArgs - 命令名和参数,args 是 `` 或者 `[optional]` 和 last 也可以是 `variadic...`
* @param description - 可执行命令的描述
* @param opts - 配置选项
* @returns 用于链接的 `this` 命令
*/
command(nameAndArgs: string, description: string, opts?: ExecutableCommandOptions): this;
/**
* 创建新的独立命令的工厂例程。
*
* 有关创建附加子命令的信息,请参见`.command()`,该子命令使用此例程来创建命令。您可以覆盖createCommand来定制子命令。
*/
createCommand(name?: string): Command;
/**
* 添加准备好的子命令。
*
* 有关创建从其父命令继承设置的附加子命令,请参见 `.command()`。
*
* @returns 用于链接的 `this` 命令
*/
addCommand(cmd: Command, opts?: CommandOptions): this;
/**
* 创建新的独立参数的工厂例程。
*
* 有关创建附加参数的信息,请参见`.argument()`,它使用此例程来创建参数。您可以重写 createArgument 以返回自定义参数。
*/
createArgument(name: string, description?: string): Argument;
/**
* 定义命令的参数语法。
*
* 默认情况下,参数是必需的,您可以在名称前后使用<>来明确指出这一点。
* 在可选参数的名称两边加上[]。
*
* @example
* ```
* program.argument('');
* program.argument('[output-file]');
* ```
*
* @returns 用于链接的 `this` 命令
*/
argument<T>(flags: string, description: string, fn: (value: string, previous: T) => T, defaultValue?: T): this;
argument(name: string, description?: string, defaultValue?: unknown): this;
/**
* 定义命令的参数语法,添加准备好的参数。
*
* @returns 用于链接的 `this` 命令
*/
addArgument(arg: Argument): this;
/**
* 定义命令的参数语法,一次添加多个(不带描述)。
*
* See also .argument().
*
* @example
* ```
* program.arguments(' [env]');
* ```
*
* @returns 用于链接的 `this` 命令
*/
arguments(names: string): this;
/**
* 覆盖是否添加隐式帮助命令的默认决定。
*
* @example
* ```
* addHelpCommand() // 强制打开
* addHelpCommand(false); // 强制关闭
* addHelpCommand('help [cmd]', 'display help for [cmd]'); // 强制使用自定义细节
* ```
*
* @returns 用于链接的 `this` 命令
*/
addHelpCommand(enableOrNameAndArgs?: string | boolean, description?: string): this;
/**
* 为生命周期事件添加挂钩。
*/
hook(event: HookEvent, listener: (thisCommand: Command, actionCommand: Command) => void | Promise<void>): this;
/**
* 注册回调以替换调用 process.exit。
*/
exitOverride(callback?: (err: CommanderError) => never | void): this;
/**
* 显示错误信息并退出(或调用exitOverride)。
*/
error(message: string, errorOptions?: ErrorOptions): never;
/**
* 您可以通过覆盖 createhelp 或使用 “configureHelp()” 覆盖帮助属性,用Help的子类自定义帮助。
*/
createHelp(): Help;
/**
* 您可以通过使用configureHelp()覆盖帮助属性来自定义帮助,或者通过覆盖createHelp()使用帮助的子类。
*/
configureHelp(configuration: HelpConfiguration): this;
/** 获取配置 */
configureHelp(): HelpConfiguration;
/**
* 默认输出到stdout和stderr。您可以为特殊应用定制此功能。
* 您还可以通过覆盖outputError来自定义错误的显示。
*
* 配置属性都是函数:
* ```
* // 用于更改写入位置的函数,stdout 和 stderr
* writeOut(str)
* writeErr(str)
* // 用于指定帮助换行宽度的匹配函数
* getOutHelpWidth()
* getErrHelpWidth()
* // functions based on what is being written out
* outputError(str, write) // 用于显示错误,不用于显示帮助
* ```
*/
configureOutput(configuration: OutputConfiguration): this;
/** 获取配置 */
configureOutput(): OutputConfiguration;
/**
* 复制在 根命令 和 子命令 之间通用的有用设置。
*
* (使用 `.command()' 添加命令时在内部使用,以便子命令继承父设置。)
*/
copyInheritedSettings(sourceCommand: Command): this;
/**
* 出现错误后显示帮助或自定义消息。
*/
showHelpAfterError(displayHelp?: boolean | string): this;
/**
* 为未知命令显示类似命令的建议,或为未知选项显示选项。
*/
showSuggestionAfterError(displaySuggestion?: boolean): this;
/**
* 为命令注册回调 `fn`。
*
* @example
* ```
* program
* .command('serve')
* .description('start service')
* .action(function() {
* // do work here
* });
* ```
*
* @returns 用于链接的 `this` 命令
*/
action(fn: (...args: any[]) => void | Promise<void>): this;
/**
* 用 `flags`、`description`和可选强制`fn`定义选项。
*
* `flags` 字符串包含短标志和/或长标志,由逗号、竖线或空格分隔。当使用 `-help' 时,以下都是有效的输出。
*
* "-p, --pepper"
* "-p|--pepper"
* "-p --pepper"
*
* @example
* ```
* // 简单boolean默认为false
* program.option('-p, --pepper', 'add pepper');
*
* --pepper
* program.pepper
* // => Boolean
*
* // 简单boolean默认为false
* program.option('-C, --no-cheese', 'remove cheese');
*
* program.cheese
* // => true
*
* --no-cheese
* program.cheese
* // => false
*
* // 必需的参数
* program.option('-C, --chdir ', 'change the working directory');
*
* --chdir /tmp
* program.chdir
* // => "/tmp"
*
* // 可选参数
* program.option('-c, --cheese [type]', 'add cheese [marble]');
* ```
*
* @returns 用于链接的 `this` 命令
*/
option(flags: string, description?: string, defaultValue?: string | boolean | string[]): this;
option<T>(flags: string, description: string, fn: (value: string, previous: T) => T, defaultValue?: T): this;
/** @deprecated 从v7开始,改为使用 `choices` 或自定义功能 */
option(flags: string, description: string, regexp: RegExp, defaultValue?: string | boolean | string[]): this;
/**
* 定义一个必需的选项,该选项在解析后必须有一个值。这通常意味着必须在命令行上指定该选项。 (否则与 `.option()` 相同。)
*
* `flags` 字符串包含 短标志 和/或 长标志,由逗号、竖线或空格分隔。
*/
requiredOption(flags: string, description?: string, defaultValue?: string | boolean | string[]): this;
requiredOption<T>(flags: string, description: string, fn: (value: string, previous: T) => T, defaultValue?: T): this;
/** @deprecated 从v7开始,改为使用 `choices` 或自定义功能 */
requiredOption(flags: string, description: string, regexp: RegExp, defaultValue?: string | boolean | string[]): this;
/**
* 创建新的独立选项的工厂例程。
*
* 有关创建附加选项的信息,请参见 `.option()`,它使用此例程来创建选项。您可以重写 createOption 以返回自定义选项。
*/
createOption(flags: string, description?: string): Option;
/**
* 添加一个准备好的选项。
*
* 有关在单个调用中创建和附加选项的信息,请参见 `.option()` 和 `.requiredOption()`。
*/
addOption(option: Option): this;
/**
* 是将选项值作为属性存储在命令对象上,还是单独存储(指定false)。在这两种情况下,都可以使用 `.opts()` 来访问选项值。
*
* @returns 用于链接的 `this` 命令
*/
storeOptionsAsProperties<T extends OptionValues>(): this & T;
storeOptionsAsProperties<T extends OptionValues>(storeAsProperties: true): this & T;
storeOptionsAsProperties(storeAsProperties?: boolean): this;
/**
* 检索选项值。
*/
getOptionValue(key: string): any;
/**
* 存储选项值。
*/
setOptionValue(key: string, value: unknown): this;
/**
* 存储选项值以及该值的来源。
*/
setOptionValueWithSource(key: string, value: unknown, source: OptionValueSource): this;
/**
* 检索选项值源。
*/
getOptionValueSource(key: string): OptionValueSource;
/**
* 用可选值改变短标志的解析。
*
* @example
* ```
* // for `.option('-f,--flag [value]'):
* .combineFlagAndOptionalValue(true) // `-f80` 被视为 `--flag=80`, 这是默认行为
* .combineFlagAndOptionalValue(false) // `-fb` 被视为 `-f -b`
* ```
*
* @returns 用于链接的 `this` 命令
*/
combineFlagAndOptionalValue(combine?: boolean): this;
/**
* 允许命令行上的未知选项。
*
* @returns 用于链接的 `this` 命令
*/
allowUnknownOption(allowUnknown?: boolean): this;
/**
* 允许命令行上有过多的命令参数。传递false使多余的参数成为错误。
*
* @returns 用于链接的 `this` 命令
*/
allowExcessArguments(allowExcess?: boolean): this;
/**
* 启用位置选项。位置意味着在子命令之前指定全局选项,这允许子命令重用相同的选项名,也允许子命令打开 passThroughOptions 。
*
* 默认行为是非定位的,全局选项可能出现在命令行的任何地方。
*
* @returns 用于链接的 `this` 命令
*/
enablePositionalOptions(positional?: boolean): this;
/**
* 传递命令参数之后的选项,而不是将它们视为命令选项,
* 因此实际的命令选项在命令参数之前。
* 为子命令启用此选项需要在程序(父命令)上启用位置选项。
*
* 默认行为是非定位的,选项可能出现在命令参数之前或之后。
*
* @returns 用于链接的 `this` 命令
*/
passThroughOptions(passThrough?: boolean): this;
/**
* 解析 `argv`,设置选项并在定义时调用命令。
*
* 默认情况下,参数来自 node,应用程序为 argv[0],脚本在 argv[1] 中运行,其后是用户参数。
*
* @example
* ```
* program.parse(process.argv);
* program.parse(); // 隐式使用 process.argv 和自动检测 node vs electron conventions
* program.parse(my-args, { from: 'user' }); // 只是用户提供的参数,argv[0]没有什么特别的
* ```
*
* @returns 用于链接的 `this` 命令
*/
parse(argv?: readonly string[], options?: ParseOptions): this;
/**
* 解析 `argv`,设置选项并在定义时调用命令。
*
* 如果您的任何操作处理程序是异步的,请使用 parseAsync 而不是 parse。 返回一个 Promise.
*
* 默认情况下,参数来自 node,应用程序为 argv[0],脚本在 argv[1] 中运行,其后是用户参数。
*
* @example
* ```
* program.parseAsync(process.argv);
* program.parseAsync(); // 隐式使用 process.argv 和自动检测 node vs electron conventions
* program.parseAsync(my-args, { from: 'user' }); // 只是用户提供的参数,argv[0]没有什么特别的
* ```
*
* @returns Promise
*/
parseAsync(argv?: readonly string[], options?: ParseOptions): Promise<this>;
/**
* 从 `argv` 中解析选项,删除已知选项,并返回argv拆分为操作数和未知参数。
*
* argv => operands, unknown
* --known kkk op => [op], []
* op --known kkk => [op], []
* sub --unknown uuu op => [sub], [--unknown uuu op]
* sub -- --unknown uuu op => [sub --unknown uuu op], []
*/
parseOptions(argv: string[]): ParseOptionsResult;
/**
* 返回一个包含作为键值对的本地选项值的对象
*/
opts<T extends OptionValues>(): T;
/**
* 返回一个对象,该对象包含作为键值对的合并的本地和全局选项值。
*/
optsWithGlobals<T extends OptionValues>(): T;
/**
* 设置描述。
*
* @returns 用于链接的 `this` 命令
*/
description(str: string): this;
/** @deprecated 从v8开始,改为使用 .argument 来添加带有描述的命令参数 */
description(str: string, argsDescription: {[argName: string]: string}): this;
/**
* 获取描述。
*/
description(): string;
/**
* 设置摘要。当作为父命令的子命令列出时使用。
*
* @returns 用于链接的 `this` 命令
*/
summary(str: string): this;
/**
* 获取摘要。
*/
summary(): string;
/**
* 为命令设置别名。
*
* 您可以多次调用以添加多个别名。自动生成的帮助中只显示第一个别名。
*
* @returns 用于链接的 `this` 命令
*/
alias(alias: string): this;
/**
* 获取命令的别名。
*/
alias(): string;
/**
* 为命令设置别名。
*
* 自动生成的帮助中只显示第一个别名。
*
* @returns 用于链接的 `this` 命令
*/
aliases(aliases: readonly string[]): this;
/**
* 获取命令的别名。
*/
aliases(): string[];
/**
* 设置命令用法。
*
* @returns 用于链接的 `this` 命令
*/
usage(str: string): this;
/**
* 获取命令用法。
*/
usage(): string;
/**
* 设置命令的名称。
*
* @returns 用于链接的 `this` 命令
*/
name(str: string): this;
/**
* 获取命令的名称。
*/
name(): string;
/**
* 从脚本文件名设置命令的名称,例如 process.argv[1]、require.main.filename 或 __filename。
*
* (虽然自述文件中没有记录,但在内部和公共场合使用。)
*
* @example
* ```ts
* program.nameFromFilename(require.main.filename);
* ```
*
* @returns 用于链接的 `this` 命令
*/
nameFromFilename(filename: string): this;
/**
* 设置搜索此命令的可执行子命令的目录。
*
* @example
* ```ts
* program.executableDir(__dirname);
* // 或者
* program.executableDir('subcommands');
* ```
*
* @returns 用于链接的 `this` 命令
*/
executableDir(path: string): this;
/**
* 获取可执行文件搜索目录。
*/
executableDir(): string;
/**
* 输出此命令的帮助信息。
*
* 输出内置帮助和使用 `.addHelpText()` 添加的自定义文本。
*
*/
outputHelp(context?: HelpContext): void;
/** @deprecated since v7 */
outputHelp(cb?: (str: string) => string): void;
/**
* 返回命令帮助文档。
*/
helpInformation(context?: HelpContext): string;
/**
* 您可以传入标志和描述来覆盖命令的帮助标志和帮助描述。
* 传入false以禁用内置帮助选项。
*/
helpOption(flags?: string | boolean, description?: string): this;
/**
* 输出帮助信息并退出。
*
* 输出内置帮助和使用 `.addHelpText()` 添加的自定义文本。
*/
help(context?: HelpContext): never;
/** @deprecated since v7 */
help(cb?: (str: string) => string): never;
/**
* 添加要与内置帮助一起显示的附加文本。
*
* Position为 `before` 或 `after` 时仅影响该命令,而 `beforeAll` 或 `afterAll` 时影响该命令及其所有子命令。
*/
addHelpText(position: AddHelpTextPosition, text: string): this;
addHelpText(position: AddHelpTextPosition, text: (context: AddHelpTextContext) => string): this;
/**
* 添加事件发生时的侦听器(回调)。(使用EventEmitter实现。)
*/
on(event: string | symbol, listener: (...args: any[]) => void): this;
}
inquirer.js 构建交互式命令界面当你使用 vue-cli (现在以被create-vue取代,但仍使用 inquirer )等脚手架创建 vue 项目时,一定见到过很炫酷的交互式问答,以让脚手架了解到你的项目需求,比如是否需要使用 vue-router,vuex(已被pinia替代),是否使用 CSS 预处理器(如less、sass),等等。这样的交互式问答功能就是由 使用 inquirer.js 所提供支持的。
当你自己搭建脚手架项目时,如果你的脚手架也要动态地了解用户本次执行的需求,那么使用 inquirer.js 是相当快捷、易于上手的选择。本章的任务就是学习和掌握如何使用 inquirer.js 来构造我们自己的一个交互式命令界面。
npm install inquirer
# 或者
yarn add inquirer
在项目下安装依赖:
npm install --save inquirer@^8.0.0
新建文件 eg3.js (如果你喜欢也可以用其它文件名),并添加一下内容:
// eg3.js
const inquirer = require('inquirer');
console.log('你好,欢迎体验 inquirer 命令行交互界面!');
console.log('下面我们将问你一些问题。。。');
const questions = [
{
type: 'confirm',
name: 'toBeDelivered',
message: '今天天气好吗?(Y:好,N:不好)',
default: true,
},
{
type: 'rawlist',
name: 'select_number',
message: '您是先生还是女士?(Y:先生,N:女士)',
choices: ['先生', '女士'],
},
{
type: 'input',
name: 'CSDN_name',
message: "您的CSDN用户名是?",
},
{
type: 'input',
name: 'phone',
message: "您的手机号码是?",
validate(value) {
const pass = value.match(
/^([01]{1})?[-.\s]?\(?(\d{3})\)?[-.\s]?(\d{3})[-.\s]?(\d{4})\s?((?:#|ext\.?\s?|x\.?\s?){1}(?:\d+)?)?$/i
);
if (pass) {
return true;
}
return '请输入有效的电话号码!!';
},
},
{
type: 'list',
name: 'fv_food',
message: '您喜欢的食物类型是?',
choices: ['甜食', '辛辣', '烧烤', '我不挑食'],
},
{
type: 'expand',
name: 'select_key',
message: '请依据 key 指定一个选项:',
choices: [
{
key: 'a',
name: '选项1',
value: 'A',
},
{
key: 'b',
name: '选项2',
value: 'B',
},
{
key: 'c',
name: '选项3',
value: 'C',
},
],
},
{
type: 'rawlist',
name: 'select_number',
message: '选择一个项目对应的编号',
choices: ['第一', '第二', '第三'],
},
{
type: 'input',
name: '评论',
message: '输入一段话',
default: '这是默认输入的一段话。',
},
];
inquirer.prompt(questions).then((answers) => {
console.log('\n本次对话交互的信息如下:');
console.log(JSON.stringify(answers, null, ' '));
});
运行效果如下:

Inquirer v9 及更高版本是本机 esm 模块,这意味着您不能再使用 commonjs 语法require(‘inquirer’)。如果像本例中一样,使用传统 commonjs 语法,则需要像上面这样安装 Inquirer v8,否则需要使用import inquirer from 'inquirer'的写法。比如本例使用:
npm install --save inquirer@^8.0.0
# 或者
yarn add inquirer@^8.0.0
则对应使用 commonjs 语法导入 inquirer:
const inquirer = require('inquirer');
而安装 Inquirer v9 之后的版本则可以直接安装,就像靠头那样:
npm install inquirer
# 或者
yarn add inquirer
导入语法也对应换成 ES module的语法:
import inquirer from 'inquirer'
question 对象启动提示界面(查询会话)
inquirer.prompt(questions, answers) -> promise
例如:
{
type: 'list',
name: 'fv_food',
message: '您喜欢的食物类型是?',
choices: ['甜食', '辛辣', '烧烤', '我不挑食'],
},
以下是一个使用了 checkbox 类型的完整示范:
const inquirer = require('inquirer');
inquirer
.prompt([
{
type: 'checkbox',
message: '选择框',
name: 'ck',
choices: [
{ name: 'option1' },
{ name: 'option2'},
new inquirer.Separator('--- 下面的 option3 指定了默认被选中: ---'),
{
name: 'option3',
checked: true,
},
new inquirer.Separator('--- 下面的 option4 是一个被禁用的选项: ---'),
{
name: 'option4',
disabled: '禁用的选项',
},
{ name: 'option5'},
],
validate(answer) {
if (answer.length < 1) {
return '您必须选择至少一个选项。';
}
return true;
},
},
])
.then((answers) => {
console.log("以下是你的选择结果:");
console.log(JSON.stringify(answers, null, ' '));
});
其效果如下:

例如:
{
type: 'confirm',
name: 'toBeDelivered',
message: '您的心情怎么样?(Y:好,N:不好)',
default: true,
},
例如:
以下是引导脚手架用户输入手机号码,并在手机号码输入错误的时候提示“请输入合法的手机号码!”例子:
{
type: 'input',
name: 'phone',
message: "您的手机号码是?",
validate(value) {
const pass = value.match(
/^([01]{1})?[-.\s]?\(?(\d{3})\)?[-.\s]?(\d{3})[-.\s]?(\d{4})\s?((?:#|ext\.?\s?|x\.?\s?){1}(?:\d+)?)?$/i
);
if (pass) {
return true;
}
return '请输入合法的手机号码!';
},
},
以下是另外一个完整的示例:
const inquirer = require('inquirer');
inquirer
.prompt([
{
type: 'list',
name: 'drinks',
message: '😘你想来点什么呢?',
choices: [
'咖啡',
'奶茶'
],
},
{
type: 'list',
name: 'size',
message: '😘我们有以下几种被型,您要哪种呢?',
choices: ['小杯(Small)', '中杯(Middle)', '大杯(Large)', '特大杯(Super Large)'],
filter(val) {
return val.toLowerCase();
},
},
])
.then((answers) => {
console.log(`您要的是${answers.size}的${answers.drinks}!`);
});
其运行效果如下:

例如:
例如:
const inquirer = require('inquirer');
const requireLetterAndNumber = (value) => {
if (/\w/.test(value) && /\d/.test(value)) {
return true;
}
return '错误:密码至少需要有一个字母和一个数字';
};
inquirer
.prompt([
{
type: 'password',
message: '这是不显示任何字符的密码输入形式 - 请输入密码:',
name: 'psw1',
validate: requireLetterAndNumber,
},
{
type: 'password',
message: '这是使用屏蔽符显示密码输入形式 - 请输入密码:',
name: 'psw2',
mask: '*',
validate: requireLetterAndNumber,
},
])
.then((answers) => {
console.log(`完全不显示输入的密码是:${answers.psw1},显示为屏蔽符输入的密码是:${answers.psw2}`);
});
其效果如下:

例如:
kolorist 在终端中使用色彩npm install --save-dev kolorist
# 或者
yarn add kolorist
boxen 在终端中创建方框npm install boxen
# 或者
yarn add boxen