本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前端脚手架
解决什么问题?
脚手架就是帮助你把项目的基础架子搭好。例如项目依赖、模板、构建工具等等。让你不用从零开始配置一个项目,尽可能快的进行业务开发。
公司已开发的项目是资产和宝贵经验,包含很多公用的逻辑。把原有项目单纯地复制粘贴存在以下问题:
- 重复复制粘贴 dirty work
- 容易忽略项目中的配置
- 存在业务定制逻辑
- 项目框架不同。基于 cra/vue-cli/vite。需要整合到一起。
前置准备: 找个项目模板
核心模块
- 界面交互
- 下载项目模板
【界面交互】项目初始化
mkdir tracey-cli & cd tracey-cli
npm init -y
mkdir bin
新建enter.js
文件,在package.json
中增加"bin": "bin/enter"
- 编辑
enter.js
#!/usr/bin/env node
// 关联依赖 commander 命令行指令配置
// npm i commander
const { program } = require('commander');
const packageInfo = require('../package');
program.version(packageInfo.version, '-v, --version');
program.name(packageInfo.name)
.usage(`<command> [option]`)
// 解析参数
program.parse(process.argv);
- 终端运行
npm link
,添加--force
可以强制覆盖原有指令
测试:
【界面交互】控制台输出样式
来了解下项目的依赖和这些依赖的简易使用:
- chalk 命令行样式 *注意版本
4.1.2
- ora 终端 loading *注意版本
5.1.0
- figlet 生成终端的艺术字
// bin/enter.js
const chalk = require("chalk");
console.log(chalk.blue.underline.bold("调整样式"))
const ora = require("ora");
// 定义一个loading
const spinner = ora("加载中...");
// 启动loading
spinner.start();
setTimeout(() => {
spinner.text = "失败";
spinner.fail();
}, 1000);
const figlet = require("figlet")
console.log(
"\r\n" +
figlet.textSync(packageInfo.name, {
font: "ANSI Shadow",
horizontalLayout: "default",
verticalLayout: "default",
whitespaceBreak: true,
})
);
【界面交互】命令行交互
- inquirer *注意版本
8.1.4
input|number|confirm 确认|list 单选|rawlist 带序号单选|checkbox 多选
const Inquirer = require('inquirer')
new Inquirer.prompt([
{
type: "input",
name: "email",
message: "What's your email address?",
// 校验
validate: function (value) {
let pass = value.match(
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{l}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/);
if (pass) {
return true;
}
return "Please enter a valid email address.";
},
},
// 多选交互功能
{
name: "react",
// 单选将这里修改为 list 即可
type: "checkbox",
message: "选择项目所需功能",
choices: [
{
name: "Babel",
checked: true,
},
{
name: "TypeScript",
},
{
name: "Router",
},
],
// 选择分支,可通过when实现
when: function (answers) {
return answers.computerLanguage == "JavaScript'
}
},
]).then((data) => {
console.log(data);
});
ejs
const ejs = require('ejs')
const destUrl = path.join(__dirname, 'templates');
// 交互
// 模版文件目录
const destUrl = path.join(__dirname, 'templates');
// 生成文件目录
// process.cwd() 对应控制台所在目录
const cwdUrl = process.cwd();
// 从模版目录中读取文件
fs.readdir(destUrl, (err, files) => {
if (err) throw err;
files.forEach((file) => {
// 使用 ejs 渲染对应的模版文件
// renderFile(模版文件地址,传入渲染数据)
ejs.renderFile(path.join(destUrl, file), answers).then(data => {
// 生成 ejs 处理后的模版文件
fs.writeFileSync(path.join(cwdUrl, file) , data)
})
})
})
【项目模板】项目模板
- cross-spawn
- download-git-repo 下载模版
获取项目模板 git clone or fs-extra
项目模板没有选择内部维护,如果想要内部维护的话可以选择 fs-extra
读取
const path = require('path')
const crossSpawn = require('cross-spawn')
const spawn = (command, args = [], options = {}) => {
return new Promise((resolve) => {
const cmd = spawn(command, args, Object.assign({ stdio: 'inherit', shell: true, cwd: '' }, options))
cmd.on('close', code => {
if (code !== 0) {
return resolve({ command: `${command} ${args.join(' ')}` });
}
resolve();
});
})
}
// 下载模板
function download(project, git) {
const cwd = path.join(process.cwd(), `${project}`);
startLoad('', '下载中...');
return new Promise(async (resolve, reject) => {
let r = await spawn(`git`, ['clone', `${git.url}`, `${project}`, '-v', '--progress']);
endLoad()
if (r) {
red('下载失败')
return false
}
green('下载成功!');
if (git.branch) {
await spawn(`git`, ['checkout', `${git.branch}`], { cwd });
}
// 删除项目里的 .git 文件夹
await remove(`./${project}/.git`);
await spawn(`git`, [`init`], { cwd })
if (r) return reject(err);
return resolve(true)
})
}
项目源码
参考资料: