@tuzki/cli
Version:
🐇 lowcode-cli is an efficient cli tool for Rabbitpre plugin component secondary development. ❤️
150 lines (149 loc) • 6.15 kB
JavaScript
/*
* 代码生成器
*
* @Author: xu.jin
* @Date: 2022-12-06 09:41:48
*
* Copyright © 2014-2022 Rabbitpre.com. All Rights Reserved.
*/
import Logger from '@tuzki/scaffold-logger';
import ejs from 'ejs';
import fg from 'fast-glob';
import fse from 'fs-extra';
import lodash from 'lodash';
import path from 'path';
const logger = Logger.get('cli:utils:generator');
const { debounce } = lodash;
const RENDER_WAIT = 150;
export default class Generator {
constructor(options) {
this.setRenderData = (renderData) => {
this.renderData = renderData;
};
this.debounceRender = debounce(() => {
this.render();
}, RENDER_WAIT);
/** 添加模板目录文件 */
this.addTemplateFiles = (templateOptions, extraData = {}) => {
const { template, targetDir } = typeof templateOptions === 'string'
? { template: templateOptions, targetDir: '' }
: templateOptions;
// 如果 template 不为文件,获取当前目录下所有文件
const templates = path.extname(template)
? [template]
: fg.sync(['**/*'], { cwd: template, dot: true });
// 遍历 templates,调用 addRenderFile,渲染文件
templates.forEach((templateFile) => {
const templatePath = path.isAbsolute(templateFile)
? templateFile
: path.join(template, templateFile);
const filePath = path.isAbsolute(templateFile)
? path.basename(templateFile)
: templateFile;
const targetPath = path.join(this.targetDir, targetDir, filePath);
this.addRenderFile(templatePath, targetPath, extraData);
});
if (this.rerender) {
this.debounceRender();
}
};
// 添加渲染文件
this.addRenderFile = (templatePath, targetPath, extraData = {}) => {
const renderIndex = this.renderTemplates.findIndex(([, templateTarget]) => templateTarget === targetPath);
// 添加 template 到 this.renderTemplates
if (renderIndex > -1) {
const targetTemplate = this.renderTemplates[renderIndex];
if (targetTemplate[0] !== templatePath) {
logger.error('[template]', `path ${targetPath} already been rendered as file ${targetTemplate[0]}`);
}
// replace template with latest content
this.renderTemplates[renderIndex] = [templatePath, targetPath, extraData];
}
else {
this.renderTemplates.push([templatePath, targetPath, extraData]);
}
// 执行 render 进行渲染
if (this.rerender) {
this.debounceRender();
}
};
// 合并 modifyRenderData 之后的数据之后,进行文件渲染
this.render = () => {
this.rerender = true;
const initialRendererData = Object.assign({}, this.renderData);
// 调用 this.renderDataRegistration 处理生成最终渲染数据
this.renderData = this.renderDataRegistration.reduce((previousValue, currentValue) => {
if (typeof currentValue === 'function') {
return currentValue(previousValue);
}
return previousValue;
}, initialRendererData);
// 遍历 this.renderTemplates 渲染文件
this.renderTemplates.forEach(args => {
this.renderFile(...args);
});
};
// 修改渲染数据
this.modifyRenderData = registration => {
this.renderDataRegistration.push(registration);
if (this.rerender) {
this.debounceRender();
}
};
// 文件渲染生成
this.renderFile = (templatePath, targetPath, extraData = {}) => {
const renderExt = '.ejs';
const realTargetPath = path.isAbsolute(targetPath)
? targetPath
: path.join(this.rootDir, targetPath);
const { ext } = path.parse(templatePath);
// ejs 文件通过 ejs 方式渲染
if (ext === renderExt) {
const templateContent = fse.readFileSync(templatePath, 'utf-8');
let renderData = Object.assign({}, this.renderData);
if (typeof extraData === 'function') {
renderData = extraData(this.renderData);
}
else {
renderData = Object.assign(Object.assign({}, renderData), extraData);
}
const content = ejs.render(templateContent, renderData);
const finalTargetPath = realTargetPath.replace(renderExt, '');
fse.ensureFileSync(finalTargetPath);
fse.writeFileSync(finalTargetPath, content, 'utf-8');
}
else {
// 非 ejs 文件直接 copy 到目标文件夹
fse.ensureDirSync(path.dirname(realTargetPath));
fse.copyFileSync(templatePath, realTargetPath);
}
};
const { rootDir, targetDir = '', defaultRenderData = {}, templates, } = options;
this.rootDir = rootDir;
this.targetDir = targetDir;
this.renderData = defaultRenderData;
this.renderTemplates = [];
this.renderDataRegistration = [];
this.rerender = false;
if (templates) {
templates.forEach(template => this.addTemplateFiles(template));
}
}
}
/**
* 获取 generatorApi
*
* @export
* @param {Options} args 参数
* @return {*}
*/
export function getGeneratorApi(args) {
const generator = new Generator(args);
return {
addRenderFile: generator.addRenderFile,
addRenderTemplate: generator.addTemplateFiles,
modifyRenderData: generator.modifyRenderData,
renderFile: generator.renderFile,
render: generator.render,
};
}