admin管理员组

文章数量:1130349

node cli

Node.js CLI现在已成为IT生态系统不可或缺的一部分 (Node.js CLIs are now an integral part of the IT ecosystem)

Command Line Interfaces (CLIs) are widely used by developers to facilitate the app development process. In the JavaScript ecosystem alone, that could be a compiling process (TypeScript to JavaScript, Webpack, Babel, etc), a package utility (creating a new NPM project, managing Git) or connecting to remote hosts. This almost endless list goes on for millions of utilities that have been developed for JavaScript based projects alone.

命令行界面(CLI)被开发人员广泛使用,以促进应用程序开发过程。 仅在JavaScript生态系统中,它可能是一个编译过程(JavaScript的TypeScript,Webpack,Babel等),一个包实用程序(创建一个新的NPM项目,管理Git)或连接到远程主机。 仅针对基于JavaScript的项目开发的数百万个实用程序就在不断增加的清单中。

It is not uncommon for a popular CLI based tool on NPM to have millions of weekly downloads, that illustrates just how integral Node.js (and JavaScript in general) has become for development processes. JavaScript undoubtedly plays a huge role for CLIs at various stages of the development pipeline, in the form of Node.js runtime programs.

流行的基于NPM的基于CLI的工具每周都有数百万次下载的情况并不少见,这说明了Node.js(以及一般JavaScript)对于开发过程已经变得如何不可或缺。 毫无疑问,JavaScript以Node.js运行时程序的形式在开发管道的各个阶段对CLI发挥了巨大作用。

CLI严重依赖于开源依赖项 (CLIs rely heavily on open source dependencies)

A CLI built with Node essentially boils down to effectively using a range of dependencies from the NPM ecosystem. These dependencies perform key tasks in your CLI, such as prompting a user for an input, colouring some output, displaying an app helper (commonly invoked with --help), terminal spinners while requests are processing, etc. It is unlikely that you will need to write a package from scratch to perform a particular task for the command line — the chances are NPM has your use case covered.

使用Node构建的CLI本质上可以归结为有效使用NPM生态系统中的一系列依赖关系。 这些依赖项在CLI中执行关键任务,例如提示用户输入信息,为某些输出着色,显示应用程序助手(通常通过--help调用),正在处理请求的终端微调器等。您不太可能会需要从头开始编写一个程序包以执行命令行的特定任务-NPM可能涵盖了您的用例。

What we do need to focus on however is to effectively organise the CLI source files into modules and define the logic of the program.

但是,我们需要重点关注的是将CLI源文件有效地组织到模块中并定义程序的逻辑。

This article introduces this development process of a Node.js based CLI program designed to authenticate a user from an external server, and provide them with a list of options from a main menu. The full project is available on GitHub for the reader to refer to or build upon:

本文介绍了基于Node.js的CLI程序的开发过程,该程序旨在验证来自外部服务器的用户,并为他们提供主菜单中的选项列表。 完整项目可在GitHub上找到,读者可以参考或建立在以下基础上:

Though simple, this program demonstrates how to handle a range of concepts for a Node based CLI program, including:

尽管很简单,但是该程序演示了如何处理基于Node的CLI程序的一系列概念,包括:

  • Styling output text in various ways, and displaying spinners when asynchronous tasks like contacting the server and waiting for a response are taking place. We’ll explore colorising text, generating ASCII art, and boxing your output.

    以各种方式设置输出文本的样式 ,并在发生异步任务(例如与服务器联系并等待响应)时显示微调框。 我们将研究为文本着色,生成ASCII文字并装箱输出。

  • How to prompt the user for various inputs, and sending those inputs to a server using fetch requests. These inputs range from simple text input, yes / no prompts, and choosing an option from a list of options.

    如何提示用户进行各种输入,以及如何使用获取请求将这些输入发送到服务器。 这些输入范围包括简单文本输入,是/否提示以及从选项列表中选择一个选项。

  • Efficiently persisting data on device as key-value pairs, that will be leveraged to store an authentication token.

    有效地将数据作为键值对持久存储在设备上,这些数据将用于存储身份验证令牌。

  • The ability to stay signed in, so the program will immediately boot to the main menu upon starting it up again. This is done by validating the authentication token before loading the main menu.

    保持登录状态的能力,因此程序再次启动时将立即启动到主菜单。 这是通过在加载主菜单之前验证身份验证令牌来完成的。

  • The overall structure of the project will be explored, including conventions of storing your modules in a lib/ folder, and making the application globally accessible so you can run it from any directory.

    将探讨项目的整体结构,包括将模块存储在lib/文件夹中以及使应用程序可全局访问的约定,以便您可以从任何目录运行它。

The style of code I have opted for in this project is one that omits the semi-colon where possible. This was purely personal preference. JavaScript engines intelligently add “missing” semi-colons at compile time. Although this suggests they are strictly required for code to be valid, the engine is very forgiving if they are omitted, either accidentally or deliberately.

我在该项目中选择的代码样式是在可能的地方省略了分号的样式。 这纯粹是个人喜好。 JavaScript引擎在编译时会智能地添加“缺少的”分号。 尽管这表明代码是有效代码的严格要求,但如果意外或故意将其忽略,引擎将非常宽容。

项目概况 (Project Overview)

Here is a screencast of the program running, going through the authentication process, displaying the main menu, and finally exiting the program that kills the process:

这是正在运行的程序的屏幕截图,该过程将通过身份验证过程,显示主菜单,最后退出导致该过程终止的程序:

CLI program demo — authentication and main menu implementation
CLI程序演示—身份验证和主菜单实现

Notice how I opted to “stay signed in” and later opted to simply “Exit” the program. Now when I start the program again, I can bypass the email address and password input and go directly to the main menu:

请注意,我是如何选择“保持登录状态”,后来又选择“退出”程序的。 现在,当我再次启动程序时,可以绕过电子邮件地址和密码输入,直接进入主菜单:

Immediately jumping into the main menu if already authenticated
如果已经通过身份验证,立即进入主菜单

This logic mirrors that of modern apps of today that persist a user sign in. We will delve into how this is done in the sections to follow. Let’s first explore the project structure itself and the dependencies being used.

这种逻辑反映了当今可以持久保持用户登录状态的现代应用程序的逻辑。我们将在后面的部分中深入研究如何完成此操作。 首先让我们探索项目结构本身以及所使用的依赖项。

项目设置 (Project Setup)

If starting a project from scratch, the first task is to initiate a new package.json and the accompanying dependencies. Assuming we are building a fresh project, run the following:

如果要从头开始项目,则第一个任务是启动新的package.json及其附带的依赖项。 假设我们正在构建一个新项目,请运行以下命令:

# initiate a new projectmkdir node-cli && cd node-cli && yarn init

Let’s install the dependencies we’ll use to facilitate the major elements of the CLI (we’ll cover each one of them next):

让我们安装将用于简化CLI的主要元素的依赖项(我们将在下一节中介绍它们):

# install dependenciesyarn add chalk clear configstore figlet inquirier log-symbols node-fetch ora# nodemon for development runtime
yarn global add nodemon

These dependencies act as the building blocks of the program. They have been chosen based on relevancy and rate of adoption at the time of writing this piece, but it is important to stress that there are indeed other great options available on NPM. For example, inquirer has been used in the demo for configuring prompts, but the prompts and enquirer packages are also very capable and come with their own advantages over similar packages.

这些依赖项充当程序的构建块。 在撰写本文时,已根据相关性和采用率对它们进行了选择,但必须强调的是,NPM上确实还有其他许多不错的选择。 例如, inquirer已经在演示用于配置的提示,但promptsenquirer包也非常有能力,并配备了类似的包自己的优势。

There is no one-size-fits-all for CLI utilities. Take a look around NPM and research alternative packages to find one that best suits your project needs.

CLI实用程序没有万能的选择。 环顾NPM并研究替代软件包,以找到最适合您项目需求的软件包。

The demo of this piece will show how each of these packages have been used. Here is a summary of each dependency and its purpose:

本文的演示将展示如何使用这些软件包。 这是每个依赖项及其用途的摘要:

  • chalk: A popular package for colorising string output.

    chalk :用于为字符串输出着色的流行软件包。

  • clear: Clears the terminal output by inserting whitespace and moving the preceding content out of the window.

    clear :通过插入空格并将前面的内容移出窗口,清除终端输出。

  • configstore: A lightweight key-value storage solution that stores values in a JSON file on your device.

    configstore :一种轻量级的键值存储解决方案,可将值存储在设备上的JSON文件中。

  • figlet: ASCII character art generation, that comes in useful for CLI splash interfaces.

    figlet :ASCII字符figlet生成,对于CLI启动界面很有用。

  • inquirer: A popular prompt library consisting of various input mechanisms designed for the command line.

    inquirer :一个流行的提示库,包含为命令行设计的各种输入机制。

  • log-symbols: Provides command line friendly symbols to embed with text.

    log-symbols :提供命令行友好符号以嵌入文本。

  • node-fetch: Provides the native JavaScript fetch() API to Node.js.

    node-fetch :向Node.js提供本机JavaScript fetch() API。

  • ora: Configurable command-line spinners, that are useful for when the program is awaiting server responses, generating or compiling projects, or other time consuming tasks.

    ora :可配置的命令行微调器,在程序等待服务器响应,生成或编译项目或其他耗时的任务时非常有用。

There are many other great packages that facilitate other CLI features — a few of which will be mentioned at the end of this article that could expand upon the demo project.

还有许多其他功能强大的软件包可促进其他CLI功能的使用-在本文结尾处将提及其中的一些功能,这些功能可能会在演示项目中扩展。

运行程序 (Running the program)

In order to efficiently test and debug the program, add the following scripts to package.json:

为了有效地测试和调试程序,请将以下脚本添加到package.json

// package.json"scripts": {
"debug": "nodemon --no-stdin index.js",
"start": "node index.js"
},

Now while in the project directory the program can be run in a debug capacity with yarn debug, and with node itself with yarn start. index.js is the entry file of the program where the main logic will be defined.

现在,在项目目录中时,该程序可以使用yarn debug以调试功能运行,而节点本身可以​​使用yarn startindex.js是程序的入口文件,将在其中定义主要逻辑。

Nodemon陷阱 (Nodemon gotchas)

nodemon enables live reloading of the program as we make changes — it mimics production behaviour for the most part, with a couple of key differences to keep in mind:

当我们进行更改时, nodemon允许实时重新加载程序-它在很大程度上模拟了生产行为,并牢记一些关键差异:

  • As discussed in this Github issue in relation to the inquirer package we’ll be using for prompts, nodemon does not by default fully support interactive prompts, and as a result we see anomalous behaviour such as a password text being displayed when it should be masked. We can limit this behaviour by including the --no-stdin flag to prevent the input stream being displayed in the terminal.

    正如在讨论这个问题,Github上相对于inquirer ,我们将使用提示的包, nodemon默认情况下不完全支持交互式提示,并作为一个结果,我们看到反常行为,比如当它应该被掩盖密码文本显示。 我们可以通过包括--no-stdin标志来限制此行为,以防止在终端中显示输入流。

  • When statements like process.exit() are called we expect the program process to be killed completely. However, with nodemon the program continues to run and waits for code changes before restarting.

    当调用诸如process.exit()类的语句时,我们希望程序进程被完全杀死。 但是,使用nodemon ,程序将继续运行,并在重新启动之前等待代码更改。

The takeaway here is to test the program’s behaviour with node before assuming there is something wrong with your program.

这里的重点是在假设程序有问题之前,使用node测试程序的行为。

项目文件结构 (Project file structure)

The project has a simple file structure where the index.js entry file is coupled with a lib/ folder that group exports in modules:

该项目具有一个简单的文件结构,其中index.js入口文件与一个lib/文件夹结合在一起,在模块中对导出进行分组:

// file structurelib/
auth.js
inquirer.js
ui.js
validate.js
index.js
.gitignore
...

A comprehensive .gitignore file for Node.js can be found here on GitHub, which is also useful for CLI based programs.

全面 .gitignore 对Node.js的文件可以发现 这里 在GitHub上,这也是基于CLI程序非常有用。

These lib modules have been imported into the entry file index.js. This file is where the program logic is defined, in an asynchronous run() function:

这些lib模块已导入到入口文件index.js 。 该文件是在异步run()函数中定义程序逻辑的位置:

// index.js at a glance#!/usr/bin/env nodeconst ui = require('./lib/ui')
const clear = require('clear')
...// clear the terminal
clear()// display the title
ui.title()// main function
const run = async () => {
...
}run()

The top line of the file is called a Shebang line, that is needed for the CLI to be run globally. We’ll look at the additional steps that need to be taken in a later section.

文件的第一行称为 Shebang 行,CLI在全局运行时需要该行。 我们将在后面的部分中介绍需要采取的其他步骤。

Before run() is executed, index.js does some preparation — it clears the terminal and displays the header of the program. The header in this case is generated within the ui module using the figlet and chalk package to generate ASCII character art and colourise it respectively:

run()之前, index.js一些准备-清除终端并显示程序的标题。 在这种情况下,标头是在ui模块中使用figletchalk包装生成的,以生成ASCII字符图并分别对其进行着色:

// ui.js titleconst chalk = require('chalk')
const figlet = require('figlet')module.exports = {
title: () =>
console.log(`${
chalk.white(
figlet.textSync(' My_CLI ', {
horizontalLayout: 'full',
})
)}\n`)
}

The new line \n provides spacing between the title and the following output.

新行 \n 在标题和以下输出之间提供间距。

title() exports a console.log() statement that houses the colourised character art. This output is displayed at the top of the program:

title()导出一个console.log()语句,该语句包含彩色字符。 此输出显示在程序顶部:

The My_CLI title being displayed at the top of the terminal
My_CLI标题显示在终端顶部

It is very common to wrap your output text with a specific chalk colour, such as chalk.white(), within console.log, and is a pattern to get used to when developing CLI apps with Node.

在console.log chalk.white()特定的粉笔颜色包装输出文本是很常见的,例如chalk.white() ,这是在使用Node开发CLI应用程序时习惯的模式。

All of the logic to follow will be defined in run(). Now let’s see how other modules have been used to handle authentication.

遵循的所有逻辑将在run()定义。 现在,让我们看看如何使用其他模块来处理身份验证。

身份验证和菜单访问 (Authentication and Menu Access)

The authentication mechanism of the CLI pertains to a simple token mechanism whereby the server will send a token once a user provides correct credentials. We will assume that our CLI is authenticating to some remote service.

CLI的身份验证机制与简单的令牌机制有关,通过该机制,一旦用户提供了正确的凭据,服务器将发送令牌。 我们将假定我们的CLI正在对某些远程服务进行身份验证。

认证流程设计 (Authentication flow design)

Provided a user is not signed in, they are firstly prompted to provide an email address and password (courtesy of inquirer), whose inputs are then sent to a server (node-fetch) for verification. If successfully authenticated, a token will be sent back.

如果未登录用户,则首先会提示他们提供电子邮件地址和密码(由inquirer ),然后将其输入发送到服务器( node-fetch )进行验证。 如果验证成功,令牌将被发回。

It is at this point that the user is asked whether to “stay signed in”. This means that the CLI will immediately open the main menu upon subsequent executions, without the need to sign in again. If the user chooses to stay signed in, the authentication token is persisted on device (configstore).

正是在这一点上,询问用户是否“保持登录状态”。 这意味着CLI在后续执行时将立即打开主菜单,而无需再次登录。 如果用户选择保持登录状态,则身份验证令牌将保留在设备( configstore )上。

In the event an authentication token is already stored when the application is run, it will default to checking whether that token is valid by verifying it with the server again. If verification fails, the user falls back to the sign in prompts once again.

如果在运行应用程序时已经存储了身份验证令牌,那么它将默认通过再次与服务器进行验证来检查该令牌是否有效。 如果验证失败,则用户将再次退回到登录提示。

This authentication flow can be summarised as follows:

此身份验证流程可以总结如下:

// CLI authentication flowcheck authentication token exists
Does not exist
Sign in prompts
Exists
Verify token on server
Successfully verified
Main menu
Failure
Sign in promptsSign in prompts
Successful authentication
Main menu
Invalid credentials
Sign in prompts

Concretely, the user cannot get past the sign in prompts without supplying a correct credentials, and the main menu cannot be reached until a valid authentication token is retrieved.

具体来说,如果没有提供正确的凭据,用户将无法通过登录提示,并且只有在检索到有效的身份验证令牌后才能访问主菜单。

I have published a piece dedicated to authentication in React Native that demonstrates an in-house token mechanism for authentication. That article can be found here: React Native: User Authentication Flow Explained.

我已经发布了一篇专门介绍React Native身份验证的文章,展示了内部令牌身份验证机制。 该文章可以在这里找到: React Native:用户身份验证流程说明

验证身份验证令牌 (Verifying authentication token)

Since authentication tokens are persisted in a configstore key value set, we can simply attempt to fetch it via its key. This is done in the auth module:

由于身份验证令牌configstoreconfigstore密钥值集中,因此我们可以简单地尝试通过其密钥来获取它。 这是在auth模块中完成的:

// fetch an authentication token if it existscheckTokenExists: () => {
return configStore.get('token')
? true
: false
},

This token needs to be verified server-side to prove that it is valid. To do this, we use node-fetch in conjunction with ora to display a spinner while the request is processing:

该令牌需要在服务器端进行验证以证明其有效。 为此,我们在处理请求时将node-fetchora结合使用以显示微调ora

// invoke spinner while token is being validatedlet spinner = ora({
text: ` ${chalk.green('Validating token...')}`,
prefixText: logSymbols.info,
}).start()// validate tokenconst validated = await auth.validateToken(token)// stop spinnerspinner.stop()

The spinner animation will continue until the validateToken() method is resolved, and is ended with ora’s stop() method. This process determines whether a user is initially signed in upon running the program.

微调器动画将继续进行,直到validateToken()方法解决为止,并以ora的stop()方法结束。 此过程确定用户在运行程序时是否最初登录。

提示用户输入凭据 (Prompting user for credentials)

In the event the user must sign in, they are prompted for an email address and password. Prompts are delivered in sequence using inquirer , that returns all the inputted answers as a JSON object once the sequence of prompts have been answered.

如果用户必须登录,则会提示他们输入电子邮件地址和密码。 提示使用inquirer顺序传递,一旦提示序列得到回答,该提示将所有输入的答案作为JSON对象返回。

inquirer provides a JSON interface for a range of inputs — we will be using the basic input and password types in this example. A validate function can also be defined that can either return true or provide the user feedback in the form of a string. The following example also embeds icons from log-symbols to further characterise these messages:

Inquirer为一系列输入提供JSON接口-在本示例中,我们将使用基本的inputpassword类型。 还可以定义一个validate函数,该函数可以返回true或以字符串形式提供用户反馈。 下面的示例还嵌入了log-symbols图标,以进一步表征这些消息:

// using inquirer to prompt email and passwordaskSignInCredentials: async () =>
inquirer.prompt([
{
name: 'email',
type: 'input',
message: 'Enter your email address:',
validate: value => validate.validateEmail(value)
? true
: logSymbols.warning + ' Please enter a valid email address'
}, {
name: 'password',
type: 'password',
message: 'Enter your password:',
mask: '*',
validate: value => value.length
? true
: logSymbols.warning + ' Please enter your password'
}
]),

inquirer supports a range of input types, and also has plugin support for ad hoc input types, including autocomplete functionality.

inquirer 支持多种输入类型,还具有对即席输入类型的插件支持,包括自动完成功能。

These two prompts will return the submitted answers once entered:

输入以下两个提示将返回提交的答案:

> {
email: "ross@jkrbinvestments"
password: "some-value-i-inputted"
}

These can now be sent to the server to verify much in the same fashion that the authentication token was, leveraging ora and node-fetch to facilitate the process. A successful validation will return a token, that can then be persisted with configstore if the user opts to “stay signed in”.

现在可以将它们发送到服务器,以使用身份验证令牌所用的相同方式进行大量验证,从而利用oranode-fetch简化该过程。 成功的验证将返回令牌,如果用户选择“保持登录状态”,则可以使用configstore该令牌。

Let’s now turn our attention to how an interactive menu can be created.

现在让我们将注意力转移到如何创建交互式菜单上。

主菜单循环 (Main menu loop)

The main menu is defined in its own indefinite loop that displays a menu or menu item depending on which menu option is selected from an inquirer prompt. The terminal window is reset before the menu item is displayed, by clearing the window and displaying the title.

主菜单以其自己的不确定循环定义,该循环显示菜单或菜单项,具体取决于从查询prompt选择的菜单选项。 通过清除窗口并显示标题,可以在显示菜单项之前重置终端窗口。

Let’s take a look at the main menu logic in its entirety:

让我们整体看一下主菜单逻辑:

// main menuwhile (signedIn) {  console.log('\n')
const { option } = await inquirer.mainOptions() // clean up UI
clear()
ui.title() // display menu item depending on option selected
switch (option) {
case 'Exit':
process.exit() case 'Sign Out and Exit':
auth.clearAuthToken()
process.exit() default:
console.log(chalk.grey('Option not yet implemented'))
}
}

The above code prompts the user to choose from a selection of predefined menu items. A switch statement then determines what to do after an item is selected. Each time a new item is selected, the terminal window is cleared and the title is displayed once again, to maintain a consistent UI.

上面的代码提示用户从预定义的菜单项中进行选择。 然后,switch语句确定在选择一个项目后该做什么。 每次选择一个新项目时,都会清除终端窗口,并再次显示标题,以保持一致的UI。

The menu list is another inquirer type, this time relying on the rawlist type:

菜单列表是另一种查询者类型,这次取决于rawlist类型:

// generating a raw list using inquirermainOptions: async () =>
inquirer.prompt([
{
name: 'option',
type: 'rawlist',
message: 'Choose an Option:\n',
choices: [
'Get Total Users',
'Manage Users',
new inquirer.Separator(),
'Sign Out and Exit',
'Exit',
]

}
]),

This is a quick and simple way to force a user to select an option before moving on in the program.

这是强迫用户在继续进行程序之前选择一个选项的快速简便的方法。

The other inquirer type we demo uses is the confirm type, that requests a Y/n response from the user when asked to stay signed in. Yes/no prompts are very useful in tasks like build configuration.

我们演示使用的另一种查询器类型是 confirm 类型,当要求用户保持登录状态时,它要求用户提供是/否响应。是/否提示在诸如构建配置之类的任务中非常有用。

In our base implementation the Exit and Sign out and Exit options have been handled. process.exit() kills the program — the only difference is that Sign out and Exit also clears the authentication token so the user doesn’t automatically sign back in upon rerunning the program. This is done simply by removing the token key from the Config Store:

在我们的基本实现中,已经处理了ExitSign out and Exit选项。 process.exit()杀死程序-唯一的区别是, Sign out and Exit还清除了身份验证令牌,因此用户在重新运行程序时不会自动重新登录。 只需从配置存储中删除令牌密钥即可完成:

// clearing the auth tokenclearAuthToken: () => {
configStore.clear('token')
}

全局运行程序 (Running the Program Globally)

The last major task is to let our program run globally. The first requirement is already covered, including a node shebang at the top of the entry file:

最后一项主要任务是让我们的程序在全球范围内运行。 已经满足了第一个要求,包括在条目文件顶部的一个节点shebang:

#!/usr/bin/env node

Next, a bin property needs to be added to package.json to map a command name to the file that needs to be executed:

接下来,需要将bin属性添加到package.json以将命令名称映射到需要执行的文件:

// package.json"bin": {
"mycli": "./index.js"
}

Now use NPM to install the module globally:

现在使用NPM全局安装模块:

npm install -g#confirm install
npm ls -g --depth=0

The program can now be executed from any directory, by simply calling mycli from the terminal.

现在,只需从终端调用mycli ,就可以从任何目录执行该程序。

综上所述 (In Summary)

This piece has hopefully prepared developers new to CLI development to jump straight into the ecosystem of tools and begin experimenting with the packages available, and to start building some tools to compliment their workflow and development pipeline.

希望这篇文章为CLI开发的新手开发人员做好准备,使其可以直接进入工具生态系统并开始尝试可用的软件包,并开始构建一些工具来补充其工作流程和开发流程。

We have opted for export functions and modules, and stayed away from class syntax in this walkthrough, but this was only for simplicity. Take advantage of data structures that make sense for your project.

我们选择了导出函数和模块,并且在本演练中不使用类语法,但这只是为了简单起见。 利用对您的项目有意义的数据结构。

Here are a few more packages that support key CLI features that were planned for this piece but did not cater for the demo project — but are worth mentioning to put on the reader’s radar:

以下是一些支持关键CLI功能的软件包,这些软件包已计划在本文中使用,但并未满足演示项目的需要-但值得一提的是,它引起了读者的注意:

  • meow: A simple yet powerful way to configure an app helper.

    meow :一种配置应用程序帮助程序的简单而强大的方法。

  • minimist: The standard way of parsing arguments and options to your program.

    minimist :解析程序参数和选项的标准方法。

  • boxen: A package for drawing different styles of boxes around terminal output.

    boxen :一个用于在终端输出周围绘制不同样式的盒子的软件包。

Developers accustomed to JavaScript will undoubtedly consider Node.js as their primary CLI solution.

习惯于JavaScript的开发人员无疑会将Node.js作为其主要的CLI解决方案。

Node.js is widely available for all major operating systems, is tailored for both server and desktop environments, and provides a fast runtime environment to carry out tasks with the underlying V8 JavaScript engine, also used for browsers like Chrome and Brave. These strengths, coupled with the familiarity of JavaScript many will have, make Node.js the favoured technology for a range of CLI based tools.

Node.js可广泛用于所有主要操作系统,是针对服务器和桌面环境量身定制的,并提供了快速的运行时环境,可使用基础V8 JavaScript引擎执行任务,该引擎也可用于Chrome和Brave等浏览器。 这些优势加上许多人对JavaScript的熟悉程度,使Node.js成为各种基于CLI的工具的首选技术。

翻译自: https://medium/@rossbulat/building-a-node-js-cli-an-introduction-fc3ac8868081

node cli

node cli

Node.js CLI现在已成为IT生态系统不可或缺的一部分 (Node.js CLIs are now an integral part of the IT ecosystem)

Command Line Interfaces (CLIs) are widely used by developers to facilitate the app development process. In the JavaScript ecosystem alone, that could be a compiling process (TypeScript to JavaScript, Webpack, Babel, etc), a package utility (creating a new NPM project, managing Git) or connecting to remote hosts. This almost endless list goes on for millions of utilities that have been developed for JavaScript based projects alone.

命令行界面(CLI)被开发人员广泛使用,以促进应用程序开发过程。 仅在JavaScript生态系统中,它可能是一个编译过程(JavaScript的TypeScript,Webpack,Babel等),一个包实用程序(创建一个新的NPM项目,管理Git)或连接到远程主机。 仅针对基于JavaScript的项目开发的数百万个实用程序就在不断增加的清单中。

It is not uncommon for a popular CLI based tool on NPM to have millions of weekly downloads, that illustrates just how integral Node.js (and JavaScript in general) has become for development processes. JavaScript undoubtedly plays a huge role for CLIs at various stages of the development pipeline, in the form of Node.js runtime programs.

流行的基于NPM的基于CLI的工具每周都有数百万次下载的情况并不少见,这说明了Node.js(以及一般JavaScript)对于开发过程已经变得如何不可或缺。 毫无疑问,JavaScript以Node.js运行时程序的形式在开发管道的各个阶段对CLI发挥了巨大作用。

CLI严重依赖于开源依赖项 (CLIs rely heavily on open source dependencies)

A CLI built with Node essentially boils down to effectively using a range of dependencies from the NPM ecosystem. These dependencies perform key tasks in your CLI, such as prompting a user for an input, colouring some output, displaying an app helper (commonly invoked with --help), terminal spinners while requests are processing, etc. It is unlikely that you will need to write a package from scratch to perform a particular task for the command line — the chances are NPM has your use case covered.

使用Node构建的CLI本质上可以归结为有效使用NPM生态系统中的一系列依赖关系。 这些依赖项在CLI中执行关键任务,例如提示用户输入信息,为某些输出着色,显示应用程序助手(通常通过--help调用),正在处理请求的终端微调器等。您不太可能会需要从头开始编写一个程序包以执行命令行的特定任务-NPM可能涵盖了您的用例。

What we do need to focus on however is to effectively organise the CLI source files into modules and define the logic of the program.

但是,我们需要重点关注的是将CLI源文件有效地组织到模块中并定义程序的逻辑。

This article introduces this development process of a Node.js based CLI program designed to authenticate a user from an external server, and provide them with a list of options from a main menu. The full project is available on GitHub for the reader to refer to or build upon:

本文介绍了基于Node.js的CLI程序的开发过程,该程序旨在验证来自外部服务器的用户,并为他们提供主菜单中的选项列表。 完整项目可在GitHub上找到,读者可以参考或建立在以下基础上:

Though simple, this program demonstrates how to handle a range of concepts for a Node based CLI program, including:

尽管很简单,但是该程序演示了如何处理基于Node的CLI程序的一系列概念,包括:

  • Styling output text in various ways, and displaying spinners when asynchronous tasks like contacting the server and waiting for a response are taking place. We’ll explore colorising text, generating ASCII art, and boxing your output.

    以各种方式设置输出文本的样式 ,并在发生异步任务(例如与服务器联系并等待响应)时显示微调框。 我们将研究为文本着色,生成ASCII文字并装箱输出。

  • How to prompt the user for various inputs, and sending those inputs to a server using fetch requests. These inputs range from simple text input, yes / no prompts, and choosing an option from a list of options.

    如何提示用户进行各种输入,以及如何使用获取请求将这些输入发送到服务器。 这些输入范围包括简单文本输入,是/否提示以及从选项列表中选择一个选项。

  • Efficiently persisting data on device as key-value pairs, that will be leveraged to store an authentication token.

    有效地将数据作为键值对持久存储在设备上,这些数据将用于存储身份验证令牌。

  • The ability to stay signed in, so the program will immediately boot to the main menu upon starting it up again. This is done by validating the authentication token before loading the main menu.

    保持登录状态的能力,因此程序再次启动时将立即启动到主菜单。 这是通过在加载主菜单之前验证身份验证令牌来完成的。

  • The overall structure of the project will be explored, including conventions of storing your modules in a lib/ folder, and making the application globally accessible so you can run it from any directory.

    将探讨项目的整体结构,包括将模块存储在lib/文件夹中以及使应用程序可全局访问的约定,以便您可以从任何目录运行它。

The style of code I have opted for in this project is one that omits the semi-colon where possible. This was purely personal preference. JavaScript engines intelligently add “missing” semi-colons at compile time. Although this suggests they are strictly required for code to be valid, the engine is very forgiving if they are omitted, either accidentally or deliberately.

我在该项目中选择的代码样式是在可能的地方省略了分号的样式。 这纯粹是个人喜好。 JavaScript引擎在编译时会智能地添加“缺少的”分号。 尽管这表明代码是有效代码的严格要求,但如果意外或故意将其忽略,引擎将非常宽容。

项目概况 (Project Overview)

Here is a screencast of the program running, going through the authentication process, displaying the main menu, and finally exiting the program that kills the process:

这是正在运行的程序的屏幕截图,该过程将通过身份验证过程,显示主菜单,最后退出导致该过程终止的程序:

CLI program demo — authentication and main menu implementation
CLI程序演示—身份验证和主菜单实现

Notice how I opted to “stay signed in” and later opted to simply “Exit” the program. Now when I start the program again, I can bypass the email address and password input and go directly to the main menu:

请注意,我是如何选择“保持登录状态”,后来又选择“退出”程序的。 现在,当我再次启动程序时,可以绕过电子邮件地址和密码输入,直接进入主菜单:

Immediately jumping into the main menu if already authenticated
如果已经通过身份验证,立即进入主菜单

This logic mirrors that of modern apps of today that persist a user sign in. We will delve into how this is done in the sections to follow. Let’s first explore the project structure itself and the dependencies being used.

这种逻辑反映了当今可以持久保持用户登录状态的现代应用程序的逻辑。我们将在后面的部分中深入研究如何完成此操作。 首先让我们探索项目结构本身以及所使用的依赖项。

项目设置 (Project Setup)

If starting a project from scratch, the first task is to initiate a new package.json and the accompanying dependencies. Assuming we are building a fresh project, run the following:

如果要从头开始项目,则第一个任务是启动新的package.json及其附带的依赖项。 假设我们正在构建一个新项目,请运行以下命令:

# initiate a new projectmkdir node-cli && cd node-cli && yarn init

Let’s install the dependencies we’ll use to facilitate the major elements of the CLI (we’ll cover each one of them next):

让我们安装将用于简化CLI的主要元素的依赖项(我们将在下一节中介绍它们):

# install dependenciesyarn add chalk clear configstore figlet inquirier log-symbols node-fetch ora# nodemon for development runtime
yarn global add nodemon

These dependencies act as the building blocks of the program. They have been chosen based on relevancy and rate of adoption at the time of writing this piece, but it is important to stress that there are indeed other great options available on NPM. For example, inquirer has been used in the demo for configuring prompts, but the prompts and enquirer packages are also very capable and come with their own advantages over similar packages.

这些依赖项充当程序的构建块。 在撰写本文时,已根据相关性和采用率对它们进行了选择,但必须强调的是,NPM上确实还有其他许多不错的选择。 例如, inquirer已经在演示用于配置的提示,但promptsenquirer包也非常有能力,并配备了类似的包自己的优势。

There is no one-size-fits-all for CLI utilities. Take a look around NPM and research alternative packages to find one that best suits your project needs.

CLI实用程序没有万能的选择。 环顾NPM并研究替代软件包,以找到最适合您项目需求的软件包。

The demo of this piece will show how each of these packages have been used. Here is a summary of each dependency and its purpose:

本文的演示将展示如何使用这些软件包。 这是每个依赖项及其用途的摘要:

  • chalk: A popular package for colorising string output.

    chalk :用于为字符串输出着色的流行软件包。

  • clear: Clears the terminal output by inserting whitespace and moving the preceding content out of the window.

    clear :通过插入空格并将前面的内容移出窗口,清除终端输出。

  • configstore: A lightweight key-value storage solution that stores values in a JSON file on your device.

    configstore :一种轻量级的键值存储解决方案,可将值存储在设备上的JSON文件中。

  • figlet: ASCII character art generation, that comes in useful for CLI splash interfaces.

    figlet :ASCII字符figlet生成,对于CLI启动界面很有用。

  • inquirer: A popular prompt library consisting of various input mechanisms designed for the command line.

    inquirer :一个流行的提示库,包含为命令行设计的各种输入机制。

  • log-symbols: Provides command line friendly symbols to embed with text.

    log-symbols :提供命令行友好符号以嵌入文本。

  • node-fetch: Provides the native JavaScript fetch() API to Node.js.

    node-fetch :向Node.js提供本机JavaScript fetch() API。

  • ora: Configurable command-line spinners, that are useful for when the program is awaiting server responses, generating or compiling projects, or other time consuming tasks.

    ora :可配置的命令行微调器,在程序等待服务器响应,生成或编译项目或其他耗时的任务时非常有用。

There are many other great packages that facilitate other CLI features — a few of which will be mentioned at the end of this article that could expand upon the demo project.

还有许多其他功能强大的软件包可促进其他CLI功能的使用-在本文结尾处将提及其中的一些功能,这些功能可能会在演示项目中扩展。

运行程序 (Running the program)

In order to efficiently test and debug the program, add the following scripts to package.json:

为了有效地测试和调试程序,请将以下脚本添加到package.json

// package.json"scripts": {
"debug": "nodemon --no-stdin index.js",
"start": "node index.js"
},

Now while in the project directory the program can be run in a debug capacity with yarn debug, and with node itself with yarn start. index.js is the entry file of the program where the main logic will be defined.

现在,在项目目录中时,该程序可以使用yarn debug以调试功能运行,而节点本身可以​​使用yarn startindex.js是程序的入口文件,将在其中定义主要逻辑。

Nodemon陷阱 (Nodemon gotchas)

nodemon enables live reloading of the program as we make changes — it mimics production behaviour for the most part, with a couple of key differences to keep in mind:

当我们进行更改时, nodemon允许实时重新加载程序-它在很大程度上模拟了生产行为,并牢记一些关键差异:

  • As discussed in this Github issue in relation to the inquirer package we’ll be using for prompts, nodemon does not by default fully support interactive prompts, and as a result we see anomalous behaviour such as a password text being displayed when it should be masked. We can limit this behaviour by including the --no-stdin flag to prevent the input stream being displayed in the terminal.

    正如在讨论这个问题,Github上相对于inquirer ,我们将使用提示的包, nodemon默认情况下不完全支持交互式提示,并作为一个结果,我们看到反常行为,比如当它应该被掩盖密码文本显示。 我们可以通过包括--no-stdin标志来限制此行为,以防止在终端中显示输入流。

  • When statements like process.exit() are called we expect the program process to be killed completely. However, with nodemon the program continues to run and waits for code changes before restarting.

    当调用诸如process.exit()类的语句时,我们希望程序进程被完全杀死。 但是,使用nodemon ,程序将继续运行,并在重新启动之前等待代码更改。

The takeaway here is to test the program’s behaviour with node before assuming there is something wrong with your program.

这里的重点是在假设程序有问题之前,使用node测试程序的行为。

项目文件结构 (Project file structure)

The project has a simple file structure where the index.js entry file is coupled with a lib/ folder that group exports in modules:

该项目具有一个简单的文件结构,其中index.js入口文件与一个lib/文件夹结合在一起,在模块中对导出进行分组:

// file structurelib/
auth.js
inquirer.js
ui.js
validate.js
index.js
.gitignore
...

A comprehensive .gitignore file for Node.js can be found here on GitHub, which is also useful for CLI based programs.

全面 .gitignore 对Node.js的文件可以发现 这里 在GitHub上,这也是基于CLI程序非常有用。

These lib modules have been imported into the entry file index.js. This file is where the program logic is defined, in an asynchronous run() function:

这些lib模块已导入到入口文件index.js 。 该文件是在异步run()函数中定义程序逻辑的位置:

// index.js at a glance#!/usr/bin/env nodeconst ui = require('./lib/ui')
const clear = require('clear')
...// clear the terminal
clear()// display the title
ui.title()// main function
const run = async () => {
...
}run()

The top line of the file is called a Shebang line, that is needed for the CLI to be run globally. We’ll look at the additional steps that need to be taken in a later section.

文件的第一行称为 Shebang 行,CLI在全局运行时需要该行。 我们将在后面的部分中介绍需要采取的其他步骤。

Before run() is executed, index.js does some preparation — it clears the terminal and displays the header of the program. The header in this case is generated within the ui module using the figlet and chalk package to generate ASCII character art and colourise it respectively:

run()之前, index.js一些准备-清除终端并显示程序的标题。 在这种情况下,标头是在ui模块中使用figletchalk包装生成的,以生成ASCII字符图并分别对其进行着色:

// ui.js titleconst chalk = require('chalk')
const figlet = require('figlet')module.exports = {
title: () =>
console.log(`${
chalk.white(
figlet.textSync(' My_CLI ', {
horizontalLayout: 'full',
})
)}\n`)
}

The new line \n provides spacing between the title and the following output.

新行 \n 在标题和以下输出之间提供间距。

title() exports a console.log() statement that houses the colourised character art. This output is displayed at the top of the program:

title()导出一个console.log()语句,该语句包含彩色字符。 此输出显示在程序顶部:

The My_CLI title being displayed at the top of the terminal
My_CLI标题显示在终端顶部

It is very common to wrap your output text with a specific chalk colour, such as chalk.white(), within console.log, and is a pattern to get used to when developing CLI apps with Node.

在console.log chalk.white()特定的粉笔颜色包装输出文本是很常见的,例如chalk.white() ,这是在使用Node开发CLI应用程序时习惯的模式。

All of the logic to follow will be defined in run(). Now let’s see how other modules have been used to handle authentication.

遵循的所有逻辑将在run()定义。 现在,让我们看看如何使用其他模块来处理身份验证。

身份验证和菜单访问 (Authentication and Menu Access)

The authentication mechanism of the CLI pertains to a simple token mechanism whereby the server will send a token once a user provides correct credentials. We will assume that our CLI is authenticating to some remote service.

CLI的身份验证机制与简单的令牌机制有关,通过该机制,一旦用户提供了正确的凭据,服务器将发送令牌。 我们将假定我们的CLI正在对某些远程服务进行身份验证。

认证流程设计 (Authentication flow design)

Provided a user is not signed in, they are firstly prompted to provide an email address and password (courtesy of inquirer), whose inputs are then sent to a server (node-fetch) for verification. If successfully authenticated, a token will be sent back.

如果未登录用户,则首先会提示他们提供电子邮件地址和密码(由inquirer ),然后将其输入发送到服务器( node-fetch )进行验证。 如果验证成功,令牌将被发回。

It is at this point that the user is asked whether to “stay signed in”. This means that the CLI will immediately open the main menu upon subsequent executions, without the need to sign in again. If the user chooses to stay signed in, the authentication token is persisted on device (configstore).

正是在这一点上,询问用户是否“保持登录状态”。 这意味着CLI在后续执行时将立即打开主菜单,而无需再次登录。 如果用户选择保持登录状态,则身份验证令牌将保留在设备( configstore )上。

In the event an authentication token is already stored when the application is run, it will default to checking whether that token is valid by verifying it with the server again. If verification fails, the user falls back to the sign in prompts once again.

如果在运行应用程序时已经存储了身份验证令牌,那么它将默认通过再次与服务器进行验证来检查该令牌是否有效。 如果验证失败,则用户将再次退回到登录提示。

This authentication flow can be summarised as follows:

此身份验证流程可以总结如下:

// CLI authentication flowcheck authentication token exists
Does not exist
Sign in prompts
Exists
Verify token on server
Successfully verified
Main menu
Failure
Sign in promptsSign in prompts
Successful authentication
Main menu
Invalid credentials
Sign in prompts

Concretely, the user cannot get past the sign in prompts without supplying a correct credentials, and the main menu cannot be reached until a valid authentication token is retrieved.

具体来说,如果没有提供正确的凭据,用户将无法通过登录提示,并且只有在检索到有效的身份验证令牌后才能访问主菜单。

I have published a piece dedicated to authentication in React Native that demonstrates an in-house token mechanism for authentication. That article can be found here: React Native: User Authentication Flow Explained.

我已经发布了一篇专门介绍React Native身份验证的文章,展示了内部令牌身份验证机制。 该文章可以在这里找到: React Native:用户身份验证流程说明

验证身份验证令牌 (Verifying authentication token)

Since authentication tokens are persisted in a configstore key value set, we can simply attempt to fetch it via its key. This is done in the auth module:

由于身份验证令牌configstoreconfigstore密钥值集中,因此我们可以简单地尝试通过其密钥来获取它。 这是在auth模块中完成的:

// fetch an authentication token if it existscheckTokenExists: () => {
return configStore.get('token')
? true
: false
},

This token needs to be verified server-side to prove that it is valid. To do this, we use node-fetch in conjunction with ora to display a spinner while the request is processing:

该令牌需要在服务器端进行验证以证明其有效。 为此,我们在处理请求时将node-fetchora结合使用以显示微调ora

// invoke spinner while token is being validatedlet spinner = ora({
text: ` ${chalk.green('Validating token...')}`,
prefixText: logSymbols.info,
}).start()// validate tokenconst validated = await auth.validateToken(token)// stop spinnerspinner.stop()

The spinner animation will continue until the validateToken() method is resolved, and is ended with ora’s stop() method. This process determines whether a user is initially signed in upon running the program.

微调器动画将继续进行,直到validateToken()方法解决为止,并以ora的stop()方法结束。 此过程确定用户在运行程序时是否最初登录。

提示用户输入凭据 (Prompting user for credentials)

In the event the user must sign in, they are prompted for an email address and password. Prompts are delivered in sequence using inquirer , that returns all the inputted answers as a JSON object once the sequence of prompts have been answered.

如果用户必须登录,则会提示他们输入电子邮件地址和密码。 提示使用inquirer顺序传递,一旦提示序列得到回答,该提示将所有输入的答案作为JSON对象返回。

inquirer provides a JSON interface for a range of inputs — we will be using the basic input and password types in this example. A validate function can also be defined that can either return true or provide the user feedback in the form of a string. The following example also embeds icons from log-symbols to further characterise these messages:

Inquirer为一系列输入提供JSON接口-在本示例中,我们将使用基本的inputpassword类型。 还可以定义一个validate函数,该函数可以返回true或以字符串形式提供用户反馈。 下面的示例还嵌入了log-symbols图标,以进一步表征这些消息:

// using inquirer to prompt email and passwordaskSignInCredentials: async () =>
inquirer.prompt([
{
name: 'email',
type: 'input',
message: 'Enter your email address:',
validate: value => validate.validateEmail(value)
? true
: logSymbols.warning + ' Please enter a valid email address'
}, {
name: 'password',
type: 'password',
message: 'Enter your password:',
mask: '*',
validate: value => value.length
? true
: logSymbols.warning + ' Please enter your password'
}
]),

inquirer supports a range of input types, and also has plugin support for ad hoc input types, including autocomplete functionality.

inquirer 支持多种输入类型,还具有对即席输入类型的插件支持,包括自动完成功能。

These two prompts will return the submitted answers once entered:

输入以下两个提示将返回提交的答案:

> {
email: "ross@jkrbinvestments"
password: "some-value-i-inputted"
}

These can now be sent to the server to verify much in the same fashion that the authentication token was, leveraging ora and node-fetch to facilitate the process. A successful validation will return a token, that can then be persisted with configstore if the user opts to “stay signed in”.

现在可以将它们发送到服务器,以使用身份验证令牌所用的相同方式进行大量验证,从而利用oranode-fetch简化该过程。 成功的验证将返回令牌,如果用户选择“保持登录状态”,则可以使用configstore该令牌。

Let’s now turn our attention to how an interactive menu can be created.

现在让我们将注意力转移到如何创建交互式菜单上。

主菜单循环 (Main menu loop)

The main menu is defined in its own indefinite loop that displays a menu or menu item depending on which menu option is selected from an inquirer prompt. The terminal window is reset before the menu item is displayed, by clearing the window and displaying the title.

主菜单以其自己的不确定循环定义,该循环显示菜单或菜单项,具体取决于从查询prompt选择的菜单选项。 通过清除窗口并显示标题,可以在显示菜单项之前重置终端窗口。

Let’s take a look at the main menu logic in its entirety:

让我们整体看一下主菜单逻辑:

// main menuwhile (signedIn) {  console.log('\n')
const { option } = await inquirer.mainOptions() // clean up UI
clear()
ui.title() // display menu item depending on option selected
switch (option) {
case 'Exit':
process.exit() case 'Sign Out and Exit':
auth.clearAuthToken()
process.exit() default:
console.log(chalk.grey('Option not yet implemented'))
}
}

The above code prompts the user to choose from a selection of predefined menu items. A switch statement then determines what to do after an item is selected. Each time a new item is selected, the terminal window is cleared and the title is displayed once again, to maintain a consistent UI.

上面的代码提示用户从预定义的菜单项中进行选择。 然后,switch语句确定在选择一个项目后该做什么。 每次选择一个新项目时,都会清除终端窗口,并再次显示标题,以保持一致的UI。

The menu list is another inquirer type, this time relying on the rawlist type:

菜单列表是另一种查询者类型,这次取决于rawlist类型:

// generating a raw list using inquirermainOptions: async () =>
inquirer.prompt([
{
name: 'option',
type: 'rawlist',
message: 'Choose an Option:\n',
choices: [
'Get Total Users',
'Manage Users',
new inquirer.Separator(),
'Sign Out and Exit',
'Exit',
]

}
]),

This is a quick and simple way to force a user to select an option before moving on in the program.

这是强迫用户在继续进行程序之前选择一个选项的快速简便的方法。

The other inquirer type we demo uses is the confirm type, that requests a Y/n response from the user when asked to stay signed in. Yes/no prompts are very useful in tasks like build configuration.

我们演示使用的另一种查询器类型是 confirm 类型,当要求用户保持登录状态时,它要求用户提供是/否响应。是/否提示在诸如构建配置之类的任务中非常有用。

In our base implementation the Exit and Sign out and Exit options have been handled. process.exit() kills the program — the only difference is that Sign out and Exit also clears the authentication token so the user doesn’t automatically sign back in upon rerunning the program. This is done simply by removing the token key from the Config Store:

在我们的基本实现中,已经处理了ExitSign out and Exit选项。 process.exit()杀死程序-唯一的区别是, Sign out and Exit还清除了身份验证令牌,因此用户在重新运行程序时不会自动重新登录。 只需从配置存储中删除令牌密钥即可完成:

// clearing the auth tokenclearAuthToken: () => {
configStore.clear('token')
}

全局运行程序 (Running the Program Globally)

The last major task is to let our program run globally. The first requirement is already covered, including a node shebang at the top of the entry file:

最后一项主要任务是让我们的程序在全球范围内运行。 已经满足了第一个要求,包括在条目文件顶部的一个节点shebang:

#!/usr/bin/env node

Next, a bin property needs to be added to package.json to map a command name to the file that needs to be executed:

接下来,需要将bin属性添加到package.json以将命令名称映射到需要执行的文件:

// package.json"bin": {
"mycli": "./index.js"
}

Now use NPM to install the module globally:

现在使用NPM全局安装模块:

npm install -g#confirm install
npm ls -g --depth=0

The program can now be executed from any directory, by simply calling mycli from the terminal.

现在,只需从终端调用mycli ,就可以从任何目录执行该程序。

综上所述 (In Summary)

This piece has hopefully prepared developers new to CLI development to jump straight into the ecosystem of tools and begin experimenting with the packages available, and to start building some tools to compliment their workflow and development pipeline.

希望这篇文章为CLI开发的新手开发人员做好准备,使其可以直接进入工具生态系统并开始尝试可用的软件包,并开始构建一些工具来补充其工作流程和开发流程。

We have opted for export functions and modules, and stayed away from class syntax in this walkthrough, but this was only for simplicity. Take advantage of data structures that make sense for your project.

我们选择了导出函数和模块,并且在本演练中不使用类语法,但这只是为了简单起见。 利用对您的项目有意义的数据结构。

Here are a few more packages that support key CLI features that were planned for this piece but did not cater for the demo project — but are worth mentioning to put on the reader’s radar:

以下是一些支持关键CLI功能的软件包,这些软件包已计划在本文中使用,但并未满足演示项目的需要-但值得一提的是,它引起了读者的注意:

  • meow: A simple yet powerful way to configure an app helper.

    meow :一种配置应用程序帮助程序的简单而强大的方法。

  • minimist: The standard way of parsing arguments and options to your program.

    minimist :解析程序参数和选项的标准方法。

  • boxen: A package for drawing different styles of boxes around terminal output.

    boxen :一个用于在终端输出周围绘制不同样式的盒子的软件包。

Developers accustomed to JavaScript will undoubtedly consider Node.js as their primary CLI solution.

习惯于JavaScript的开发人员无疑会将Node.js作为其主要的CLI解决方案。

Node.js is widely available for all major operating systems, is tailored for both server and desktop environments, and provides a fast runtime environment to carry out tasks with the underlying V8 JavaScript engine, also used for browsers like Chrome and Brave. These strengths, coupled with the familiarity of JavaScript many will have, make Node.js the favoured technology for a range of CLI based tools.

Node.js可广泛用于所有主要操作系统,是针对服务器和桌面环境量身定制的,并提供了快速的运行时环境,可使用基础V8 JavaScript引擎执行任务,该引擎也可用于Chrome和Brave等浏览器。 这些优势加上许多人对JavaScript的熟悉程度,使Node.js成为各种基于CLI的工具的首选技术。

翻译自: https://medium/@rossbulat/building-a-node-js-cli-an-introduction-fc3ac8868081

node cli

本文标签: 简介NodeCLIJS