1,什么是CLI工具
CLI(命令行界面)工具是一种允许用户通过文本命令与计算机程序或操作系统进行交互的软件。CLI工具通常用于执行特定任务、管理系统设置、运行脚本或处理文件。与图形用户界面(GUI)相比,CLI工具通常更轻量、更快速,并且可以轻松地在不同平台上使用。CLI工具在软件开发、系统管理和自动化任务中非常常见。
在现代软件开发中,命令行界面(CLI)工具以其高效、灵活的特点深受开发者喜爱。其中,脚手架工具作为一种特定类型的CLI应用,能够快速生成项目基础结构,极大地简化开发流程,提升工作效率。本文将带领你从构思到实现,逐步构建一个自定义的CLI脚手架工具。
2,CLI工具能为我们做什么
我们在项目开发时,经常会用到一些cli工具,比如vue-cli
、npm init
等等,这些CLI工具我们常常用做项目初始化、代码检查、模板创建等交互相对简单,且重复性较多的工作。
3,开发CLI工具需要了解的前置知识
commander:
提供了用户命令行输入和参数解析的工具,具体使用可以参考我写的这篇文章
commander.js 入门指南:构建强大的命令行界面 (全网最全教程)
inquirer:
一个强大的用户与命令行交互的工具,具体使用可以参考我写的这篇文章 inquirer.js 构建交互式命令行工具,全网详细 inquirer.js中文教程
fs-extra:
fs-extra
是对标准fs
模块的扩展,它不仅包含了所有原生fs
模块的方法,而且还添加了许多实用的功能,如复制、移动、删除文件/目录等。这些方法都支持Promise,使得我们可以用同步或异步的方式轻松处理文件。具体使用可以参考我写的这篇文章,深入浅出fs-extra:Node.js文件操作的瑞士军刀
chalk:
可以实现好看的日志输出等
4,从零开始搭建一个cli工具
工具介绍:
团队使用zustand 做状态管理工具,为了简化zustand 文件的创建,我开发了这个cli工具,zustand-cli
zustand-cli
是一个命令行工具,用于快速生成 Zustand 状态管理模板。它可以帮助您轻松创建基于 JavaScript 或 TypeScript 的 Zustand 存储和使用文件。通过使用 zustand-cli
,您可以节省时间并保持项目结构的一致性。
主要功能:
- 自动生成 Zustand 存储模板(支持 JavaScript 和 TypeScript)。
- 可选生成 Zustand 使用文件(支持 JavaScript 和 TypeScript)。
- 简化状态管理文件的创建过程。
- 快速入门,无需额外配置。
使用方法:
- 在命令行中运行
zustand new
。 - 选择您要生成的模板类型(JavaScript 或 TypeScript)。
- 根据提示选择是否生成使用文件。
- 自动生成的文件将在当前目录中创建。
js
NOLANKYWU-MB1:sh nolan$ zustand new
? Which template do you want to generate? TypeScript
? Do you want to generate a usage file? Yes
Created zustandTemplateTS.ts
Created zustandUseTemplateTS.tsx



关于zustand 的介绍,有兴趣的可以看我这篇文章,Zustand 状态库:轻便、简洁、强大的 React 状态管理工具
开始
在开始之前,你需要确保你的开发环境中已经安装了Node.js和npm(Node.js的包管理器)。这是因为我们将使用Node.js来构建我们的CLI工具。
js
mkdir zustand-cli && cd zustand-cli // 创建仓库目录
npm init // 初始化 package.json
npm install -g typescript // 安装全局 TypeScript
tsc --init // 初始化 tsconfig.json
然后分别安装commander,inquirer,fs-extra,chalk等依赖
js
npm install commander inquirer fs-extra chalk
在项目文件夹中创建一个名为index.js的文件,这将是我们CLI工具的起点。 项目目录如下

其中 zustandTemplateJS.js,zustandTemplateTS.ts,zustandUseTemplateJS.jsx,zustandUseTemplateTS.tsx
都是模版文件,
./bin/index.js
文件代码如下
js
#!/usr/bin/env node
import { program } from 'commander';
import inquirer from 'inquirer';
import fs from 'fs-extra';
import chalk from 'chalk';
import path, { dirname } from 'path';
import { fileURLToPath } from 'url';
async function main() {
// 定义一个新的命令 "new",用于生成 Zustand 模板
program
.command('new')
.description('Generate a new Zustand template')
.action(async () => {
// 定义要询问用户的问题
const questions = [
{
type: 'list',
name: 'fileType',
message: 'Which template do you want to generate?',
choices: ['TypeScript', 'JavaScript'],
},
{
type: 'confirm',
name: 'generateUsageFile',
message: 'Do you want to generate a usage file?',
},
];
// 使用 inquirer 库向用户询问问题
const answers = await inquirer.prompt(questions);
const { fileType, generateUsageFile } = answers;
const isTypeScript = fileType === 'TypeScript';
// 根据用户选择的类型生成 Zustand 文件
const templateFile = isTypeScript
? 'zustandTemplateTS.ts'
: 'zustandTemplateJS.js';
const templateContent = await fs.readFile(
path.join(dirname(fileURLToPath(import.meta.url)), templateFile),
'utf-8'
);
await fs.writeFile(templateFile, templateContent);
console.log(chalk.green(`Created ${templateFile}`));
// 如果用户选择生成使用文件,则生成相应的使用文件
if (generateUsageFile) {
const usageFile = isTypeScript
? 'zustandUseTemplateTS.tsx'
: 'zustandUseTemplateJS.jsx';
const usageContent = await fs.readFile(
path.join(dirname(fileURLToPath(import.meta.url)), usageFile),
'utf-8'
);
await fs.writeFile(usageFile, usageContent);
console.log(chalk.green(`Created ${usageFile}`));
}
});
// 解析命令行参数并执行程序
program.parse(process.argv);
}
// 调用 main 函数
main();
这段代码定义了一个基础的CLI
接下来,修改你的package.json来识别CLI命令,并设置为使用ES模块:
json
"bin": {
"zustand": "./bin/index.js"
},
"type": "module"
这段代码是在
package.json
文件中定义的配置。它包含两个字段:bin
和type
。
bin
:这个字段用于指定一个或多个可执行的二进制文件,它们将在安装此 npm 包时被链接到全局或项目范围内的可执行路径。这对于创建命令行工具(CLI)非常有用。在这个例子中,
bin
字段定义了一个名为zustand
的命令行工具,它将在安装此包时链接到./bin/index.js
文件。这意味着用户可以在命令行中直接运行zustand
命令,而无需指定完整的文件路径。当用户运行zustand
时,实际上是在执行./bin/index.js
文件。
type
:这个字段用于指定项目中的模块类型。它可以是"commonjs"
(默认值)或"module"
。在这个例子中,
type
设置为"module"
,这意味着项目中的所有 JavaScript 文件都将被视为 ECMAScript 模块(ESM)。这将允许你在项目中使用 ESM 语法,例如import
和export
语句。请注意,这需要 Node.js v12.20.0 或更高版本,并且在项目中使用 ESM 语法时,你可能需要调整你的构建和测试工具配置。
通过全局链接你的项目:
js
npm link
npm link
是一个用于在本地开发和测试 Node.js 包(例如 CLI 工具)的实用命令。它允许你在本地安装和使用一个包,而无需将其发布到 npm 仓库。这样,你可以在将包发布到 npm 之前进行开发和测试。
npm link
的工作原理是创建一个符号链接(symlink),将你的包链接到全局node_modules
文件夹。这意味着你可以在任何项目中使用这个包,就像它已经被全局安装一样。要使用
npm link
,请按照以下步骤操作:
在包的根目录中运行
npm link
。这将创建一个指向包的符号链接,并将其添加到全局node_modules
文件夹中。这样,你就可以在任何项目中使用这个包了。如果你正在开发一个依赖于此包的项目,你可以在项目根目录中运行
npm link <package-name>
。这将在项目的node_modules
文件夹中创建一个指向全局符号链接的符号链接。这样,你就可以在项目中使用这个包,就像它已经被安装在项目中一样。这种方法的优点是,你可以在不同的项目中使用同一个本地包,并在开发过程中立即看到更改的效果。这对于开发和测试新功能非常有用。
请注意,当你完成开发并准备好将包发布到 npm 时,你需要取消链接。要做到这一点,你可以在包的根目录中运行
npm unlink
。如果你在项目中链接了这个包,你还需要在项目根目录中运行npm unlink <package-name>
,然后安装发布到 npm 的包版本,例如npm install <package-name>
。
5,发布npm包
发布一个 npm 包涉及以下几个步骤:
-
创建一个 npm 账户 :首先,你需要在 npmjs.com 上创建一个账户(如果你还没有的话)。
-
登录到 npm :在你的命令行中运行
npm login
,然后输入你的 npm 用户名、密码和电子邮件地址。这将使你的本地开发环境与你的 npm 账户相关联。 -
准备你的项目:确保你的项目具有以下内容:
- 一个
package.json
文件,其中包含项目的名称、版本、描述、入口点(主文件)等信息。 - 一个
.npmignore
文件(可选),用于指定在发布时应排除的文件。如果没有这个文件,npm 会根据.gitignore
文件进行排除。
- 一个
-
设置包名称和版本 :在
package.json
文件中设置你的包名称和版本。确保名称是唯一的,没有在 npm 上被其他包使用。版本应遵循 语义化版本(Semantic Versioning) 规范。 -
构建和测试你的包:在发布之前,确保你的包已经通过了所有测试,并已准备好发布。运行项目的构建脚本(如果有的话)以确保一切就绪。
-
发布包 :在你的项目根目录中运行
npm publish
。这将把你的包上传到 npm 仓库,并使其可供其他人安装和使用。 -
更新和维护你的包 :在开发过程中,你可能需要发布新的版本。每次发布新版本时,请确保更新
package.json
文件中的版本号(遵循语义化版本规范),然后再次运行npm publish
。
注意:如果你的包包含了原生模块或需要在发布前进行特定的构建步骤,请查看 npm 文档,以了解更多关于编译、构建和发布原生模块的详细信息。
我项目中的package.json 文件如下
json
{
"name": "@tencent/zustand-cli",
"version": "1.0.5",
"description": "CLI for generating zustand state management files",
"main": "./bin/index.js",
"type": "module",
"keywords": [
"zustand",
"redux",
"React",
"state management",
"state",
"management",
"cli"
],
"scripts": {
"start": "node bin/index.js"
},
"author": "nolankywu",
"bin": {
"zustand": "./bin/index.js"
},
"license": "ISC",
"dependencies": {
"chalk": "^5.3.0",
"commander": "^12.0.0",
"fs-extra": "^11.2.0",
"inquirer": "^8.2.6",
"keypress": "^0.2.1",
"zustand": "^4.5.2"
}
}
在登录npm后执行 npm publish 就可以把包发布到npm上啦
完整项目在我的GitHub上,欢迎fork:
npm 包的地址
欢迎使用