mirror of
https://gitee.com/niucloud-team/niucloud.git
synced 2026-08-01 20:15:49 +00:00
244 lines
7.5 KiB
JavaScript
244 lines
7.5 KiB
JavaScript
/**
|
||
* 生成小程序用 DIY bridge(静态 v-if,禁止 <component :is>)
|
||
*
|
||
* target:
|
||
* app -> diy-dynamic-bridge-app(框架 app 微页面)
|
||
* addon -> diy-dynamic-bridge-{key}(单插件微页面,供 zip 合并)
|
||
* router-> diy-dynamic-bridge.router(开发期 vue router,mp-core 用原生模板)
|
||
*/
|
||
const fs = require('fs')
|
||
const path = require('path')
|
||
const { ROOT, BUILD_DIR } = require('./addon-utils.cjs')
|
||
const { scanAllDiyComponents } = require('./diy-component-scan.cjs')
|
||
|
||
const REGISTRY_JSON = path.join(BUILD_DIR, 'diy-component-registry.json')
|
||
|
||
function importPath(entry) {
|
||
if (entry.scope === 'app') {
|
||
return `@/app/components/diy/${entry.folder}/index.vue`
|
||
}
|
||
return `@/addon/${entry.addonKey}/components/diy/${entry.folder}/index.vue`
|
||
}
|
||
|
||
function resolveOutputPath(options = {}) {
|
||
if (options.target === 'app' || options.appOnly) {
|
||
return path.join(ROOT, 'src/components/diy-dynamic-bridge-app/diy-dynamic-bridge-app.mp.vue')
|
||
}
|
||
if (options.target === 'addon' && options.addonKey) {
|
||
const key = options.addonKey
|
||
return path.join(ROOT, `src/components/diy-dynamic-bridge-${key}/diy-dynamic-bridge-${key}.mp.vue`)
|
||
}
|
||
if (options.target === 'router') {
|
||
return path.join(ROOT, 'src/components/diy-dynamic-bridge/diy-dynamic-bridge.router.mp.vue')
|
||
}
|
||
return path.join(ROOT, 'src/components/diy-dynamic-bridge/diy-dynamic-bridge.mp.vue')
|
||
}
|
||
|
||
function loadComponents(options = {}) {
|
||
if (options.target === 'app' || options.appOnly) {
|
||
return scanAllDiyComponents(options).app
|
||
}
|
||
if (options.target === 'addon' && options.addonKey) {
|
||
return scanAllDiyComponents({ addonKeys: [options.addonKey] }).addon
|
||
}
|
||
if (options.target === 'router') {
|
||
return []
|
||
}
|
||
if (options.addonKeys && options.addonKeys.length) {
|
||
const { app, addon } = scanAllDiyComponents({ addonKeys: options.addonKeys })
|
||
return [...app, ...addon].sort((a, b) => a.vueComponentName.localeCompare(b.vueComponentName))
|
||
}
|
||
if (fs.existsSync(REGISTRY_JSON)) {
|
||
const data = JSON.parse(fs.readFileSync(REGISTRY_JSON, 'utf-8'))
|
||
if (Array.isArray(data.components) && data.components.length) {
|
||
return data.components
|
||
}
|
||
}
|
||
return scanAllDiyComponents(options).all
|
||
}
|
||
|
||
function buildBridgeVue(components) {
|
||
const imports = components
|
||
.map((entry) => `import ${entry.vueComponentName} from '${importPath(entry)}'`)
|
||
.join('\n')
|
||
|
||
const blocks = components
|
||
.map(
|
||
(entry) => ` <${entry.vueComponentName}
|
||
v-if="componentName === '${entry.vueComponentName}'"
|
||
:ref="setInnerRef"
|
||
:component="component"
|
||
:global="global"
|
||
:index="index"
|
||
:scrollBool="scrollBool"
|
||
@update:componentIsShow="emit('update:componentIsShow', $event)"
|
||
@loadingFn="emit('loadingFn', $event)"
|
||
/>`
|
||
)
|
||
.join('\n')
|
||
|
||
return `<!-- AUTO-GENERATED by generate-diy-dynamic-bridge-mp.cjs — do not edit -->
|
||
<template>
|
||
<view v-if="componentName">
|
||
${blocks}
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { shallowRef, watch } from 'vue'
|
||
import { resolveDiyMeta } from '@/utils/diy-component-registry'
|
||
|
||
${imports}
|
||
|
||
const emit = defineEmits<{
|
||
'update:componentIsShow': [value: boolean]
|
||
loadingFn: [value: unknown]
|
||
}>()
|
||
|
||
const props = defineProps<{
|
||
componentName?: string
|
||
component?: object
|
||
global?: object
|
||
index?: number
|
||
scrollBool?: number
|
||
}>()
|
||
|
||
const innerRef = shallowRef<any>(null)
|
||
|
||
function setInnerRef(el: any) {
|
||
innerRef.value = el
|
||
}
|
||
|
||
function warnIfUnknown() {
|
||
const name = props.componentName || ''
|
||
if (!name) return
|
||
if (!resolveDiyMeta(name)) {
|
||
console.warn('[diy-dynamic-bridge] unknown componentName:', name)
|
||
}
|
||
}
|
||
|
||
watch(() => props.componentName, warnIfUnknown, { immediate: true })
|
||
|
||
defineExpose({
|
||
getInnerRef: () => innerRef.value
|
||
})
|
||
</script>
|
||
`
|
||
}
|
||
|
||
function buildRouterVue() {
|
||
return `<!-- AUTO-GENERATED router by generate-diy-dynamic-bridge-mp.cjs -->
|
||
<template>
|
||
<view v-if="componentName">
|
||
<diy-dynamic-bridge-app
|
||
v-if="routeBridge === 'app'"
|
||
:ref="setInnerRef"
|
||
:componentName="componentName"
|
||
:component="component"
|
||
:global="global"
|
||
:index="index"
|
||
:scrollBool="scrollBool"
|
||
@update:componentIsShow="emit('update:componentIsShow', $event)"
|
||
@loadingFn="emit('loadingFn', $event)"
|
||
/>
|
||
<!-- MP_MERGE_ADDON_BRIDGE_VUE -->
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { computed, shallowRef } from 'vue'
|
||
import { resolveDiyMeta } from '@/utils/diy-component-registry'
|
||
|
||
const emit = defineEmits<{
|
||
'update:componentIsShow': [value: boolean]
|
||
loadingFn: [value: unknown]
|
||
}>()
|
||
|
||
const props = defineProps<{
|
||
componentName?: string
|
||
component?: object
|
||
global?: object
|
||
index?: number
|
||
scrollBool?: number
|
||
}>()
|
||
|
||
const innerRef = shallowRef<any>(null)
|
||
|
||
function setInnerRef(el: any) {
|
||
innerRef.value = el
|
||
}
|
||
|
||
const routeBridge = computed(() => {
|
||
const meta = resolveDiyMeta(props.componentName || '')
|
||
return meta?.addonKey || 'app'
|
||
})
|
||
|
||
defineExpose({
|
||
getInnerRef: () => innerRef.value
|
||
})
|
||
</script>
|
||
`
|
||
}
|
||
|
||
function ensureBridgeShells(options = {}) {
|
||
const appVue = path.join(ROOT, 'src/components/diy-dynamic-bridge-app/diy-dynamic-bridge-app.vue')
|
||
fs.mkdirSync(path.dirname(appVue), { recursive: true })
|
||
if (!fs.existsSync(appVue)) {
|
||
fs.writeFileSync(
|
||
appVue,
|
||
`<!-- MP: injected from diy-dynamic-bridge-app.mp.vue -->\n<template><view /></template>\n<script setup lang="ts"></script>\n`,
|
||
'utf-8'
|
||
)
|
||
}
|
||
|
||
if (options.addonKey) {
|
||
const key = options.addonKey
|
||
const addonVue = path.join(ROOT, `src/components/diy-dynamic-bridge-${key}/diy-dynamic-bridge-${key}.vue`)
|
||
fs.mkdirSync(path.dirname(addonVue), { recursive: true })
|
||
fs.writeFileSync(
|
||
addonVue,
|
||
`<!-- AUTO-GENERATED stub for addon bridge ${key} -->\n<template><view v-if="componentName" /></template>\n<script setup lang="ts"></script>\n`,
|
||
'utf-8'
|
||
)
|
||
}
|
||
}
|
||
|
||
function describeScope(options, count) {
|
||
if (options.target === 'app' || options.appOnly) return `app-only (${count})`
|
||
if (options.target === 'addon' && options.addonKey) return `addon/${options.addonKey} (${count})`
|
||
if (options.target === 'router') return 'router'
|
||
if (options.addonKeys?.length) return `app + ${options.addonKeys.join(', ')} (${count})`
|
||
return `full registry (${count})`
|
||
}
|
||
|
||
function generateDiyDynamicBridgeMp(options = {}) {
|
||
ensureBridgeShells(options)
|
||
const outPath = resolveOutputPath(options)
|
||
const vue =
|
||
options.target === 'router'
|
||
? buildRouterVue()
|
||
: buildBridgeVue(loadComponents(options))
|
||
|
||
fs.mkdirSync(path.dirname(outPath), { recursive: true })
|
||
fs.writeFileSync(outPath, vue, 'utf-8')
|
||
|
||
const count = options.target === 'router' ? 0 : loadComponents(options).length
|
||
console.log(
|
||
`[generate-diy-dynamic-bridge-mp] ${describeScope(options, count)} -> ${path.relative(ROOT, outPath)}`
|
||
)
|
||
return outPath
|
||
}
|
||
|
||
module.exports = {
|
||
generateDiyDynamicBridgeMp,
|
||
resolveOutputPath,
|
||
ensureBridgeShells,
|
||
OUT_VUE: path.join(ROOT, 'src/components/diy-dynamic-bridge/diy-dynamic-bridge.mp.vue')
|
||
}
|
||
|
||
if (require.main === module) {
|
||
const { generateDiyRegistry } = require('./generate-diy-registry.cjs')
|
||
generateDiyRegistry()
|
||
generateDiyDynamicBridgeMp()
|
||
}
|