上一篇文章:实战 | 从零开始使用JavaScript制做本身的命令行(CLI工具) 中介绍了如何从零开始制做CLI,只是一个简单的示例,今天更进一步,来制做一个实际的程序,生成HTML模板。javascript
在这篇文章中,咱们将构建一个简单的CLI,容许用户生成HTML页面。咱们首先要生成一个标准的空白页面,而后让用户输入参数,好比文件名和标题,先经过选项,而后经过提示问题让用户输入参数。html
建立用于编写CLI的文件夹。我将其命名为 html-generator-cli。打开一个终端,而后在此文件夹中运行:前端
npm init
该命令会有几个问题要问你,顺便说一下,这正是咱们最终但愿在空白HTML页面生成器中包含的内容。这将在文件夹中生成 package.json
文件:java
咱们须要建立包的 index.js
文件做为入口在package.json中引入。在这个文件中,写入下面代码:node
console.log('Hello World!');
如今咱们须要建立运行这段代码的命令。web
{ "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
打开一个新终端并运行:shell
html-generator-cli
若是您使用Windows,如今应该会看到“Hello World!”。在您的终端中。若是您使用的是基于UNIX的操做系统,则应该获得一个错误,可能与语法错误和意外的token有关。我本人用的是Mac,结果人以下npm
这是由于与Windows不一样,基于UNIX的系统不关心文件的扩展名(此处为“.js”),所以不知道使用哪一种语言。咱们必须告诉系统使用Node运行脚本。为此,咱们在文件的开头添加一条注释行:json
#!/usr/bin/env node console.log('Hello World!');
咱们要建立一个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文件,是否是很是简单?你能够经过添加新选项并验证用户输入来改进此示例。
文章首发《前端外文精选》微信公众号
继续阅读其余高赞文章