如何使用 NodeJS 构建命令行工具 - 分步指南
步骤
这篇文章将指导开发人员使用 Node.js 构建 CLI 工具。您还将学习如何将该工具发布到 NPM。Node.js 允许我们使用 JavaScript 构建命令行工具。正如 NPM 仓库所示,Node.js 拥有丰富的包生态系统。
构建人们可以使用的 CLI 工具是提升编程和解决问题能力的好方法。在本文中,我们将探讨我如何创建一个用于检查网站是否正常运行的CLI 工具。您可以在此处找到源代码。
步骤
制定计划
- 在 nodejs 上运行应用程序
- 从终端获取我的参数
- 从isitup检查网站状态
- 将响应返回到终端
- 如果网站已启动,则创建一个启动选项。
创建 Node 应用程序
让我们为我们的项目创建一个文件夹,并导航到我们终端上的项目目录的根目录。
mkdir cli-project && cd cli-project
初始化节点项目
npm init -y
。
这将创建一个具有如下 package.json 结构的节点应用程序:
{
"name": "cli-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
}
创建 index.js 文件
touch index.js
打开此文件并将“Hello, here is my first CLI tool”打印到控制台
\\ index.js
console.log("Hello, here is my first CLI tool")
现在返回终端并运行node index
$ node index
Hello, here is my first CLI tool
现在你的 Node 应用已经运行了,是时候把它转换成 Shell 命令了。
为了不用 node 命令直接调用 index.js 文件,请将下面的代码放在#!/usr/bin/env node
index.js 文件的顶部。
\\ index.js
#!/usr/bin/env node
console.log("Hello, here is my first CLI tool")
接下来,我们将在 package.json 文件中添加一个 bin 属性。但是,我们的项目将在单个文件上运行,因此我们不会使用 bin 属性来指定命令名称。我们将使用 name 属性来指定。
{
"name": "cli-project",
"version": "1.0.0",
// ...
"bin": "./index.js",
// ...
}
如果你cli-project
现在在项目目录中运行它应该返回
$ cli-project
Hello, here is my first CLI tool
我们现在要做两点修改。我们不想让 CLI 名称变成。因此,我们将 package.json属性cli-project
的值更改为name
webcheck
{
"name": "webcheck",
// ...
}
我们的 shell 命令仍然是本地的。现在是时候让它变成全局的了。运行npm link
离开项目根目录,webcheck
从任意目录运行。你应该会看到如下结果。
$ webcheck
Hello, here is my first CLI tool
恭喜!!!你刚刚用 Node 应用创建了你的第一个 Shell 命令。它可以推送到 NPM 供用户下载运行,但由于项目只完成了一半,我建议等到 Node 应用完成后再发布。
从终端解析参数
为了解析来自终端的参数,我们将使用内置的 Node.js 模块argv。根据 Node.js 官方文档,process.argv 属性返回一个数组,其中包含启动 Node.js 进程时传递的命令行参数。第一个元素是 process.execPath。第二个元素是正在执行的 JavaScript 文件的路径。其余元素是任何其他命令行参数。因此,我们传递给终端的任何参数都将是数组的第三个元素。编辑 index.js 文件,使其如下所示。
\\ index.js
#!/usr/bin/env node
console.log(processs.argv);
在终端上运行你的应用。输出应该类似于以下内容。
$ webcheck
[
'C:\\Program Files\\nodejs\\node.exe',
'C:\\Users\\adeniyi\\Desktop\\Projects\\cli-project\\index'
]
现在向您的命令添加一个附加参数,您的输出应该类似于此。
$ webcheck file
[
'C:\\Program Files\\nodejs\\node.exe',
'C:\\Users\\adeniyi\\Desktop\\Projects\\cli-project\\index',
'file'
]
注意:附加的参数越多,数组就越大。为了达到我们的目的,我们将参数限制为字符串,并将其解析到项目中作为数组的第三个元素。
现在是时候将此参数解析到我们的应用程序中,并从isitup api获取信息了。
打开你的 index.js 文件并输入此代码。
#!/usr/bin/env node
const fetch = require("node-fetch");
// console.log(process.argv);
const website = process.argv[2];
function CheckWeb(name) {
const info =fetch(`https://isitup.org/${name}.json`)
.then(response => response.json());
info.then(function(result) {
if (result.response_code == 200) {
console.log('website is up and running')
} else {
console.log('website is down')
}
}
CheckWeb(website);
由于 Node.js 不支持原生的 JavaScript fetch,我们需要这个包来帮助我们从isitup APInode-fetch
获取数据。运行。 我们的 CheckWeb 函数接受一个 name 参数,并从 API 获取相应的响应。现在,我们将命令行参数传递给函数。让我们打开终端,看看代码是如何运行的。npm install node-fetch
$ webcheck duckduckgo.com
website is up and running
耶!!!
$ webcheck google.com
website is down
等等?!
我们来试试看看哪里出了问题。我最喜欢的调试工具(控制台)来帮忙了。
#!/usr/bin/env node
//...
function CheckWeb(name) {
const info =fetch(`https://isitup.org/${name}.json`)
.then(response => response.json());
info.then(function(result) {
console.log(result)
}
CheckWeb(website);
再次从终端运行该应用程序
$ webcheck google.com
{
domain: "google.com",
port: 80,
status_code: 1,
response_ip: "216.58.210.206",
response_code: 301,
response_time: 0.008
}
因此,301 重定向被认为是将用户从 HTTP 升级到 HTTPS 的最佳实践。我们需要我们的应用知道这一点,并告诉我们 Google 已恢复。我们可以通过两种方式来实现这一点:一连串的 if else 语句,用于导航相应的响应代码,或者查找null
响应代码
。
#!/usr/bin/env node
const fetch = require("node-fetch");
// console.log(process.argv);
const website = process.argv[2];
function CheckWeb(name) {
const info =fetch(`https://isitup.org/${name}.json`)
.then(response => response.json());
info.then(function(result) {
if (result.response_code == null) {
console.log('website is down')
} else {
console.log('website is up and running')
}
}
CheckWeb(website);
运行你的应用
$ webcheck google.com
website is up and running
或这个
#!/usr/bin/env node
const fetch = require("node-fetch");
// console.log(process.argv);
const website = process.argv[2];
function CheckWeb(name) {
const info =fetch(`https://isitup.org/${name}.json`)
.then(response => response.json());
info.then(function(result) {
if (result.response_code == 200) {
console.log('\x1b[32m%s\x1b[0m', 'website is up and running');
} else if (result.response_code == 301) {
console.log('\x1b[34m%s\x1b[0m', 'website has been moved permanently but is up');
} else if (result.response_code == 302){
console.log('\x1b[34m%s\x1b[0m', 'temporary redirect, website is up');
} else if (result.response_code == 403) {
console.log('\x1b[33m%s\x1b[0m', 'information not found');
}
else {
console.log('\x1b[31m%s\x1b[0m', 'website is down')
}
});
CheckWeb(website);
这段'\x1b[31m%s\x1b[0m'
代码以及您在控制台语句中看到的类似代码决定了响应消息的颜色。
运行您的应用
$ webcheck google.com
website has been moved permanently but is up
现在,我们可以将 cli 工具的第一个版本发布到 NPM 了。您需要创建一个.npmignore
文件。将以下内容复制到该文件中。
//.npmignore
node_modules/
这确保你不会将 Node 模块与包一起发布。现在,运行(npm publish
如果你之前没有从终端登录过 npm,请先登录)npm login
干杯!用户现在可以前往 NPM 搜索并下载你的 cli 工具了。
从终端启动网站
为此,我们需要open
一个能帮助我们打开 URL 的包。然后,我们将编写一个函数来启动网站。npm install open
编辑 index.js 文件
#!/usr/bin/env node
const fetch = require("node-fetch");
const open = require("open");
const website = process.argv[2];
function CheckWeb(name) {
const info =fetch(`https://isitup.org/${name}.json`)
.then(response => response.json());
info.then(function(result) {
function openWebSite () {
setTimeout(function()
{ open(`https://${result.domain}`); }, 1000);
};
if (result.response_code == 200) {
console.log('\x1b[32m%s\x1b[0m', 'website is up and running');
openWebSite();
} else if (result.response_code == 301) {
console.log('\x1b[32m%s\x1b[0m', 'website has been moved permanently but is up');
openWebSite();
} else if (result.response_code == 302){
console.log('\x1b[34m%s\x1b[0m', 'temporary redirect, website is up');
openWebSite();
} else if (result.response_code == 403) {
console.log('\x1b[33m%s\x1b[0m', 'information not found');
openWebSite();
}
else {
console.log('\x1b[31m%s\x1b[0m', 'website is down')
}
});
}
}
CheckWeb(website);
openWebsite 函数会自动从终端在默认浏览器中启动选中的网站。但是,我们希望用户能够决定是否打开该网站。
我们将安装两个包arg
和inquirer
。我们将使用 和 将命令行参数解析为选项arg
,inquirer
以提示用户输入值。npm install arg inquirer
我们将像这样构建我们的 index.js 文件
#!/usr/bin/env node
const fetch = require("node-fetch");
const open = require('open');
const arg = require('arg');
const inquirer = require('inquirer');
function ParseCliArgsIntoOptions() {
const args = arg(
{
'--website': Boolean,
'--yes': Boolean,
'-w': '--website',
'-y': '--yes',
},
{
argv: process.argv.slice(2),
}
);
return {
website: args['--website'] || false,
};
}
async function PromptForOptions(options) {
const questions = [];
if (!options.website) {
questions.push({
type: 'confirm',
name: 'website',
message: 'Open the website on your browser?',
default: false,
});
}
const answers = await inquirer.prompt(questions);
return {
...options,
website: options.website || answers.website,
};
}
async function LaunchWebsite(result) {
let options = ParseCliArgsIntoOptions();
options = await PromptForOptions(options);
if (options.website == true) {
open(`https://${result.domain}`);
}
}
const website = process.argv[2];
function CheckWeb(name) {
// ....
}
我们所做的就是创建一个 LaunchWebsite 函数,它接受另外两个函数,ParseCliArgsIntoOptions()
这两个函数会针对该PromptForOptions()
函数提示的问题,提供一个布尔值“是/否”选项。如果选项true
为“是”,则打开网站。
现在,我们将 LaunchWebsite 函数注入到 Checkweb 函数中,并将操作结果传递fetch
给它。
#!/usr/bin/env node
const fetch = require("node-fetch");
const open = require('open');
const arg = require('arg');
const inquirer = require('inquirer');
function ParseCliArgsIntoOptions() {
//...
}
async function PromptForOptions(options) {
//...
}
async function LaunchWebsite(result) {
//...
}
function CheckWeb(name) {
const info =fetch(`https://isitup.org/${name}.json`)
.then(response => response.json());
info.then(function(result) {
if (result.response_code == 200) {
console.log('\x1b[32m%s\x1b[0m', 'website is up and running');
LaunchWebsite(result)
} else if (result.response_code == 301) {
console.log('\x1b[32m%s\x1b[0m', 'website has been moved permanently but is up');
LaunchWebsite(result)
console.log('\x1b[34m%s\x1b[0m', 'website has been moved permanently but is up');
LaunchWebsite(result)
} else if (result.response_code == 302){
console.log('\x1b[34m%s\x1b[0m', 'temporary redirect, website is up');
LaunchWebsite(result)
} else if (result.response_code == 403) {
console.log('\x1b[33m%s\x1b[0m', 'information not found');
LaunchWebsite(result)
}
else {
console.log('\x1b[31m%s\x1b[0m', 'website is down')
}
});
}
}
CheckWeb(website);
如果你现在在终端上运行 shell 命令,就会发生这种情况
$ webcheck google.com
website has been moved permanently but is up
? Open the website on your browser? (y/N)
太棒了!旅程即将结束。
让我们为那些可能忘记添加网站扩展程序的用户处理错误,以此作为圆满的结束。网站可能已经正常运行,但这肯定会导致网站无法访问。
$ webcheck google
website is down
有很多方法可以解决这个问题。你可以创建一个包含所有可能扩展名(超过 400 个)的数组,然后编写一个正则表达式函数,用于在我们的网站字符串中搜索数组参数。如果你问我,这有点没必要。或者,你也可以像下面这样,直接搜索参数中的子字符串“.”
#!/usr/bin/env node
const fetch = require("node-fetch");
const open = require('open');
const arg = require('arg');
const inquirer = require('inquirer');
function ParseCliArgsIntoOptions() {
//...
}
async function PromptForOptions(options) {
//...
}
async function LaunchWebsite(result) {
//...
}
function CheckWeb(name) {
if (name.indexOf('.') > -1) {
const info =fetch(`https://isitup.org/${name}.json`)
.then(response => response.json());
info.then(function(result) {
//...
});
} else {
console.log('\x1b[31m%s\x1b[0m', 'please append your url extension e.g(mouse.com)')
}
}
CheckWeb(website);
在终点站。
$ webcheck google
please append your url extension e.g(mouse.com)
现在,让我们再次发布更新的工具。你必须更新版本号。运行npm version 1.1.0
然后推送到 NPMnpm publish
结论
我们的 CLI 工具已在 NPM 上启动并运行。
如果您对此有任何疑问,欢迎在评论区留言。此外,欢迎纠正或补充我可能遗漏的任何内容。欢迎通过电子邮件或Twitter给我留言。再次强调,您可以在这里
找到源代码。 谢谢!