创建项目
- 新建文件夹 mycli_demo
- 在文件夹 mycli_demo 内新建文件 package.json
{
"name": "mycli_demo",
"version": "1.0.0",
"bin": {
"mycli": "index.js"
},
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"fs-extra": "^11.3.0",
"prompts": "^2.4.2"
}
}
- 新建文件 templates\react\package.json
{
"name": "your-project-name",
"version": "1.0.0",
"description": "react + ts + vite 项目模板",
"main": "index.js",
"scripts": {
"dev": "vite"
},
"keywords": [],
"author": "",
"license": "ISC"
}
- 新建文件 templates\vue\package.json
{
"name": "your-project-name",
"version": "1.0.0",
"description": "vue + ts + vite 项目模板",
"main": "index.js",
"scripts": {
"dev": "vite"
},
"keywords": [],
"author": "",
"license": "ISC"
}
- 新建文件 index.js
#!/usr/bin/env node
// prompts 用于与用户进行交互
const prompts = require("prompts");
// fs-extra 用于文件操作
const fse = require("fs-extra");
// path 用于处理文件路径
const path = require("path");
// 定义项目模板路径
const templateDir = path.join(__dirname, "templates");
// 定义交互问题
const questions = [
{
type: "select",
name: "projectType",
message: "请选择项目类型:",
choices: [
{ title: "vue", value: "vue" },
{ title: "react", value: "react" },
],
initial: 0,
},
{
type: "text",
name: "projectName",
message: "请输入项目名称:",
validate: (value) => (value.trim() === "" ? "项目名称不能为空" : true),
},
{
type: "select",
name: "packageManager",
message: "请选择包管理器:",
choices: [
{ title: "npm", value: "npm" },
{ title: "yarn", value: "yarn" },
],
initial: 0,
},
];
async function createProject() {
try {
// 发起交互,并获取用户输入
const answers = await prompts(questions);
// 解析用户输入的项目信息
const { projectType, projectName, packageManager } = answers;
// 根据用户输入的项目名称,生成新项目目录。 process.cwd() 用于获取当前工作目录的路径
const projectDir = path.join(process.cwd(), projectName);
// 检查新项目目录是否已存在
if (fse.existsSync(projectDir)) {
console.error(`项目目录 ${projectDir} 已存在,请选择其他名称。`);
return;
}
// 创建项目目录
fse.ensureDirSync(projectDir);
// 根据用户选择,获取目标模板路径
const templatePath = path.join(templateDir, projectType);
// 复制模板目录到目标目录
fse.copySync(templatePath, projectDir);
// 根据用户选择更新项目配置
const packageJsonPath = path.join(projectDir, "package.json");
// 读取 package.json 文件
const packageJson = fse.readJsonSync(packageJsonPath);
// 更新 package.json 中的项目名称
packageJson.name = projectName;
// 保存更新后的 package.json
fse.writeJsonSync(packageJsonPath, packageJson, { spaces: 2 });
console.log(`项目 ${projectName} 创建成功!`);
console.log(`请进入项目目录:cd ${projectName}`);
console.log(`使用 ${packageManager} 安装依赖:${packageManager} install`);
} catch (error) {
if (error.name === "AbortError") {
console.log("用户取消了项目创建。");
} else {
console.error("创建项目时发生错误:", error);
}
}
}
// 执行创建项目函数
createProject();
安装依赖
pnpm i
或更新到最新版依赖
pnpm i prompts fs-extra
将本地包链接到全局
主要是为了方便本地调试包
npm link --force
此命令会强制将 mycli 链接到全局 node_modules 目录中,同时在全局 bin 目录中根据 package.json 中定义的 bin 字段创建对应的可执行文件
( --force 会强制覆盖已存在的 mycli 链接和可执行文件)
效果预览
命令行中输入 mycli 回车,便会开启交互,最终根据既定模板完成项目的创建。
{
"name": "reactDemo",
"version": "1.0.0",
"description": "react + ts + vite 项目模板",
"main": "index.js",
"scripts": {
"dev": "vite"
},
"keywords": [],
"author": "",
"license": "ISC"
}
更多交互命令
参考 prompts 官网 https://www.npmjs.com/package/prompts
脚手架开发的意义和原理
脚手架的系统管理和发布