动手搭建cli脚手架
项目搭建
新建如下目录结构
cli目录结构
下面贴上具体文件代码
/bin/cli.js
#! /usr/bin/env node
// #! 符号的名称叫 Shebang,用于指定脚本的解释程序
// Node CLI 应用入口文件必须要有这样的文件头
// 如果是Linux 或者 macOS 系统下还需要修改此文件的读写权限为 755
// 具体就是通过 chmod 755 cli.js 实现修改
// 用于检查入口文件是否正常执行
console.log("开始运行my-cli脚手架进行构建");
const { program } = require("commander");
const figlet = require("figlet");
program
// 定义命令和参数
.command("create <app-name>")
.description("创建一个新项目")
// -f or --force 为强制创建,如果创建的目录存在则直接覆盖
.option("-f, --force", "强制创建,如果创建的目录存在则直接覆盖")
.action((name, options) => {
// 在 create.js 中执行创建任务
require("../lib/create.js")(name, options);
});
program
// 配置版本号信息
.version(`v${require("../package.json").version}`)
.usage("<command> [option]");
program.on("--help", () => {
// 使用 figlet 绘制 Logo
console.log(
"\r\n" +
figlet.textSync("my-cli", {
font: "Standard",
horizontalLayout: "default",
verticalLayout: "default",
width: 80,
whitespaceBreak: true,
})
);
});
// 解析用户执行命令传入参数
program.parse(process.argv);
/lib/Animate.js
const ora = require("ora");
class Animate {
constructor(initText) {
this.initText = initText;
this.spinner = ora(initText);
}
start(text) {
this.spinner.start(text);
}
change(text, color) {
// color: 'black' | 'red' | 'green' | 'yellow' | 'blue' | 'magenta' | 'cyan' | 'white' | 'gray' | boolean
// 修改动画样式
this.spinner.text = text;
this.spinner.color = color || "cyan";
}
stop() {
this.spinner.stop(); // 停止
}
message(msg, type) {
// type: succeed✔ | fail✖ | warn⚠ | infoℹ
this.spinner[type](msg);
}
}
module.exports = Animate;
/lib/create.js
const path = require("path");
const fs = require("fs-extra");
const inquirer = require("inquirer");
const Generator = require("./Generator");
const ejs = require("ejs");
module.exports = async function (name, options) {
// 执行创建命令
// 当前命令行选择的目录
const cwd = process.cwd();
// 需要创建的目录地址
const targetPath = path.join(cwd, name);
// 目录是否已经存在?
if (fs.existsSync(targetPath)) {
// 是否为强制创建?
if (options.force) {
await fs.remove(targetPath);
} else {
// 询问用户是否确定要覆盖
let { action } = await inquirer.default.prompt([
{
type: "confirm",
name: "action",
message: "目标目录已存在,是否覆盖:",
},
]);
if (!action) {
return;
} else if (action === true) {
// 移除已存在的目录
console.log(`\r\n移除目录中...`);
await fs.remove(targetPath);
// 移除已存在的目
console.log(`\r\n移除目录成功!`);
}
}
}
// 创建项目
const generator = new Generator(name, targetPath);
// 开始创建项目
generator.create();
};
/lib/Generator.js
const inquirer = require("inquirer");
const fs = require("fs");
const path = require("path");
const Animate = require("./Animate");
const figlet = require("figlet");
class Generator {
constructor(name, targetPath) {
// 目录名称
this.name = name;
// 创建位置
this.targetPath = targetPath;
// gis目录
this.gisList = [];
// 目标package.json路径
this.packageJsonPath = path.resolve(this.targetPath, "./package.json");
this.frameTemplate = "";
this.useRuoyi = false;
this.useGis = false;
}
// 核心创建逻辑
async create() {
console.log('\r\n开始构建项目...')
await this.question();
this.useAnimate();
await this.copyTemplate();
await this.handleGis();
}
// 指引提问
async question() {
const { frameTemplate, useRuoyi, useGis } = await inquirer.default.prompt([
{
type: "list",
name: "frameTemplate",
message: "请选择框架",
choices: ["Vue3", "Vue2"],
},
{
type: "confirm",
name: "useRuoyi",
message: "是否使用若依框架",
},
{
type: "confirm",
name: "useGis",
message: "是否使用地图组件",
},
]);
this.frameTemplate = frameTemplate;
this.useRuoyi = useRuoyi;
this.useGis = useGis;
// 使用地图则选择23维
if (this.useGis) {
const res = await inquirer.default.prompt([
{
type: "checkbox",
name: "gisList",
message: "请选择要使用的地图框架",
choices: ["macrogis", "macroglobe"],
},
]);
this.gisList = res.gisList;
}
}
// 使用动画
async useAnimate() {
// 开始构建项目
const animate = new Animate();
animate.start("开始复制模板代码...");
setTimeout(() => {
animate.change("开始搭建依赖...");
setTimeout(() => {
// 构建结束
animate.stop();
animate.message("构建成功!", "succeed");
console.log(
"\r\n" +
figlet.textSync("my-cli", {
font: "Standard",
horizontalLayout: "default",
verticalLayout: "default",
width: 80,
whitespaceBreak: true,
})
);
console.log("\r\n如何使用:");
console.log(`cd ./${this.name}`);
console.log("npm i");
console.log("npm run dev");
}, 3000);
}, 4000);
}
// 复制模板
async copyTemplate() {
// 设置原名称
let templateName = "";
if (this.useRuoyi && this.frameTemplate == "Vue3") {
templateName = "ruoyi-vue3";
} else if (this.useRuoyi && this.frameTemplate == "Vue2") {
templateName = "ruoyi-vue2";
} else if (!this.useRuoyi && this.frameTemplate == "Vue3") {
templateName = "vite-vue3";
} else if (!this.useRuoyi && this.frameTemplate == "Vue2") {
templateName = "webpack-vue2";
}
// 设置源目录
const sourcePath = path.resolve(__dirname, `../templates/${templateName}`);
// 复制目录
const err = await fs.cpSync(sourcePath, this.targetPath, {
recursive: true,
});
if (err) {
console.error(err);
return;
}
}
// 处理gis
async handleGis() {
// 如果选择了gis
if (this.gisList.includes("macrogis")) {
//写入package.json
const data = await fs.readFileSync(this.packageJsonPath);
if (!data) {
console.error("读取json文件失败!");
return;
}
const jsonData = JSON.parse(data);
// 固定稳定版本1.3.6
jsonData.dependencies.macrogis = "1.3.6";
const err = await fs.writeFileSync(
this.packageJsonPath,
JSON.stringify(jsonData, null, 4)
);
if (err) {
console.error("写入json文件失败!");
return;
}
// 复制到node_modules
const gisPath = path.resolve(__dirname, `../templates/macrogis`);
const modulePath = path.resolve(
this.targetPath,
"./node_modules/macrogis"
);
fs.cpSync(gisPath, modulePath, { recursive: true });
}
// 如果选择了globe
if (this.gisList.includes("macroglobe")) {
//写入package.json
const data = await fs.readFileSync(this.packageJsonPath);
if (!data) {
console.error("读取json文件失败!");
return;
}
const jsonData = JSON.parse(data);
// 固定稳定版本1.3.4
jsonData.dependencies.macroglobe = "1.3.4";
const err = await fs.writeFileSync(
this.packageJsonPath,
JSON.stringify(jsonData, null, 4)
);
if (err) {
console.error("写入json文件失败!");
return;
}
// 复制到node_modules
const globePath = path.resolve(__dirname, `../templates/macroglobe`);
const modulePath = path.resolve(
this.targetPath,
"./node_modules/macroglobe"
);
fs.cpSync(globePath, modulePath, { recursive: true });
}
}
}
module.exports = Generator;
package.json
{
"name": "my-cli",
"version": "1.0.0",
"description": "cli",
"main": "./bin/cli.js",
"bin": {
"my-cli": "./bin/cli.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"commander": "^14.0.0",
"ejs": "^3.1.10",
"figlet": "^1.8.2",
"fs-extra": "^11.3.0",
"inquirer": "^12.9.0",
"ora": "^4.1.1"
}
}














网友评论