Node.js 开发 cli 工具
一、为什么需要 cli 工具
我们见识过了很多的cli工具,例如 vue-cli、create-react-app、angular/cli等。它让开发者摆脱了wepack复杂繁琐的配置,快速、个性化地搭建项目,从而更好地专注业务层面的开发。
二、起步
cli的开发思路是复制拷贝,其实与拷贝git仓库无异。
创建一个文件夹,运行
npm init -y
cnpm i arg chalk esm inquirer listr lodash ncp pkg-install --save
上面是我们所需要的依赖,以下是各个安装包的说明
arg : 解析cli 命令行的参数
chalk : 让控制台打印颜色
esm : 这个包可以通过装饰 require,从而可以在模块中使用最新的ECMAScript语法
inquirer : 控制台交互式命令集合
lodash : JavaScript 实用工具库
ncp : 拷贝文件使用库
pkg-install : 用于在生成的项目中安装依赖
编辑 package.json
{
"name": "zdxhxh-cli",
"version": "0.0.1",
"description": "cli 工具",
"main": "src/main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"license": "ISC",
"bin": {
"xh-cli": "bin/create-project.js"
},
"author": {
"name": "xh"
},
"keywords": [
"cli",
"create-project"
],
"files": [
"bin/",
"src/",
"templates/"
]
}
说明 :
- bin : 用来指定内部命令对应可执行文件的位置,当全局安装时这个包时,由于
node_modules/.bin/
目录会在运行时,加入系统的PATH环境变量,所以在命令行行中直接通过命令来调用这些脚本- files : 用来指定那些目录会在发布时,上传到NPM服务器上。这里我们需要
bin、src、template
这几个目录
按照以下目录创建文件
├─bin
└─ create-project.js
├─src
| └─cli.js
| └─main.js
│ └─utils
| └─parseArgumentsIntoOptions.js
| └─promptForMissingOptions.js
└─templates
三、编码
在bin/create-project.js
中 ,当我们运行 xh-cli
时,就会执行该文件
#!/usr/bin/env node
// 这样包装后,引入的模块文件可以使用最新的 ES语法
require = require("esm")(module /*, options*/);
// 属性会返回一个数组,其中包含当 Node.js 进程被启动时传入的命令行参数
require("../src/cli").cli(process.argv);
接下来的目标是 : 解析命令行参数。
在src/utils/parseArgumentsIntoOptions.js
导出方法,代码如下
import arg from "arg";
import get from "lodash/get";
// 解析命令行参数 rawArgs即 process.argv
export default function (rawArgs) {
const args = arg(
{
"--git": Boolean,
"--yes": Boolean,
"--install": Boolean,
"--version": Boolean,
"-y": "--yes",
"-i": "--install",
"-v": "--version",
},
{
// process.argv除了前两个,其余的元素才是额外的命令行参数
argv: rawArgs.slice(2),
}
);
return {
skipPrompts: get(args, "--yes", false), // 快速跳过配置
version : get(args, "--version", false),
template: args._[0],
runInstall: get(args, "--install", false), // 拷贝后自动安装依赖
};
}
接下来的目标是 : 在没有传入默认的语言参数下,让用户在命令行中选择参数.
在src/utils/promptForMissingOptions.js
导出方法,代码如下
import inquirer from "inquirer";
import get from "lodash/get";
export default async function promptForMissingOptions(options) {
if (options.skipPrompts) {
return {
...options,
template: get(options, "template", "javascript")
};
}
const questions = [];
// 如果没有指定
if (!options.template) {
questions.push({
type: "list",
name: "template",
message: "请选择壳工程语言",
choices: ["javascript", "typescript"],
default: "javascript"
});
}
const answers = await inquirer.prompt(questions);
return {
...options,
template: get(options, "template", answers.template)
};
}
在src/cli.js
中导出 cli 方法
import parseArgumentsIntoOptions from "./utils/parseArgumentsIntoOptions";
import promptForMissingOptions from "./utils/promptForMissingOptions";
import get from "lodash/get";
import pjson from "../package.json";
import chalk from "chalk";
export async function cli(args) {
let options = parseArgumentsIntoOptions(args);
// 打印版本信息
if (get(options, "version")) {
return console.log(chalk.green(`当前cli版本为 : ${pjson.version}`));
}
options = await promptForMissingOptions(options);
console.log(options);
}
在命令行执行以下命令
npm link
xh-cli
则会出现命令行交互
image-20201225105156614.png接下来的目标 : 执行上述命令交互后,根据所选项将项目复制到当前执行命令的路径上。
我们在src/main.js
中,增加以下代码
import chalk from "chalk";
import fs from "fs";
// 用于递归复制文件
import ncp from "ncp";
import path from "path";
import { promisify } from "util";
import Listr from "listr";
import { projectInstall } from "pkg-install";
import get from "lodash/get";
// 递归拷贝文件
const copy = promisify(ncp);
async function copyTemplateFiles(options) {
return copy(options.templateDirectory, options.targetDirectory, {
// 是否覆盖原有内容
clobber: false
});
}
export async function createProject(options) {
options = {
...options,
targetDirectory: get(
options,
"targetDirectory",
path.resolve(process.cwd(), get(options, "template"))
)
};
const currentFileUrl = import.meta.url;
const templateDir = path.resolve(
new URL(currentFileUrl).pathname.replace(/^\//, ""),
"../../templates",
options.template.toLowerCase()
);
options.templateDirectory = templateDir;
const tasks = new Listr([
{
title: "拷贝中",
task: () => copyTemplateFiles(options)
},
{
title: "安装依赖",
task: () =>
projectInstall({
cwd: options.targetDirectory
}),
skip: () =>
!options.runInstall
? "Pass --install to automatically install dependencies"
: undefined
}
]);
await tasks.run();
console.log("%s Project ready", chalk.green.bold("DONE"));
return true;
}
在 src/cli.js
中增加以下代码
import { createProject } from "./main";
export async function cli(args) {
// ...
await createProject(options);
}
这样,cli工具的雏形已经完成了。
四、运行
在template
创建typescript
、javascript
两个文件夹,并各自创建README.md
文件
├─javascript
└─typescript
在任意目录下执行
xh-cli
选择任意一个,你看是不是复制成功了呢 ?
image-20201225110252405.png当然,你通过以下命令在拷贝项目成功后添加依赖
xh-cli --install
最终,发布到npm 中
npm publish
网友评论