【Cli大法】从0开始玩一个创建react-vite模板的cli工具
前排告示:这里只是记录一个demo,使用cli通过git clone 开源的react-vite模板一键导入到本地文件的方法
首先项目目录如下
初始化
npm init
packjson初始化
{
"name": "fe-cli",
"version": "1.0.0",
"main": "index.js",
"type": "module",// 这里一定要加module 表明当前是支持ES6模块化ES Modules 的node项目
"bin": {
"fecli": "./dist/bin/cli.js"//这里使用fecli进行npm link的绑定
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc",
"dev": "tsc -w"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"chalk": "^5.3.0",
"commander": "^12.1.0",
"download-git-repo": "^3.0.2",
"fs-extra": "^11.2.0",
"inquirer": "^12.0.1",
"ora": "^8.1.1"
},
"devDependencies": {
"@types/fs-extra": "^11.0.4",
"@types/node": "^22.9.0",
"typescript": "^5.6.3"
}
}
同时配置tsconfig.json
{
"compilerOptions": {
"target": "ES2018",
"module": "ESNext",//当前是Esmodule
"outDir": "./dist",
"rootDir": "./",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node"
},
"include": ["bin/**/*", "lib/**/*","src/**/*","src/types/*.d.ts"],// 对这些文件夹下的文件进行ts类型处理
"exclude": ["node_modules"]
}
创建用于创建cli的文件(bin/cli.ts)
#!/usr/bin/env node 这一行命令必须要加,要不然fecli执行的时候会出错
/*
* @Date: 2024-11-11 20:23:12
* @Description:
*/
import { Command } from "commander";
import create from "../lib/create.js";
console.log('CLI 启动');
console.log('命令行参数:', process.argv);
const program = new Command();
program
.version("1.0.0")
.command("create <project-name>")
.description("create a new project")
.action(async (name) => {
try {
console.error('fdfdfd:');
await create(name);
} catch (error) {
console.error('执行出错:', error);
}
});
console.log('已注册的命令:', program.commands.map(cmd => cmd.name()));
program.parse(process.argv);
创建用户交互cli界面(lib/create.ts)
#!/usr/bin/env node
/*
* @Date: 2024-11-11 20:26:43
* @Description:
*/
import fs from "fs-extra";
import * as path from "path";
import chalk from "chalk";
import inquirer from "inquirer";
import TemplateDownloader from '../download/download.js';
/**
* @description: cli创建项目
* @param {string} projectName
* @return {*}
*/
const createCli = async (projectName: string) => {
try {
const answers = await inquirer.prompt([
{
type: "input",
name: "projectName",
message: "please input project name",
},
{
type: "list",
name: "framework",
message: "please choose framework:",
choices: ["React", "Vue"],
},
{
type: "list",
name: "buildTool",
message: "please choose build tool:",
choices: ["Vite", "Webpack"],
},
{
type: "confirm",
name: "useEslint",
message: "use eslint?",
default: true,
},
]);
// 创建项目目录
const targetDir = path.join(process.cwd(), projectName);
console.log(chalk.blue(`Creating project in ${targetDir}`));
if (fs.existsSync(targetDir)) {
const { action } = await inquirer.prompt([
{
type: "list",
name: "action",
message: "the directory already exists, please choose action:",
choices: ["overwrite", "cancel"],
},
]);
console.log(chalk.blue(`${action} project...`));
}
// 根据用户选择生成项目
console.log(chalk.blue("Creating project..."));
const templatePath = await TemplateDownloader.downloadTemplate(answers.framework,
answers.buildTool);
console.log('create template path is: ',templatePath);
return true;
} catch (error) {
console.error("发生错误:", error);
throw error;
}
};
export default createCli;
创建模板下载的界面(download/download.ts)
#!/usr/bin/env node
import ora from "ora";
import path from "path";
import chalk from "chalk";
import fs from "fs-extra";
import download from "download-git-repo";
/**
* @description: 模板下载器
* @return {*}
*/
class TemplateDownloader {
private templateRegistry: Record<string, string>;
private cacheDir: string;
constructor() {
// 后续会支持更多模板
this.templateRegistry = {
"react-vite": "knakamura13/react-vite-template#main",
};
// 下载后,模板暂存的位置
this.cacheDir = path.resolve(process.cwd(), "cli-templates");
}
// 获取缓存路径
getCachePath(templateKey: string) {
return path.join(this.cacheDir, templateKey);
}
// 获取所选模板对应的key值
getTemplateKey(framework: string, buildTool: string) {
return `${framework.toLowerCase()}-${buildTool.toLowerCase()}`;
}
async downloadTemplate(framework: string, buildTool: string) {
const templateKey = this.getTemplateKey(framework, buildTool);
const templateUrl = this.templateRegistry[templateKey];
const cachePath = this.getCachePath(templateKey);
console.log("this template url is ", templateUrl);
if (!templateUrl) {
throw new Error(`Template ${templateKey} not found`);
}
const spinner = ora(`Downloading ${templateKey} template...`).start();
try {
await download(templateUrl, cachePath, (err?: Error) => {
if (err) {
spinner.fail();
throw err;
}
});
spinner.succeed("Template downloaded successfully");
return cachePath;
} catch (error) {
spinner.fail("Template download failed");
throw error;
}
}
}
export default new TemplateDownloader();
之后执行
tsc
npm link
fecli create <项目名>
之后模板代码就会放到cli-template这个文件下面
未完待续...