15.4    Tools工具

Node.js has also established itself as a valuable tool on the command line. Node.js也已经成为命令行上一个有价值的工具。For this reason, you'll find ready-made solutions for numerous problems in the area of command-line applications, which you can install as packages in your application. 出于这个原因,您将为命令行应用程序领域的许多问题找到现成的解决方案,您可以将其作为包安装在应用程序中。In the following sections, you'll be introduced to three of these tools—Commander, chalk, and node-emoji—and integrate them into your Math Trainer.在下面的部分中,您将了解其中的三个工具:Commander、chalk和node-emoji,并将它们集成到数学培训器中。

15.4.1    Commander指挥官

As you've seen in the previous section, searching the command line for specific options involves a certain amount of work. 正如您在上一节中所看到的,在命令行中搜索特定选项需要一定的工作量。The situation gets even more inconvenient at this point if you also want to provide the shorthand notation of options instead of what we've done so far in the example. 如果您还想提供选项的简写符号,而不是我们在示例中迄今为止所做的,那么在这一点上,情况会变得更加不方便。In this case, you normally don't use equal signs as separators between the option and the value, so you also have to adjust the routine here. 在这种情况下,您通常不使用等号作为选项和值之间的分隔符,因此您还必须调整此处的例程。For parsing the command line, you can include Commander in your application. 为了解析命令行,您可以在应用程序中包含Commander。You can install the package via the npm install commander command. Because you've already swapped out the parsing of the command line to a separate file in the previous step, the adjustments for integrating Commander are limited to the lib/getOptions.js file. 您可以通过npm install commander命令安装程序包。因为您已经在上一步中将命令行的解析换成了一个单独的文件,所以集成Commander的调整仅限于lib/getOptions.js文件。The updated version of this file is shown in Listing 15.20.该文件的更新版本如清单15.20所示。

importprogramfrom'commander';

exportdefault(levelDefault=2,amountDefault=4)=>{
program
.version('1.0.0')
.option(
'-l, --level <n>',
'Difficulty level of tasks (1-3)',
parseInt,
levelDefault,
)
.option('-a, --amount <n>','Number of tasks',parseInt,inline image
amountDefault)
.parse(process.argv);

constoptions=program.opts();

return{
level:options.level,
amount:options.amount,
};
};

Listing 15.20     Integrating Commander集成指挥官 (lib/getOptions.js)

Due to the customization in Listing 15.20, the source code of your application has become simpler, and you also gained additional features. 由于清单15.20中的自定义,您的应用程序的源代码变得更简单,而且您还获得了其他功能。Thus, by default, Commander supports the -V and --version options to display the version of the application. 因此,默认情况下,Commander支持-V--version选项来显示应用程序的版本。In addition, when the application is invoked with the -h or --help option, a help block describing how to use the command is displayed.此外,当使用-h--help选项调用应用程序时,会显示一个帮助块,描述如何使用该命令。

After these changes, you no longer support only the long version of the options, but also a shortened version. 在这些更改之后,您不再只支持选项的长版本,而是支持缩短版本。If you call your application with the -h option, you'll see an output like the one shown in Listing 15.21.如果您使用-h选项调用应用程序,您将看到类似于清单15.21中所示的输出。

$ math-trainer -h

Usage: math-trainer [options]

Options:

-V, --version output the version number
-l, --level <n> difficulty level of tasks (1-3) (default: 2)
-a, --amount <n> number of tasks (default: 4)
-h, --help output usage information

Listing 15.21     Display of the Math Trainer Help显示数学培训师帮助

Using the option method of the Commander package, you can define the individual options of your application. 使用Commander包的选项方法,可以定义应用程序的各个optionAs the first argument, the method expects the name of the option. 作为第一个参数,该方法需要选项的名称。Here you can specify both the short and the long variant. 在这里,您可以指定短变体和长变体。If a value is to be passed to the application via the option, you can specify it afterwards. 如果要通过选项将值传递给应用程序,则可以在之后指定该值。You have two different options for specifying the value: If you put the value in angle brackets, as in this example, it's a mandatory value. 您有两个不同的选项来指定值:如果将值放在尖括号中,如本例所示,则它是一个强制值。If you want to define an optional option, you can use square brackets here. 如果您想定义一个可选选项,可以在此处使用方括号。The second parameter of the options method represents the description of the option. options方法的第二个参数表示选项的描述。This is displayed in the help menu. 这将显示在帮助菜单中。As a third argument, you can pass a function to manipulate the value. 作为第三个参数,您可以传递一个函数来操作该值。For the Math Trainer application, use the parseInt function to convert the passed value into a number. 对于Math Trainer应用程序,使用parseInt函数将传递的值转换为数字。The last parameter allows you to pass a default value for the option. 最后一个参数允许您传递该选项的默认值。Commander then automatically inserts the string default: <value> into the option description.然后,Commander会自动将字符串default: <value>插入到选项描述中。

For Commander to work, you must use the parse method to specify which data structure to evaluate. 为了使Commander工作,您必须使用parse方法来指定要评估的数据结构。In most cases, this will be the process.argv array, but here you have the option to specify any array that follows the rules of process.argv.在大多数情况下,这将是process.argv数组,但在这里您可以选择指定任何遵循process.argv规则的数组。

All methods of the Commander object return the object itself, so that a fluent interface notation becomes possible, and you can directly concatenate the method calls.Commander对象的所有方法都返回对象本身,因此可以使用流畅的接口表示法,并且可以直接连接方法调用。

You can obtain the values that were passed when the application was called by using the opts method. 您可以使用opts方法获取调用应用程序时传递的值。This method returns an object containing the individual options and their associated values as key-value pairs.此方法返回一个对象,该对象包含作为键值对的各个选项及其关联值。

The Commander project site can be found at https://github.com/tj/commander.js. 指挥官项目现场可在https://github.com/tj/commander.js上找到。A lightweight alternative to Commander.js is available in the form of minimist. Commander.js的一个轻量级替代方案是以minimist的形式提供的。This module deals only with the correct parsing of command-line options. 这个模块只处理命令行选项的正确解析。This project can be found at https://github.com/substack/minimist.此项目可在https://github.com/substack/minimist上找到。

15.4.2    Chalk

A feature that is often underestimated is the formatting of the command line. 一个经常被低估的功能是命令行的格式。It allows you to highlight important terms and thus guide the user on the command line. 它允许您突出显示重要术语,从而在命令行上为用户提供指导。You can apply colors and other formatting using control characters directly in console.log, for example. 例如,您可以直接在console.log中使用控制字符应用颜色和其他格式。Listing 15.22 shows how this works.清单15.22显示了这是如何工作的。

console.log('\u001b[33m yellow');
console.log('\u001b[31m red');
console.log('\u001b[34m blue');
console.log('\u001b[0m');

Listing 15.22     Coloring the Console为控制台着色

The output of this example consists of the character strings yellow, red, and blue, each colored correspondingly. 这个例子的输出由yellowredblue的字符串组成,每个字符串都有相应的颜色。The last line resets the color of the console back to its original state. 最后一行将控制台的颜色重置回其原始状态。The string \u001b[4m allows you to underline the subsequent characters. 字符串\u001b[4m允许您在后面的字符中加下划线。Other features include italic, strikethrough, and bold font. 其他功能包括斜体、删除线和粗体。You can also change the background color of the console. 您也可以更改控制台的背景颜色。Admittedly, dealing with ANSI control characters in development isn't always convenient. 诚然,在开发过程中处理ANSI控制字符并不总是很方便。Applying styles on the console is such a common problem that a module called chalk comprehensively solves this problem for you. 在控制台上应用样式是一个常见的问题,因此一个名为chalk的模块可以为您全面解决这个问题。It can be installed with the npm using the npm install chalk command. 它可以使用npm install chalk命令与npm一起安装。The code you implemented in Listing 15.22 via control characters can be implemented more elegantly with chalk using meaningful function names. 您在清单15.22中通过控制字符实现的代码可以使用有意义的函数名用粉笔更优雅地实现。The result shown in Listing 15.23 is the same as the one from the previous example.清单15.23中显示的结果与上一个示例中的结果相同。

importchalkfrom'chalk';

console.log(chalk.yellow('yellow'));
console.log(chalk.red('red'));
console.log(chalk.blue('blue'));

Listing 15.23     Using Chalk使用粉笔

In your Math Trainer application, chalk enables you to format the output of the result in bold and green for success, and bold and red for failure. 在Math Trainer应用程序中,粉笔可以将结果的输出格式设置为粗体和绿色表示成功,粗体和红色表示失败。For this purpose, you must modify the lib/index.js file after installing the package, as shown in Listing 15.24.为此,您必须在安装包后修改lib/index.js文件,如清单15.24所示。

import{createInterface}from'readline';
importchalkfrom'chalk';
importcreateTaskfrom'./task.js';
importpromisedQuestionfrom'./promisedQuestion.js';
importgetOptionsfrom'./getOptions.js';

const{ amount, level }=getOptions();

constoperations=['+','-','*','/'];
consttasks=[];

operations.forEach((operation) =>{
for(leti=0; i<amount; i++) {
tasks.push(createTask(operation, level));
}
});

constrl=createInterface({
input: process.stdin,
output: process.stdout,
});

asyncfunctionquestion(index) {
constresult=awaitpromisedQuestion(`${tasks[index].task} = `, rl);
tasks[index].input =parseInt(result);
if(tasks[index].input===tasks[index].result) {
console.log(chalk.bold.green('Correct!'));
} else{
console.log(chalk.bold.red('Wrong'));
}
if(++index<tasks.length) {
question(index);
} else{
rl.close();
}
}

question(0);

Listing 15.24     Using Chalk in Math Trainer在数学教练中使用粉笔 (lib/index.js)

As you can see in Listing 15.24, it's possible to apply several styles at the same time by concatenating the statements. 正如您在清单15.24中看到的那样,通过连接语句可以同时应用多种样式。Another convenient feature of chalk is that it also takes care of resetting the formatting to the console default style for you after the passed character string has been formatted.粉笔的另一个方便功能是,它还负责在传递的字符串格式化后,将格式重置为控制台默认样式。

The chalk project can be found on GitHub at https://github.com/chalk/chalk.粉笔项目可以在GitHub上找到,位于https://github.com/chalk/chalk

15.4.3    node-emoji

One of the most popular tools that uses emojis for console output is the package manager Yarn. 使用表情符号进行控制台输出的最流行的工具之一是包管理器Yarn。Like chalk, emojis can be used to direct the user's attention to a particular output on the console, to make the console clearer because certain states can be expressed more quickly via an emoji than via text, and, finally, to liven up your application a bit by using the right emojis in the appropriate places. 与粉笔一样,表情符号可以用于将用户的注意力引向控制台上的特定输出,使控制台更加清晰,因为某些状态可以通过表情符号比通过文本更快地表达,最后,通过在适当的位置使用正确的表情符号,使应用程序更加生动。Because JavaScript supports the Unicode character set, it's possible to use Unicode emojis directly. 因为JavaScript支持Unicode字符集,所以可以直接使用Unicode表情符号。An alternative to this is to use the node-emoji package, which allows you to use the text representation of various emojis, making your code more readable.另一种方法是使用node-emoji包,它允许您使用各种表情符号的文本表示,从而使代码更具可读性。

Your implementation of the Math Trainer application is currently missing a summary of the results. 您的数学培训师应用程序的实现目前缺少结果摘要。To implement these in the lib/summary.js file, you can use the node-emoji package. 要在lib/summary.js文件中实现这些功能,可以使用node-emoji包。In the first step, you install this package via the npm install node-emoji command. 在第一步中,通过npm install node-emoji命令安装此包。The source code in the lib/summary.js file is shown in Listing 15.25.lib/summary.js文件中的源代码如清单15.25所示。

importemojifrom'node-emoji';

exportdefault(tasks)=>{
constcorrectCount=tasks.reduce((correctCount, task)=>{
if(task.input===task.result) {
correctCount++;
}
returncorrectCount;
}, 0);
constpercent=(correctCount*100)/tasks.length;
if(percent===100) {
returnemoji.emojify(
`:trophy: Congratulations, you have solved all ${tasks.length} tasks
inline image
correctly.`,

);

} elseif(percent>=50) {
returnemoji.emojify(
`:sunglasses: Very good, you have correctly solved ${correctCount} out of
inline image
${tasks.length} tasks.`,

);

} elseif(percent>=1) {
returnemoji.emojify(
`:cry: You have correctly solved ${correctCount} out of ${tasks.length} tasks,
inline image
you can do better.`,

);

} else{
returnemoji.emojify(
`:skull_and_crossbones:
inline image
Your answers to all ${tasks.length} tasks are wrong.`,

);

}
};

Listing 15.25     Preparation of the Results Summary编制结果摘要 (lib/summary.js)

To display the result, you divide it into four categories: The user solved all tasks correctly, the user solved more than 50% correctly, the user solved less than 50% correctly, and the user didn't solve any task correctly. 为了显示结果,您将其分为四类:用户正确地解决了所有任务,用户正确地完成了50%以上,用户正确解决了50%以下,以及用户没有正确地解决任何任务。You can use the emoji.emojify method to generate a character string that contains an emoji. 可以使用emoji.emojify方法生成包含表情符号的字符串。This method should be marked with colons. 这个方法应该用冒号标记。The emoji.emojify(':trophy:') call creates a character string containing the trophy emoji. emoji.emojify(':trophy:')调用创建包含奖杯表情符号的字符串。In the last step you need to display the generated character string. 在最后一步中,您需要显示生成的字符串。You can do this by calling the helper function for summaries before you exit the process. 您可以通过在退出流程之前为摘要调用helper函数来实现这一点。Listing 15.26 contains the customized source code of the lib/index.js file.清单15.26包含lib/index.js文件的自定义源代码。

import{createInterface}from'readline';
importchalkfrom'chalk';
importcreateTaskfrom'./task.js';
importpromisedQuestionfrom'./promisedQuestion.js';
importgetOptionsfrom'./getOptions.js';
importsummaryfrom'./summary.js';

const{ amount, level }=getOptions();

constoperations=['+','-','*','/'];
consttasks=[];

operations.forEach((operation) =>{
for(leti=0; i<amount; i++) {
tasks.push(createTask(operation, level));
}
});

constrl=createInterface({
input: process.stdin,
output: process.stdout,
});

asyncfunctionquestion(index) {
constresult=awaitpromisedQuestion(`${tasks[index].task} = `, rl);
tasks[index].input =parseInt(result);
if(tasks[index].input===tasks[index].result) {
console.log(chalk.bold.green('Correct!'));
} else{
console.log(chalk.bold.red('Wrong'));
}
if(++index<tasks.length) {
question(index);
} else{
console.log(summary(tasks));
rl.close();
}
}

question(0);

Listing 15.26     Summary Display摘要显示 (lib/index.js)

In addition to formatting strings, the node-emoji package is also capable of resolving emojis in character strings or assigning them a random emoji. 除了格式化字符串外,node-emoji包还能够解析字符串中的表情符号,或者为其分配一个随机的表情符号。You can find the project site at https://github.com/omnidan/node-emoji.您可以在以下位置找到项目网站:https://github.com/omnidan/node-emoji