forked from opentiny/tiny-vue
354 lines
9.4 KiB
JavaScript
354 lines
9.4 KiB
JavaScript
import gulp from 'gulp'
|
||
import minimist from 'minimist'
|
||
import shell from 'shelljs'
|
||
import path from 'path'
|
||
import { generateKey } from 'crypto'
|
||
|
||
const build = gulp.series(init, read, write, clean)
|
||
build.description = '将本地文档的组件api, 转换为低代码需要的bundle.json 格式'
|
||
build.flags = {
|
||
'--apiPath': '设置api文档路径,默认为: examples/sites/demos/pc',
|
||
'--tinyVer': '设置生成中,tinyVer的版本号,默认为 3.11.0'
|
||
}
|
||
export default build
|
||
|
||
const rootPath = process.cwd()
|
||
let apiPath = 'examples/sites/demos/pc'
|
||
let tinyVer = '3.11.0'
|
||
let guid = 1
|
||
let menus = []
|
||
|
||
const ignoreKeys = ['color', 'font', 'icon']
|
||
const mixinKeys = {
|
||
'form': [{ key: 'form-item', nameCn: '表单项', desc: '表单中的一行表单域对象' }],
|
||
'layout': [
|
||
{ key: 'row', nameCn: '行元素', desc: '行元素' },
|
||
{ key: 'col', nameCn: '列元素', desc: '列元素' }
|
||
],
|
||
'breadcrumb': [{ key: 'breadcrumb-item', nameCn: '面包屑项', desc: '面包屑的层级对象' }],
|
||
'timeline': [{ key: 'timeline-item', nameCn: '时间线数据项', desc: '时间线数据项' }],
|
||
'dropdown': [
|
||
{ key: 'dropdown-menu', nameCn: '下拉菜单', desc: '下拉菜单' },
|
||
{ key: 'dropdown-item', nameCn: '下拉菜单项', desc: '下拉菜单项' }
|
||
],
|
||
'carousel': [{ key: 'carousel-item', nameCn: '走马灯数据项', desc: '走马灯数据项' }],
|
||
'checkbox': [
|
||
{ key: 'checkbox-group', nameCn: '复选框组', desc: '复选框组' },
|
||
{ key: 'checkbox-button', nameCn: '复选框按钮', desc: '复选框按钮' }
|
||
],
|
||
'radio': [
|
||
{ key: 'radio-group', nameCn: '单选框组', desc: '单选框组' },
|
||
{ key: 'radio-button', nameCn: '单选框按钮', desc: '单选框按钮' }
|
||
],
|
||
'grid': [
|
||
{ key: 'grid-column', nameCn: '表格列对象', desc: '表格列对象' },
|
||
{ key: 'grid-toolbar', nameCn: '表格工具栏', desc: '表格工具栏' }
|
||
]
|
||
}
|
||
|
||
const result = {
|
||
'data': {
|
||
'framework': 'Vue',
|
||
'materials': {
|
||
'components': [],
|
||
'snippets': [],
|
||
'blocks': []
|
||
}
|
||
}
|
||
}
|
||
|
||
// 1. 初始化
|
||
function init(cb) {
|
||
const argv = minimist(process.argv.slice(2))
|
||
if (argv.apiPath) {
|
||
apiPath = argv.apiPath
|
||
}
|
||
if (argv.tinyVer) {
|
||
tinyVer = argv.tinyVer
|
||
}
|
||
|
||
// 读菜单
|
||
shell.cp(path.join(apiPath, '/menus.js'), 'gulp/menus.mjs')
|
||
shell.sed('-i', 'import.meta.env.VITE_BUILD_TARGET', 'false', 'gulp/menus.mjs')
|
||
import('./menus.mjs').then((res) => {
|
||
menus = res.cmpMenus
|
||
cb()
|
||
})
|
||
}
|
||
|
||
// 2. 遍历菜单,处理每一个组件
|
||
function read(cb) {
|
||
menus.forEach((group) => {
|
||
const snippetItem = {
|
||
'group': group.label,
|
||
'children': []
|
||
}
|
||
group.children.forEach((component) => {
|
||
if (ignoreKeys.includes(component.key)) {
|
||
return
|
||
}
|
||
if (component.key.startsWith('grid-') || component.key.startsWith('chart-')) {
|
||
return
|
||
}
|
||
|
||
const componentInfo = _readOneComp(component)
|
||
const componentItem = genComp(componentInfo)
|
||
const snipItem = genSnip(componentInfo)
|
||
|
||
result.data.materials.components.push(componentItem)
|
||
snippetItem.children.push(snipItem)
|
||
|
||
// 特殊的混合组件判断, 比如遍历到form时,要插入 form-item的定义
|
||
if (mixinKeys[component.key]) {
|
||
mixinKeys[component.key].forEach((mixin) => {
|
||
const componentInfo = _readOneComp(component, mixin)
|
||
const componentItem = genComp(componentInfo)
|
||
const snipItem = genSnip(componentInfo)
|
||
|
||
result.data.materials.components.push(componentItem)
|
||
snippetItem.children.push(snipItem)
|
||
})
|
||
}
|
||
})
|
||
result.data.materials.snippets.push(snippetItem)
|
||
})
|
||
cb()
|
||
}
|
||
|
||
function _readOneComp(component, mixin = '') {
|
||
if (!mixin) {
|
||
shell.echo('---正在读取组件', component.key)
|
||
const key = component.key
|
||
const camelizeKey = camelize(key)
|
||
const cmpName = component.nameCn
|
||
const desc = readMdDesc(key)
|
||
const jsStr = shell.cat(`${apiPath}/app/${key}/webdoc/${key}.js`).replace('export default', '(') + ')'
|
||
const api = eval(jsStr).apis.filter((item) => item.name === key)[0]
|
||
const props = api?.props || api?.properties || []
|
||
const events = api?.events || []
|
||
const slots = api?.slots || []
|
||
guid++
|
||
const componentInfo = { id: guid, version: tinyVer, key, camelizeKey, cmpName, desc, props, events, slots }
|
||
|
||
return componentInfo
|
||
} else {
|
||
shell.echo('---正在读取混入的组件', component.key, mixin.key)
|
||
const key = mixin.key
|
||
const camelizeKey = camelize(key)
|
||
const cmpName = mixin.nameCn
|
||
const desc = mixin.desc
|
||
const jsStr =
|
||
shell.cat(`${apiPath}/app/${component.key}/webdoc/${component.key}.js`).replace('export default', '(') + ')'
|
||
const api = eval(jsStr).apis.filter((item) => item.name === mixin.key)[0]
|
||
const props = api?.props || api?.properties || []
|
||
const events = api?.events || []
|
||
const slots = api?.slots || []
|
||
guid++
|
||
const componentInfo = { id: guid, version: tinyVer, key, camelizeKey, cmpName, desc, props, events, slots }
|
||
|
||
return componentInfo
|
||
}
|
||
}
|
||
// 3. 将结果写bundle.json
|
||
function write(cb) {
|
||
shell.ShellString(JSON.stringify(result, null, ' ')).to('gulp/bundle.json')
|
||
cb()
|
||
}
|
||
|
||
// 4. 清除
|
||
function clean(cb) {
|
||
shell.rm('gulp/menus.mjs')
|
||
cb()
|
||
}
|
||
|
||
// 以下辅助方法
|
||
|
||
// 转换:button-group ==> ButtonGroup
|
||
const camelize = (str) => {
|
||
return str
|
||
.replace(/-(\w)/g, (_, c) => (c ? c.toUpperCase() : ''))
|
||
.replace(/^(\w)/g, (_, c) => (c ? c.toUpperCase() : ''))
|
||
}
|
||
// 从标签中,提取文字。 <div>常用的操作按钮。</div> ==> 常用的操作按钮。
|
||
const readMdDesc = (key) => {
|
||
const mdStr = shell.cat(`${apiPath}/app/${key}/webdoc/${key}.cn.md`)
|
||
const matched = mdStr.match(/<div>([\w|\s|\u4e00-\u9fa5]*)<\/div>/i)
|
||
return matched ? matched[1] || '' : ''
|
||
}
|
||
const genComp = ({ id, version, key, camelizeKey, cmpName, desc, props, events, slots }) => {
|
||
const item = {
|
||
'id': id,
|
||
'version': version,
|
||
'name': {
|
||
'zh_CN': cmpName
|
||
},
|
||
'component': 'Tiny' + camelizeKey,
|
||
'icon': '',
|
||
'description': desc,
|
||
'doc_url': '',
|
||
'screenshot': '',
|
||
'tags': '',
|
||
'keywords': '',
|
||
'dev_mode': 'proCode',
|
||
'npm': {
|
||
'package': '@opentiny/vue',
|
||
'exportName': camelizeKey,
|
||
'version': version,
|
||
'destructuring': true
|
||
},
|
||
'group': 'component',
|
||
'configure': {
|
||
'loop': true,
|
||
'condition': true,
|
||
'styles': true,
|
||
'isContainer': false,
|
||
'isModal': false,
|
||
'nestingRule': {
|
||
'childWhitelist': '',
|
||
'parentWhitelist': '',
|
||
'descendantBlacklist': '',
|
||
'ancestorWhitelist': ''
|
||
},
|
||
'isNullNode': false,
|
||
'isLayout': false,
|
||
'rootSelector': '',
|
||
'shortcuts': {
|
||
'properties': []
|
||
},
|
||
'contextMenu': {
|
||
'actions': [],
|
||
'disable': []
|
||
},
|
||
'framework': 'Vue'
|
||
},
|
||
'schema': {
|
||
'properties': [
|
||
{
|
||
'label': {
|
||
'zh_CN': '基础信息'
|
||
},
|
||
'description': {
|
||
'zh_CN': '基础信息'
|
||
},
|
||
'collapse': {
|
||
'number': 6,
|
||
'text': {
|
||
'zh_CN': '显示更多'
|
||
}
|
||
},
|
||
'content': props.map((prop) => genProp(prop))
|
||
}
|
||
],
|
||
'events': {
|
||
...events.map((event) => genEvent(event)).reduce((pre, curr) => ({ ...pre, ...curr }), {})
|
||
},
|
||
'slots': {
|
||
...slots.map((slot) => genSlot(slot)).reduce((pre, curr) => ({ ...pre, ...curr }), {})
|
||
}
|
||
}
|
||
}
|
||
|
||
return item
|
||
}
|
||
|
||
const genProp = ({ name, type, defaultValue, desc }) => {
|
||
const typeMap = {
|
||
string: {
|
||
component: 'MetaInput',
|
||
props: {}
|
||
},
|
||
boolean: {
|
||
component: 'MetaSwitch',
|
||
props: {}
|
||
},
|
||
number: {
|
||
component: 'MetaNumberic',
|
||
props: {}
|
||
},
|
||
object: {
|
||
component: 'MetaCodeEditor',
|
||
props: {
|
||
language: 'json'
|
||
}
|
||
},
|
||
array: {
|
||
component: 'MetaCodeEditor',
|
||
props: {
|
||
language: 'json'
|
||
}
|
||
},
|
||
function: {
|
||
component: 'MetaCodeEditor',
|
||
props: {
|
||
language: 'javascript'
|
||
}
|
||
}
|
||
}
|
||
const normalizeType = type.trim().toLowerCase()
|
||
|
||
return {
|
||
'property': name,
|
||
'type': type,
|
||
'defaultValue': defaultValue == '--' ? '' : defaultValue,
|
||
'label': {
|
||
'text': {
|
||
'zh_CN': desc['zh-CN']
|
||
}
|
||
},
|
||
'cols': 12,
|
||
'rules': [],
|
||
'hidden': false,
|
||
'required': true,
|
||
'readOnly': true,
|
||
'disabled': true,
|
||
'widget': typeMap[normalizeType]
|
||
? typeMap[normalizeType] //
|
||
: { 'component': '', 'props': {} },
|
||
'description': {
|
||
'zh_CN': desc['zh-CN']
|
||
}
|
||
}
|
||
}
|
||
const genEvent = ({ name, type, defaultValue, desc }) => {
|
||
return {
|
||
['on' + camelize(name)]: {
|
||
'label': {
|
||
'zh_CN': desc['zh-CN']
|
||
},
|
||
'description': {
|
||
'zh_CN': desc['zh-CN']
|
||
},
|
||
'type': 'event',
|
||
'functionInfo': {
|
||
'params': [],
|
||
'returns': {}
|
||
},
|
||
'defaultValue': ''
|
||
}
|
||
}
|
||
}
|
||
const genSlot = ({ name, type, defaultValue, desc }) => {
|
||
return {
|
||
[name]: {
|
||
'label': {
|
||
'zh_CN': desc['zh-CN']
|
||
},
|
||
'description': {
|
||
'zh_CN': desc['zh-CN']
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
const genSnip = ({ id, version, key, camelizeKey, cmpName, desc, props, events, slots }) => {
|
||
return {
|
||
'name': {
|
||
'zh_CN': cmpName
|
||
},
|
||
'icon': key,
|
||
'screenshot': '',
|
||
'snippetName': 'Tiny' + camelizeKey,
|
||
'schema': {}
|
||
}
|
||
}
|