前段时间在公司项目中发现了一个问题,公司项目大部分使用vue开发,涉及到了pc,h5,微信公众号,企业微信等页面;
而这些页面的项目管理并不是统一配置的,可以说是各有各的风格;
对于这些头疼的配置,想到了vue-cli下载模板的功能;是否也是可以使用类似功能实现一个统一配置的项目模板呢?
之后查阅了部分资料,搭配成型的插件使用是完全可以达到效果;那该怎么做呢?
前置准备的插件:
- chalk: 美化Node输出神器
- commander:开发命令行工具,是 一个帮助快速开发Nodejs命令行工具的package。
- figlet:终端输出特殊的文字,这些文字只包含 ANSI 对应的字符。
- handlebars:模板语言,用模板和输入对象来生成 HTML 或其他文本格式
- inquirer:Node中的交互式命令行界面(CLI)工具
- ora:终端微调器,主要用来作为等待图标使用
开发步骤:
首先在本地创建一个文件夹目录用于存放你的代码(当然最好使用git仓库存放);
然后使用 npm init -y 初始化,这样根目录就多了一个 package.json 文件,接着对该文件进行部分修改,在文件中,添加bin字段,并指向安装的核心代码路径(bin的作用这里不做介绍,可自行百度)具体修改如下:
{
...
"bin": {
"temp-pro": "bin/temp-pro.js"
}
...
}
这里的 temp-pro.js 就是核心代码了;当然 temp-pro是npm install package_name 包名,你可以换成自己的;
核心代码中,使用 commander 做一些提示命令,如:-v 获取版本, --help获取帮助, create <name> 创建项目等等;
逻辑并不是太复杂,简单的说,就是输入特定的命令(获取版本,帮助,创建),在创建项目前会判断项目是否已经存在,不存在会根据配置的git地址进行克隆下载;
会有固定的几种模板:
- vue模板:pc系统(自己搭建封装的一个简单案例)和h5
- node模板:nest(ts)+mySql 和 express+mongodb
- app模板:uniapp,flutter(暂未开发)
然后根据自己的需求选择,当然私有库是需要登录的,特别适用于私有化的操作;
可以参考简单的流程图:
以下是参考代码:
#!/usr/bin/env node
const program = require("commander");
const { handlerHelp, isExistFileName } = require("./handler");
program
.version(require("../package.json").version, "-v, --version")
.on("--help", handlerHelp)
.command("create <name>")
.action(name => {
isExistFileName(name);
});
program.parse(process.argv);
const chalk = require("chalk");
const fs = require("fs");
const path = require('path')
const inquirer = require("inquirer");
const ora = require("ora");
const figlet = require("figlet");
const handlebars = require("handlebars");
const child_process = require('child_process')
const { inquirerConfig, gitConfig } = require("./inquirer");
const spinner = ora("正在下载项目模板, 请稍等...");
const LOGO = "TEMPPRO";
const removeDirectory = (url) => {
const files = fs.readdirSync(url);
files.forEach(function (file, index) {
const curPath = path.join(url, file);
if (fs.statSync(curPath).isDirectory()) removeDirectory(curPath);
else fs.unlinkSync(curPath);
});
fs.rmdirSync(url);
}
/**
* @description 给help美化颜色
*/
const handlerHelp = () => {
console.log();
console.log(
`运行 ${chalk.cyan("wz-cli <command> --help")} 了解给定命令的详细用法。`
);
console.log();
};
exports.handlerHelp = handlerHelp;
/**
* @description 检测文件名是否存在
* @param {String} name
* @returns
*/
const isExistFileName = name => {
if (!name) return
if (!fs.existsSync(name)) {
handlerInquirer(name);
} else {
spinner.fail(chalk.red.bold(`Error: 项目【${name}】已存在,请更改项目名称!`))
}
};
/**
* @description 提供用户界面和查询会话流程
* @param {String} name
*/
const handlerInquirer = name => {
inquirer.prompt(inquirerConfig).then(answers => {
spinner.info(chalk.green.bold(`正在下载项目模板, 请稍等...`));
const { keywords = "" } = answers;
const findGit = gitConfig.find(git => git.name === keywords) || {};
if (findGit.url !== "" && findGit.checkout !== "") {
downloadProject(findGit.url, findGit.checkout, name, answers);
} else {
spinner.fail(chalk.red.bold(`Error: 模板地址不存在!`));
}
});
};
/**
* @description 根据git地址下载文件
* @param {String} url git 地址
* @param checkout
* @param {String} name 项目名字
* @param {*} answers 会话流程回调参数
*/
const downloadProject = (url, checkout, name, answers) => {
child_process.exec(`git clone -b ${checkout} ${url} ${name}`, (error, stdout) => {
if (error) {
removeDirectory(name);
return spinner.fail(chalk.red.bold(error.message));
}
// removeDirectory(`${name}/.git`);
spinner.succeed(chalk.green.bold("项目模板初始化完成..."));
const fileName = `${name}/package.json`;
const meta = {
name,
description: answers.description,
author: answers.author,
};
if (fs.existsSync(fileName)) {
const content = fs.readFileSync(fileName).toString();
const result = handlebars.compile(content)(meta);
fs.writeFileSync(fileName, result);
}
handlerLogo(name);
})
};
/**
* @description 打印logo
* @param {String} name 项目名
* @returns
*/
const handlerLogo = name => {
if (!name) return;
console.log(
"\r\n" +
figlet.textSync(LOGO, {
horizontalLayout: "default",
verticalLayout: "default",
width: 80,
whitespaceBreak: true,
}) +
"\r\n"
);
console.log(chalk.green(`\t打开项目:cd ${name}`));
console.log(
chalk.green(
`\t安装依赖:npm install or cnpm install or yarn install or pnpm install`
)
);
};
exports.isExistFileName = isExistFileName;
const gitConfig = [
{
name: "Pc(vue3 + Element-Plus + abk公司内部使用)",
url: "http://xxx.com/frontend/xxx.git",
checkout: 'pc-admin-template'
},
{
name: "H5(vue3 + vant + abk公司内部使用)",
url: "http://xxx.com/frontend/xxx.git",
checkout: 'h5-template'
},
{
name: "Node(Nest + MySql + 个人使用)",
url: "https://gitee.com/wang-xiaoze/nest-api-admin.git",
checkout: 'master'
},
{
name: "Node(Express + MongoDB + 公共模板)",
url: "https://gitee.com/wang-xiaoze/node-service-template.git",
checkout: 'master'
},
];
exports.gitConfig = gitConfig;
const inquirerConfig = [
{
type: "input",
name: "description",
message: "Please enter a project description?",
default: "An interesting project template",
},
{
type: "input",
name: "author",
message: "Please enter the author's name?",
default: "王小泽(wangxiaoze@petalmail.com)",
},
{
type: "list",
name: "keywords",
message: "Please select the required project template?",
choices: gitConfig.map(item => item.name),
default: 0,
},
];
exports.inquirerConfig = inquirerConfig;
源码地址:
如果有错误的地方还请大家多多指教,共同学习哦!