UNPKG

bd-admin

Version:

一款能根据需求快速配置vue后台管理的脚手架

336 lines (335 loc) 12.8 kB
import inquirer from "inquirer"; import fs from "fs-extra"; import chalk from "chalk"; import { join, dirname, extname } from "path"; import { createFolder } from "../../utils/index.js"; import { questionsList } from "./utils/index.js"; import { ConvertVue } from "./utils/convertVue.js"; import { createViteConfig } from "./utils/viteConfig.js"; import { createPackage } from "./utils/packageConfig.js"; import { createMain } from "./utils/main.js"; import { fileURLToPath } from "url"; import spawn from "cross-spawn"; import ora from "ora"; const message = "项目创建中。。。"; const spinner = ora(message); const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const userQuestions = (projectName) => { inquirer .prompt(questionsList) .then(async (answers) => { spinner.start(); const { checkbox, modelCheckbox } = answers; const modelCheckboxList = ["echarts", "three"]; const checkboxList = ["eslint", "i18n"]; const checkboxObj = {}; checkboxList.forEach((key) => { checkboxObj[key] = checkbox.includes(key); }); modelCheckboxList.forEach((key) => { checkboxObj[key] = modelCheckbox.includes(key); }); await createTemplates(Object.assign(Object.assign(Object.assign({}, answers), checkboxObj), { projectName })); const child = spawn("npm", ["run", "prettier"]); spinner.color = "blue"; spinner.text = "格式化代码"; child.on("close", function (code) { if (code !== 0) { console.log("\n"); console.log(chalk.cyanBright("项目创建完毕")); console.log(chalk.cyanBright(`cd ${projectName}`)); console.log(chalk.cyanBright("npm install")); spinner.stop(); // 停止 delEmptyFile(projectName); spinner.succeed("Loading succeed"); // 成功 ✔ } else { spinner.stop(); // 停止 console.log(chalk.red("构建异常")); } }); }) .catch((error) => { spinner.stop(); // 停止 if (error.isTtyError) { // Prompt couldn't be rendered in the current environment } else { // Something else went wrong } }); }; const delEmptyFile = (projectName) => { const path = join(process.cwd(), projectName); const getFiles = (getPath) => { fs.readdir(getPath, (err, files) => { if (err) throw err; files.forEach((file) => { const filedir = join(getPath, file); const stats = fs.statSync(filedir); const isDir = stats.isDirectory(); if (isDir) { fs.readdir(filedir, (cErr, cFiles) => { if (cErr) throw err; if (cFiles.length <= 0) { fs.rmdir(filedir); } else { getFiles(filedir); } }); } }); }); }; getFiles(path); }; const createTemplates = async (answers) => { const { variant, projectName } = answers; const templatePath = variant === "TypeScript" ? "project_template_ts" : "project_template"; // 从模板复制项目 await projectCopy(templatePath, projectName, answers); }; const projectCopy = async (templatePath, projectName, answers) => { const { variant, eslint } = answers; const files = await fs.readdir(join(__dirname, templatePath), { withFileTypes: true, }); const manualOperation = [ "src", "package.json", ".prettierrc", ".eslintrc_js.cjs", ".eslintrc_ts.cjs", "tsconfig.json", "tsconfig.node.json", "env.d.ts", ]; const model = { eslint: [".prettierrc"], ts: ["tsconfig.json", "tsconfig.node.json", "env.d.ts"], }; files.forEach((item) => { //兼容低版本 const ItemPath = item.path ? item.path : join(process.cwd(), "build", "commands", "create", "project_template_ts"); const path = join(ItemPath, item.name); const targetPath = join(process.cwd(), projectName, path.split(templatePath)[1]); const fileName = item.name; // 处理src下文件 if (fileName === "src") { fs.ensureDirSync(join(process.cwd(), projectName, "src")); readFileList(join(__dirname, templatePath, "src"), answers).then((fileList) => { }); } // ts if (variant === "TypeScript" && model.ts.includes(fileName)) { fs.copy(path, targetPath, (err) => { if (err) throw err; }); } // eslint if (eslint) { if (fileName === ".prettierrc") { fs.copy(path, targetPath, (err) => { if (err) throw err; }); } if (variant === "TypeScript" && fileName === ".eslintrc_ts.cjs") { fs.copy(path, targetPath.replace(".eslintrc_ts", ".eslintrc"), (err) => { if (err) throw err; }); } if (variant !== "TypeScript" && fileName === ".eslintrc_js.cjs") { fs.copy(path, targetPath.replace(".eslintrc_js", ".eslintrc"), (err) => { if (err) throw err; }); } } // 其他文件自动复制 if (manualOperation.includes(item.name)) return; fs.copy(path, targetPath, (err) => { if (err) throw err; }); }); const fileSuffix = variant === "TypeScript" ? "ts" : "js"; // 追加写入viteConfig文件 const viteConfigData = createViteConfig(answers); fs.outputFileSync(join(process.cwd(), projectName, `vite.config.${fileSuffix}`), viteConfigData); // 追加写入package文件 const createPackageData = createPackage(answers); fs.outputFileSync(join(process.cwd(), projectName, "package.json"), createPackageData); //追加main文件 const mainData = createMain(answers); fs.outputFileSync(join(process.cwd(), projectName, "src", `main.${fileSuffix}`), mainData); }; const readFileList = async (path, answers) => { const { projectName, variant } = answers; const templatePath = variant === "TypeScript" ? "project_template_ts" : "project_template"; const files = await fs.readdir(path, { withFileTypes: true }); const fileList = []; // 将当前目录下文件写入 fileWrite(path, files, projectName, templatePath, answers); // 获取目录 for (const dirent of files) { const filedir = join(path, dirent.name); const targetPath = join(process.cwd(), projectName, filedir.split(templatePath)[1]); if (dirent.isFile()) { // 处理文件 fileList.push(filedir); } else if (dirent.isDirectory()) { fs.ensureDirSync(targetPath); // 递归读取子目录中的文件 const subDirFiles = await readFileList(filedir, answers); fileList.push(...subDirFiles); } } // 当所有文件都被读取后,返回文件列表 return fileList; }; const isCopyFile = (answers, filePath) => { const file = filePath.substring(filePath.indexOf("src"), filePath.length); const { variant } = answers; const filePostfix = variant === "TypeScript" ? ".ts" : ".js"; const modelFile = { echarts: [join("src", "hooks", `userEcharts${filePostfix}`)], i18n: [ join("src", "hooks", `useI18${filePostfix}`), join("src", "lang", "en.json"), join("src", "lang", "zh-CN.json"), join("src", "lang", `lang${filePostfix}`), join("src", "layouts", "hooks", `useLang${filePostfix}`), ], three: [ join("src", "utils", "three", `index${filePostfix}`), join("src", "utils", "three", `ModelThree${filePostfix}`), join("src", "utils", "three", `SpriteThree${filePostfix}`), join("src", "utils", "three", `Three${filePostfix}`), join("src", "utils", "three", "indexdb", `index${filePostfix}`), join("src", "utils", "three", "interface", `index${filePostfix}`), join("src", "utils", "three", "utils", `index${filePostfix}`), ], }; let flag = true; for (const [key, value] of Object.entries(modelFile)) { if (!value.includes(file)) continue; if (typeof answers[key] === "boolean" && !answers[key]) { flag = false; return false; } } return flag; }; // css文件处理 const cssFile = (fileName, fromPath, toPath, answers) => { const { css } = answers; if (css === "less") { fs.copyFile(fromPath, toPath.replace("scss", "less")); } else { fs.copyFile(fromPath, toPath); } }; const fileWrite = (path, files, projectName, templatePath, answers) => { const fileList = files.filter((dirent) => dirent.isFile()); const vueFileNameList = fileList .filter((dirent) => extname(dirent.name) === ".vue") .map((item) => item.name.replace(extname(item.name), "")); fileList.forEach(() => { }); if (vueFileNameList.length === 0) { // 目录下没有vue文件 fileList.forEach((dirent) => { const filedir = join(path, dirent.name); const targetPath = join(process.cwd(), projectName, filedir.split(templatePath)[1]); // 根据模块判断是否需要复制 if (isCopyFile(answers, targetPath)) { const [name, suffix] = dirent.name.split("."); if (suffix === "scss") { cssFile(dirent.name, filedir, targetPath, answers); } else { fs.copyFile(filedir, targetPath); } } }); } else { fileList.forEach(async (item) => { const filedir = join(path, item.name); const targetPath = join(process.cwd(), projectName, filedir.split(templatePath)[1]); const fileName = item.name.replace(extname(item.name), ""); if (vueFileNameList.includes(fileName)) { // vue文件写入 if (extname(item.name) === ".vue") { // 判断vue文件是否需要写入 const flag = writeVueFile(answers, targetPath); if (flag) { const convertVue = new ConvertVue({ path, fileName, answers, model: ["hook_1"], }); const vueFile = await convertVue.init(); fs.outputFileSync(targetPath, vueFile); } } } else { // 其他文件复制 // 根据模块判断是否需要复制 if (isCopyFile(answers, targetPath)) { const [name, suffix] = item.name.split("."); if (suffix === "scss") { cssFile(item.name, filedir, targetPath, answers); } else { fs.copyFile(filedir, targetPath); } } } }); } }; const writeVueFile = (answers, filePath) => { const file = filePath.substring(filePath.indexOf("src"), filePath.length); const modelFile = { three: [join("src", "views", "three", `three.vue`)], }; let flag = true; for (const [key, value] of Object.entries(modelFile)) { if (!value.includes(file)) continue; if (typeof answers[key] === "boolean" && !answers[key]) { flag = false; return false; } } return flag; }; const action = (projectName) => { const cwdUrl = process.cwd(); createFolder(join(cwdUrl, projectName)) .then(() => { userQuestions(projectName); }) .catch(() => { console.log(chalk.red(`项目名可能已存在,请更换项目名或者删除文件夹${projectName}`)); }); }; export default { command: "create <registry-name> ", description: "create a new project", action: action, };