create-bfe-cttq
Version:
CTTQ大前端脚手架项目
150 lines (147 loc) • 4.92 kB
JavaScript
/**
* 各插件交互式生成API接口
*/
const fs = require('fs');
const path = require('path');
const ejs = require("ejs")
const { isBinaryFileSync } = require('isbinaryfile');
const mergePackage = require("./util/mergePackage");
const { isFunction, isObject, isString} = require("./util/shared");
/**
* 通过模版引擎重新渲染文件
* @param {*} name 文件路径名称
* @param {*} data 替换数据
* @returns 最新的文件信息
*/
function renderFile(name, data = {}) {
if (fs.lstatSync(name).isDirectory()) {
return null;
}
if (isBinaryFileSync(name)) {
return fs.readFileSync(name);
}
return ejs.render(fs.readFileSync(name, 'utf-8'), data);
}
module.exports = class GeneratorAPI {
/**
* 构造器
* @param {*} name 插件名称
* @param {*} generator 生成器
*/
constructor(name, generator) {
this._name = name;
this._generator = generator;
}
/**
* 插件名称
*/
get name() {
return this._name;
}
/**
* 把文件渲染写入到项目中
* @param {*} source 来源文件:文件地址、模版库中的名称、渲染函数
* @param {*} opt 可选配置信息
*/
render(source, opt = {}) {
let options = {
isTemplate: true,
...opt,
}
let data = {
projectName: this._generator.answers.name,
projectDescription: this._generator.answers.description,
};
if (isString(source)) {
this._injectFileMiddleware(async (files) => {
const globby = require('globby');
let cwd = source;
if (options.isTemplate) {
cwd = this._generator.templateManager.pathAt(source);
}
const _files = await globby(['**/*'], { cwd: cwd, dot: true, onlyFiles: false, markDirectories: true });
for (const rawPath of _files) {
const targetPath = rawPath.split('/').map(filename => {
if (filename.charAt(0) === '_' && filename.charAt(1) !== '_') {
return `.${filename.slice(1)}`;
}
if (filename.charAt(0) === '_' && filename.charAt(1) === '_') {
return `${filename.slice(1)}`;
}
return filename;
}).join('/');
const sourcePath = path.resolve(cwd, rawPath);
const content = renderFile(sourcePath, data);
files[targetPath] = content;
}
});
} else if (isObject(source)) {
this._injectFileMiddleware(files => {
for (const targetPath in source) {
let sourcePath = source[targetPath];
if (options.isTemplate) {
sourcePath = this._generator.templateManager.pathAt(sourcePath);
}
const content = renderFile(sourcePath, data);
files[targetPath] = content;
}
});
} else if (isFunction(source)) {
this._injectFileMiddleware(source);
}
}
/**
* 扩充合并package.json信息
* @param {*} fields pkg字段信息
* @param {*} opt 可选配置
*/
extendPackage(fields, opt = {}) {
let options = {
isTemplate: true,
...opt,
}
if (isString(fields)) {
let pkg = fields;
if (options.isTemplate) {
pkg = this._generator.templateManager.pathAt(fields);
}
if (fs.existsSync(pkg)) {
fields = JSON.parse(fs.readFileSync(pkg, "utf-8") || "{}");
} else {
fields = {};
}
}
const pkg = this._generator.pkg
const toMerge = isFunction(fields) ? fields(pkg, this._generator.templateManager.rootPath) : fields;
mergePackage(this.name, pkg, toMerge, this._generator.conflictDepSources, options);
}
/**
* 注册事件:创建前
* @param {*} cb 回调函数
*/
onBeforeCreate(cb) {
this._generator.beforeCreateCallbacks.push(cb);
}
/**
* 注册事件:创建后
* @param {*} cb 回调函数
*/
onCreated(cb) {
this._generator.createdCallbacks.push(cb);
}
/**
* 获取在模版库中的地址
* @param {*} source 模版名称
* @returns 地址
*/
templatePathAt(source) {
return this._generator.templateManager.pathAt(source);
}
/**
* 注入渲染文件的函数方法
* @param {*} middleware 中间件
*/
_injectFileMiddleware(middleware){
this._generator.fileMiddlewares.push(middleware)
}
}