从零开发一个 Node.js CLI 程序
在进行 Node.js 开发时,我们常常会用到一些命令行界面(CLI)工具。例如 Vite:
$ npm create vite@latest
Need to install the following packages:
create-vite@4.4.1
Ok to proceed? (y)
√ Project name: ... vite-project
√ Select a framework: » Vue
√ Select a variant: » TypeScript
Scaffolding project in path/to/vite-project...
Done. Now run:
cd vite-project
npm install
npm run dev
静态博客建站工具 Hexo:
$ hexo init my-site
INFO Cloning hexo-starter https://github.com/hexojs/hexo-starter.git
INFO Install dependencies
INFO Start blogging with Hexo!
这些工具能够极大地简化我们的配置工作或是给我们提供一个方便的接口来调用别人写好的库。在这篇文章中,我们将充分利用 Node.js 的生态系统,从零开始编写一个简单易用、具有高拓展性的静态网站生成器(SSG)。我们将会涉及到以下内容:
- 使用 TypeScript 和 ESLint 规范我们的代码
- 通过命令调用我们的 Node.js 代码
- 简单的命令参数分析与处理
- 使用
npm create
命令来调用我们的程序 - 将我们的包发布到 npm 以供更多人使用
如果读者对于如何编写一个静态网站生成器感兴趣,也可以继续查阅后面的章节,了解如何一步步构建出一个简单快速、高拓展性的静态网站生成器,我们将会涉及到以下内容:
- 一个基本的静态网站生成器的基本要素
- 数据读取、配置加载、模板管理和主题继承
- 通过
fs.watch()
实现热重载功能 - 使用
Promise
实现异步渲染管线 - 通过
ES Module
实现插件拓展功能
最终的静态网站生成器成品已经发布到了 npm 上,并且已经基本具备了一个完整的静态网站生成器的各种功能。在此欢迎各位读者参考与使用:Vivia。
初始化项目
首先为我们的 CLI 项目创建一个文件夹。在本文中,我们使用 npm 作为我们的包管理器。
$ mkdir vivia
$ cd vivia
$ npm init -y
Wrote to path/to/vivia/package.json:
{
"name": "vivia",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
配置 TypeScript:
$ tsc --init --module es2022
Created a new tsconfig.json with:
target: es2016
module: es2022
strict: true
esModuleInterop: true
skipLibCheck: true
forceConsistentCasingInFileNames: true
You can learn more at https://aka.ms/tsconfig
若读者之前没有安装过 TypeScript,可以先使用下面的命令全局安装 TypeScript 后,再执行上面的命令初始化 TypeScript。
$ npm i typescript -g
配置 ESLint:
$ npm init @eslint/config
√ How would you like to use ESLint? · style
√ What type of modules does your project use? · esm
√ Which framework does your project use? · none
√ Does your project use TypeScript? · No / Yes
√ Where does your code run? · node
√ How would you like to define a style for your project? · guide
√ Which style guide do you want to follow? · standard-with-typescript
√ What format do you want your config file to be in? · JSON
Checking peerDependencies of eslint-config-standard-with-typescript@latest
The config that you've selected requires the following dependencies:
eslint-config-standard-with-typescript@latest @typescript-eslint/eslint-plugin@^6.4.0 eslint@^8.0.1 eslint-plugin-import@^2.25.2 eslint-plugin-n@^15.0.0 || ^16.0.0 eslint-plugin-promise@^6.0.0 typescript@*
√ Would you like to install them now? · No / Yes
√ Which package manager do you want to use? · npm
Installing eslint-config-standard-with-typescript@latest, @typescript-eslint/eslint-plugin@^6.4.0, eslint@^8.0.1, eslint-plugin-import@^2.25.2, eslint-plugin-n@^15.0.0 || ^16.0.0 , eslint-plugin-promise@^6.0.0, typescript@*
up to date, audited 219 packages in 8s
95 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
Successfully created .eslintrc.json file in path/to/vivia
随后,打开 tsconfig.json
,指定要编译的 TypeScript 文件在 src
目录下,并将编译结果输出到 dist
文件夹:
"outDir": "./dist",
"include": [
"src/**/*"
]
再打开 .eslintrc.json
,配置使用 tsconfig.json
中的规则进行解析。
"parserOptions": {
...
"project": "./tsconfig.json"
},
现在,让我们创建一个用于存放源代码的 src
文件夹,在其中新建一个 cli.js
文件作为程序的入口,并写入以下内容:
console.log('Hello Node.js CLI!')
现在,我们可以编译并使用 node
命令执行我们 JavaScript 代码了:
$ tsc --build
$ node ./dist/main.js
Hello Node.js CLI!
编辑 package.json
,写入以下内容:
"scripts": {
"dev": "tsc --watch"
}
现在,我们可以通过执行 npm run dev
来实时编译我们的代码了。如果读者使用 VSCode 来开发项目,还可以右键资源管理器,勾选“npm 脚本”选项来快速查看和运行 package.json
中定义的所有脚本。读者还可以选择安装 ESLint、Error Lens 来获得更直观的错误提示,并使用 Prettier-Standard 一键格式化代码为 standard 风格。
通过命令运行程序
作为一个 Node.js CLI 程序,首先应该能像 ls
、cat
等命令一样直接在控制台运行,而不是使用 node <filename>
这种笨拙的方式。继续编辑 package.json
,并加入以下内容:
"bin": {
"vivia": "dist/cli.js"
},
其中,bin
的每一键都代表一个命令,而其对于的值则是实际要执行的 JavaScript 文件位置。
然后,运行 npm i -g
,全局安装本项目。此时如果读者尝试直接执行 vivia
命令的话,会得到一个 Windows Script Host
的错误。显然,Windows 将我们的 cli.js
文件误以为成了 Microsoft JScript 文件了。实际上,我们应该使用 node
来执行。我们可以通过在 cli.ts
文件开头加上
#!/usr/bin/env node
来告诉操作系统使用 node
来执行我们的脚本。
现在,重新编译文件,再次尝试运行 vivia
命令,可以发现程序已经能正常运行了。
$ vivia
Hello Node.js CLI!