上一篇文章介绍了如何从零搭建cli脚手架工具并发布到npm,通过安装插件的方式去使用;本文重点在于通过问答方式获取项目所需的可配置化数据动态生成配置文件
背景:我们公司目前做的是本地化部署的项目,前端和后端需要开发多套;而调用的接口地址、静态资源地址、app名称等信息都是随着项目变化的;每新增一套代码需要重复复制、修改产生的时间成本,以及人为的修改和复制必然会导致一些疏漏,因此还要考虑查漏补缺的时间成本。为了节约成本、简化工作去搭建工具来替代我们干活
可以先看一下效果
- 模板原代码
- cli脚手架工具命令:hs-cli create project-name
- 修改后的代码
1、思路
下载不同模板代码(vue项目/react项目)
可以创建多个模板仓库,根据获取选择项来生成下载链接
新增、修改文件(ejs模板解析/字符串模板)
在已下载的文件夹中定位到需要新增/修改的文件位置,使用fs插件实现文件的读写功能
2、代码初始化
参考上一篇文章:node搭建cli脚手架工具-发布到NPM
3、一问一答
效果
如何实现?需要用到inquirer插件
const inquirer = require('inquirer')
let questions = [
{
type: 'list',
message: '创建平台还是金融子系统?',
name: 'type',
choices: [
{
name: '平台',
value: '1'
},
{
name: '金融',
value: '2'
}
]
},
{
type: 'input',
message: '请输入静态资源域名:',
name: 'sourceName',
when: (answer)=>{
return answer.type
},
default: '.xxx.com',
},
{
type: 'input',
message: '请输入平台接口域名:',
name: 'interfaceVtrade',
when: (answer)=>{
return answer.type === 1
},
default: '.xxx.com.cn',
},
{
type: 'input',
message: '请输入金融接口域名:',
name: 'interfaceVbkr',
when: (answer)=>{
return answer.type === 2
},
default: '.xxx.com',
},
]
inquirer.prompt(questions).then(async (answer)=>{
console.log(JSON.stringify(answer))
})
4、新增文件夹
使用fs-extra插件
const fes = require('fs-extra')
// 生成文件夹
function generateFolder(targetFolder) {
// 判断文件夹是否存在
if (!fes.existsSync(targetFolder)) {
fes.mkdirsSync(targetFolder);
}
}
5、写入文件
首先介绍ejs插件/模板字符串生成想要的配置代码
ejs
1、准备template.html文件
export const AppInfo = {
name: "<%= projectName %>", // 应用名称
systemId: "1", // 系统id
sourceName: "<%= sourceName %>", // 静态资源
interfaceVtrade: "<%= interfaceVtrade %>", // 平台接口地址域名
interfaceVbkr: "<%= interfaceVbkr %>", // 金融接口地址域名
};
export enum RuntimeEnvironment {
Dev,
Daily,
Feature,
Beta,
Production,
}
export enum Platform {
VTRADE,
VBKR,
}
2、解析html
const ejs = require('ejs')
const fes = require('fs-extra')
// ejs获取模板
function getTempByEjs(targetFile, data) {
let str = fes.readFileSync(targetFile, 'utf8');
return ejs.render(str, data);
}
// 调用
const text = getTempByEjs('./template.html', {...args})
模板字符串
1、准备template.js
module.exports = ({projectName, sourceName, interfaceVtrade, interfaceVbkr}) => {
return (
`// App应用信息
export const AppInfo = {
name: "${projectName}", // 应用名称
systemId: "1", // 系统id
sourceName: "${sourceName}", // 静态资源
interfaceVtrade: "${interfaceVtrade || interfaceVbkr}", // 平台接口地址域名
interfaceVbkr: "${interfaceVbkr || interfaceVtrade}", // 金融接口地址域名
};
export enum RuntimeEnvironment {
Dev,
Daily,
Feature,
Beta,
Prod,
}
export enum Platform {
VTRADE,
VBKR,
}
// 静态资源(平台和金融的相同)
export const StaticResourceDomain = {
[Platform.VTRADE]: \`//r<%= environments %>\${AppInfo.sourceName}/fe/<%= app %>/<%= environment %>/\`,
[Platform.VBKR]: \`//r<%= environments %>\${AppInfo.sourceName}/fe/<%= app %>/<%= environment %>/\`,
};
// 接口域名(平台和金融的不同)
export const APIDomain = {
[Platform.VTRADE]: \`//admin<%= environment %>\${AppInfo.interfaceVtrade}\`,
[Platform.VBKR]: \`//admin<%= environment %>\${AppInfo.interfaceVbkr}\`,
};`
)
}
2、解析
const template = require('./template');
const text = JSON.parse(JSON.stringify(template(...args)))
然后就是写入文件
const fes = require('fs-extra')
// 生成文件
function generateFile(targetFolder, name, date = {}) {
const targetFile = path.resolve(targetFolder, name);
fes.writeFileSync(targetFile, date)
}
6、总结
- 1、抽取不同的部分,采用问答模式,动态配置
- 2、通过类似的模式,可以生成重复度很高的业务代码,比如列表页