上一篇文章:实战 | 从零开始使用JavaScript制作自己的命令行(CLI工具) 中介绍了如何从零开始制作CLI,只是一个简单的示例,今天更进一步,来制作一个实际的程序,生成HTML模板。
在这篇文章中,我们将构建一个简单的CLI,允许用户生成HTML页面。我们首先要生成一个标准的空白页面,然后让用户输入参数,比如文件名和标题,先通过选项,然后通过提示问题让用户输入参数。
创建 Hello World CLI
创建用于编写CLI的文件夹。我将其命名为 html-generator-cli。打开一个终端,然后在此文件夹中运行:
npm init
该命令会有几个问题要问你,顺便说一下,这正是我们最终希望在空白HTML页面生成器中包含的内容。这将在文件夹中生成 package.json
文件:
我们需要创建包的 index.js
文件作为入口在package.json中引入。在这个文件中,写入下面代码:
console.log('Hello World!');
现在我们需要创建运行这段代码的命令。
{
"name": "html-generator-cli",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"bin": "index.js"
}
将最后一行添加到package.json中。现在,我们可以测试我们非常简单的CLI。在项目文件夹中局安装我们新创建的包到本机:
npm install -g ../html-generator-cli
打开一个新终端并运行:
html-generator-cli
如果您使用Windows,现在应该会看到“Hello World!”。在您的终端中。如果您使用的是基于UNIX的操作系统,则应该得到一个错误,可能与语法错误和意外的token有关。我本人用的是Mac,结果人如下
这是因为与Windows不同,基于UNIX的系统不关心文件的扩展名(此处为“.js”),因此不知道使用哪种语言。我们必须告诉系统使用Node运行脚本。为此,我们在文件的开头添加一条注释行:
#!/usr/bin/env node
console.log('Hello World!');
创建一个空白的HTML页面
我们要创建一个CLI来生成HTML文件,为此,我们将使用Node.js文件系统模块。该模块是Node内置模块,提供与文件系统交互的API,也就是说可以创建、读取、修改和删除文件。我们只需要使用文件系统模块的 writeFile
方法即可,该方法允许你创建文件。
#!/usr/bin/env node
const fs = require('fs');
const html = `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Title</title>
</head>
<body>
</body>
</html>`;
fs.writeFile('index.html', html, error => {
if (error) {
console.log(error);
}
});
如果您再次在终端中保存并运行 html-generator-cli
,现在应该在文件夹中看到一个 index.html
文件。
将参数传递给代码
现在我们生产的文件名和HTML中的 title
标签内容是写死的,我们应该可以将文件名和标题作为参数传递给CLI。要传递参数,你只需在命令之后写上参数,然后这些参数就可以在一个名为 argv
的变量中提供给进程。
在代码中编写如下代码:
const args = process.argv;
console.log(args);
并在终端中运行它:
html-generator-cli hello haha
然后,你应该在控制台中看到一个包含参数作为字符串的数组:
传递的参数在数组的最后两项,我们只需要使用数组的 slice(2)
方法即可拿到。我们决定第一个输入参数是文件名(不带HTML扩展名),第二个参数将是HTML页面的标题。这些参数都不是必需的,如果没有提供名称和标题,则我们将文件称为index.html,标题为“Title”。
#!/usr/bin/env node
const fs = require('fs');
const args = process.argv.slice(2);
let fileName = args[0] ? `${args[0]}.html` : 'index.html';
let title = args[1] || 'Title';
const html = `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>${title}</title>
</head>
<body>
</body>
</html>`;
fs.writeFile(fileName, html, error => {
if (error) {
console.log(error);
}
});
我们保持简单,不验证用户输入的情况,用户可能会给该文件指定了无效的名称,这是你在实际工作中必须验证的内容。
现在,你可以在终端中尝试以下操作:
html-generator-cli page "new generator"
结果
使用参数选项
先前的方法易于实现,但有一些缺点:用户必须知道期望哪些参数以及以什么顺序。如果他不想给出文件名,他也没有办法给出标题,我们可以通过创建选项来改善这一点。
与其一个接一个地写参数,我们可以构建我们的CLI,让用户输入类似于这样的文件名和/或标题。
html-generator-cli --file-name page --html-title "new generator"
写起来有点长,但是用户更清楚他给出的参数是什么,顺序不再起作用,你可以给出一个标题,即使你没有给出任何文件名。
const args = process.argv;
const FILE_NAME_OPTION = '--file-name';
const HTML_TITLE_OPTION = '--html-title';
const fileNameOptionIndex = args.findIndex(arg => arg === FILE_NAME_OPTION);
const htmlTitleOptionIndex = args.findIndex(arg => arg === HTML_TITLE_OPTION);
const fileNameOption = fileNameOptionIndex > -1 && args[fileNameOptionIndex + 1];
const titleOption = htmlTitleOptionIndex > -1 && args[htmlTitleOptionIndex + 1];
let fileName = fileNameOption ? `${fileNameOption}.html` : 'index.html';
let title = titleOption || 'Title';
现在,我们在参数数组 args
中获得选项 --file-name
或 --html-title
的索引。如果存在一个选项,那么要给文件名或标题的值就是参数数组中 --file-name
或 --html-file
之后的元素。如果不存在选项,则其索引将为 -1
。如果此索引为 -1
或参数数组中该选项之后没有任何值,我们分别为文件名或标题提供默认值。其余代码未更改。
你可以运行新的CLI,如果没有选择,它将创建标题为“Title”的index.html文件。如果你编写一个选项但忘记提供一个值,它将也提供默认值。如果你正确地使用给定的选项编写命令,那么它应该创建一个具有正确名称和正确HTML标题的文件。
html-generator-cli --file-name hello --html-title "CLI helloworld!"
效果
同样,在实际的CLI中,你会希望多检查一些输入,首先要确保用户输入的值是有效的,但也要在缺失值或选项出现两次的情况下警告他们。
向用户询问参数
使用选项已经是一种改进了,但是它仍然需要用户知道他可以传递什么参数以及使用哪个标记。当你初始化你的npm项目时,你可以通过很多东西作为选项。CLI会直接问您一些问题,因此您无需阅读文档即可了解如何提供项目名称,版本等信息。
要从控制台读取用户输入,我们需要Node(自版本7)提供的模块 readline
。你可以使用以下代码在终端中对其进行测试:
const readline = require('readline');
const interface = readline.createInterface({
input: process.stdin,
output: process.stdout
});
interface.question('你叫什么名字?', answer => {
console.log(`Hello ${answer}`);
interface.close();
});
效果如下
为了生成我们的HTML页面,我们首先要询问文件名,然后询问标题。如果用户没有输入任何内容,我们将获得默认值。我们向用户显示默认值是什么,以便在默认值正确的情况下可以跳过该问题。
#!/usr/bin/env node
const fs = require('fs');
const readline = require('readline');
let fileName = 'index.html';
let title = 'Title';
const interface = readline.createInterface({
input: process.stdin,
output: process.stdout
});
interface.question(`File name (${fileName}): `, answer => {
if (answer && answer.length) {
fileName = `${answer}.html`;
}
interface.question(`HTML title (${title}): `, answer => {
if (answer && answer.length) {
title = answer;
}
interface.close();
const html = `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>${title}</title>
</head>
<body>
</body>
</html>`;
fs.writeFile(fileName, html, error => {
if (error) {
console.log(error);
}
});
});
});
如果你在终端中运行它,将会询问两个问题。
用户不必了解您的CLI选项,所有重要的事情都可以直接询问。但是,你应该只以这种方式询问主要配置问题,并让用户阅读文档以了解不太常见的选项。
结束
我们使用Node和npm创建了一个简单的CLI,允许用户生成一个空白的HTML文件,是不是非常简单?你可以通过添加新选项并验证用户输入来改进此示例。
文章首发《前端外文精选》微信公众号
继续阅读其他高赞文章
- 2020年的12个Vue.js开发技巧和窍门
- 【小技巧】CSS如何实现文字两端对齐效果?
- 7个简单但棘手的JavaScript面试问题
- 让你可以在2020年成为前端大师的9个项目
- 【实战】Vue.js 图标选择组件开发
- 你必须知道的HTTP基本概念
- 【笔记】Web全栈工程师的自我修养(上)
- 【笔记】Web全栈工程师的自我修养(下)
- 【小技巧】H5页面上如何禁止手机虚拟键盘弹出?
- 拒绝JavaScript,这三个CSS技巧你一定用的上
- 7个能提高你生产力的隐藏Chrome DevTools功能
- 【图文教程】同步你的VSCode设置及扩展插件,换机不用愁
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。