Skip to content
Appearance
如何自定义出码插件
插件开发相关约定
为了让 CodeGenInstance 实例能够调用用户传入的自定义插件,我们需要做相关的约定:
- 提供 run 函数,不能是箭头函数,否则无法绑定相关上下文
- 函数名遵守 tinyEngine-generateCode-plugin-xxx 的规则
- 提供 options 进行配置并且有默认 options
例如:
javascript
// 默认配置
const defaultOptions = {}
// 如有必要,增加参数 options,增加自定义插件的可复用性
function customPlugin(options) {
const runtimeOptions = merge(defaultOptions, options)
// 返回对象
return {
// 插件名
name: 'tiny-engine-generate-code-plugin-demo',
// 插件相关描述
description: 'demo',
// run 函数,出码流程会调用
run(schema, context) {
// 在函数内实现自定义出码
console.log('here is a demo plugin')
}
}
}run 函数参数简要说明
- schema 当前应用的 schema
- context codeInstance 提供的上下文,包括
- config 当前 instance 的配置
- genResult 当前出码的文件数组
- genLogs 当前出码的日志
- ...customContext 用户在 generateApp 实例化函数中自定义传入的上下文
- this 上下文 在 run 函数中,可以使用 this 访问 codeInstance 提供的上下文
this.addLog(log): void向 genLogs 中增加一条日志this.addFile(fileItem: FileItem, override: boolean): boolean向 genResult 中增加一个文件this.getFile(path, fileName)根据 path 和 fileName 在 genResult 中寻找目标文件this.replaceFile(fileItem)从 genResult 中替换指定文件this.deleteFile(fileItem)从 genResult 中删除指定文件
详细 api 说明,可查看 官方出码能力API
增加增量插件
在 如何自定义出码章节中,我们描述了如何替换官方出码插件,但是如果我们并不需要替换官方插件,而是增加插件,则可以往指定的出码阶段增加插件,例如:
javascript
generateApp({
customPlugins: {
// 往 transformStart 阶段,传入自定义插件
transformStart: [customPluginItem1, customPluginItem2],
// 往 transform 阶段传入自定义插件
transform: [customPluginItem3, customPluginItem4],
// 往 transformEnd 阶段传入自定义插件
transformEnd: [customPluginItem5, customPluginItem6]
}
})对页面出码进行调整
如需对页面出码进行调整,有两种主要方式:
- 自定义页面出码以及区块出码插件,整个页面文件内容可自定转换并且自定义。
- 对官方当前出码结果进行局部调整,可使用 pluginConfig 往页面出码进行传参,实现局部调整的结果。具体可查看自定义页面出码插件章节
相关示例
示例一:自定义路由生成插件
当前官方的出码中,默认生成的路由是 hash 路由,假如我们想要生成 history mode 的路由,则可以使用自定义路由插件的方式进行实现。
以下是简要的插件示例:
javascript
function genWebHistoryRouter() {
return {
name: 'tiny-engine-generate-code-plugin-web-history-router',
description: 'transform router schema to router code plugin',
run(schema, context) {
// 与官方路由插件的不同,createWebHashHistory 变成了 createWebHistory
const importSnippet = "import { createRouter, createWebHistory } from 'vue-router'"
const exportSnippet = `
export default createRouter({
history: createWebHistory(),
routes
})`
// 这里省略具体的路由生成逻辑,核心是将页面路由拼接成路由配置文件的字符串,具体可参考官方路由插件
const router = '{}'
this.addFile({
// 文件类型
fileType: 'js',
// 生成的文件名
fileName: 'index.js',
// 生成的文件相对于项目根目录的路径
path: './src/router',
// 文件内容
fileContent: `${importSnippet}\n ${routeSnippets} \n ${exportSnippet}`
})
}
}
}替换官方插件:
javascript
const instance = generateApp({
customPlugins: {
// 替换官方路由出码插件
router: genWebHistoryRouter()
}
})
// 出码
const res = await instance.generate(appSchema)示例二:在 transformStart 阶段增加对 schema 的解析或者预处理
javascript
function parseSchema() {
return {
name: 'tiny-engine-generate-code-plugin-parse-demo',
description: 'parseSchema demo',
run(schema) {
// 对 schema 进行合法性判断
if (!isValid(schema)) {
throw new Error('schema is not valid')
}
// 这里可以对 schema 进行预处理
// 比如,我们提前遍历页面,过滤掉文件夹类型的 pageSchema,避免生成页面插件时遇到文件夹类型的 schema
const { pageSchema } = schema
const pagesMap = {}
const resPageTree = []
schema.componentsMap = [...schema.componentsMap, ...BUILTIN_COMPONENTS_MAP]
for (const componentItem of pageSchema) {
pagesMap[componentItem.meta.id] = componentItem
}
for (const componentItem of pageSchema) {
if (!componentItem.meta.isPage) {
continue
}
const newComponentItem = {
...componentItem
}
let path = ''
let curParentId = componentItem.meta.parentId
let depth = 0
while (curParentId !== '0' && depth < 1000) {
const preFolder = pagesMap[curParentId]
path = `${preFolder.meta.name}${path ? '/' : ''}${path}`
newComponentItem.meta.router = `${preFolder.meta.router}/${newComponentItem.meta.router}`
curParentId = preFolder.meta.parentId
depth++
}
newComponentItem.path = path
resPageTree.push(newComponentItem)
}
schema.pageSchema = resPageTree
}
}
}在 transformStart 阶段增加我们自定义的插件
javascript
const instance = generateApp({
customPlugins: {
// 在 transformStart 增加自定义的插件
transformStart: [genWebHistoryRouter()]
}
})
// 出码
const res = await instance.generate(appSchema)示例三:在 transformEnd 阶段对已出码文件进行后置处理
在 transformEnd 阶段,我们已经得到了可读高质量的代码,但是由于每个团队的代码风格不同,我们可以在这一阶段增加插件,使用 prettier 等工具完成代码的格式化。
例如:
javascript
import prettier from 'prettier'
import parserHtml from 'prettier/parser-html'
import parseCss from 'prettier/parser-postcss'
import parserBabel from 'prettier/parser-babel'
function formatCode() {
const parserMap = {
json: 'json-stringify',
js: 'babel',
jsx: 'babel',
css: 'css',
less: 'less',
html: 'html',
vue: 'vue'
}
const mergedOption = options
return {
name: 'tiny-engine-generate-code-plugin-format-code',
description: 'transform block schema to code',
/**
* 格式化出码
* @param {import('../generator/generateApp').AppSchema} schema
* @returns
*/
run(schema, context) {
context.genResult.forEach((item) => {
const { fileContent, fileName } = item
const parser = parserMap[fileName.split('.').at(-1)]
if (!parser) {
return
}
const formattedCode = prettier.format(fileContent, {
parser,
plugins: [parserBabel, parseCss, parserHtml, ...(mergedOption.customPlugin || [])],
...mergedOption
})
this.replaceFile({ ...item, fileContent: formattedCode })
})
}
}
}更进一步的,我们还可以使用 babel 等专业工具对出码进一步处理,进一步满足出码需求。