2023-11-24 11:07:31 +08:00
|
|
|
import type {
|
2023-11-17 03:01:19 +08:00
|
|
|
CodegenContext,
|
|
|
|
CodegenOptions,
|
2023-11-23 23:42:08 +08:00
|
|
|
CodegenResult,
|
2023-11-17 03:01:19 +08:00
|
|
|
} from '@vue/compiler-dom'
|
2023-11-24 11:07:31 +08:00
|
|
|
import { type DynamicChildren, type RootIRNode, IRNodeTypes } from './ir'
|
2023-11-17 03:01:19 +08:00
|
|
|
|
|
|
|
// IR -> JS codegen
|
|
|
|
export function generate(
|
2023-11-23 23:42:08 +08:00
|
|
|
ir: RootIRNode,
|
2023-11-17 03:01:19 +08:00
|
|
|
options: CodegenOptions & {
|
|
|
|
onContextCreated?: (context: CodegenContext) => void
|
2023-11-23 23:42:08 +08:00
|
|
|
} = {},
|
2023-11-17 03:01:19 +08:00
|
|
|
): CodegenResult {
|
|
|
|
let code = ''
|
2023-11-24 11:07:31 +08:00
|
|
|
let preamble = ''
|
2023-11-17 03:01:19 +08:00
|
|
|
|
2023-11-24 11:07:31 +08:00
|
|
|
const { helpers, vaporHelpers } = ir
|
|
|
|
if (ir.template.length) {
|
|
|
|
preamble += ir.template
|
|
|
|
.map(
|
|
|
|
(template, i) => `const t${i} = template(\`${template.template}\`)\n`,
|
|
|
|
)
|
|
|
|
.join('')
|
|
|
|
vaporHelpers.add('template')
|
|
|
|
}
|
2023-11-17 03:01:19 +08:00
|
|
|
|
2023-11-23 23:42:08 +08:00
|
|
|
code += `const root = t0()\n`
|
|
|
|
if (ir.children[0]) {
|
2023-11-24 11:07:31 +08:00
|
|
|
code += `const {${genChildren(ir.children[0].children)}} = children(root)\n`
|
|
|
|
vaporHelpers.add('children')
|
2023-11-23 23:42:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for (const opration of ir.opration) {
|
|
|
|
switch (opration.type) {
|
|
|
|
case IRNodeTypes.TEXT_NODE: {
|
2023-11-24 11:07:31 +08:00
|
|
|
// TODO handle by runtime: document.createTextNode
|
2023-11-23 23:42:08 +08:00
|
|
|
code += `const n${opration.id} = document.createTextNode(${opration.content})\n`
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
case IRNodeTypes.INSERT_NODE:
|
|
|
|
{
|
|
|
|
let anchor = ''
|
|
|
|
if (typeof opration.anchor === 'number') {
|
|
|
|
anchor = `, n${opration.anchor}`
|
|
|
|
} else if (opration.anchor === 'first') {
|
|
|
|
anchor = `, 0 /* InsertPosition.FIRST */`
|
|
|
|
}
|
|
|
|
code += `insert(n${opration.element}, n${opration.parent}${anchor})\n`
|
2023-11-24 11:07:31 +08:00
|
|
|
vaporHelpers.add('insert')
|
2023-11-23 23:42:08 +08:00
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const [expr, effects] of Object.entries(ir.effect)) {
|
|
|
|
let scope = `watchEffect(() => {\n`
|
2023-11-24 11:07:31 +08:00
|
|
|
helpers.add('watchEffect')
|
2023-11-23 23:42:08 +08:00
|
|
|
for (const effect of effects) {
|
|
|
|
switch (effect.type) {
|
2023-11-24 11:07:31 +08:00
|
|
|
case IRNodeTypes.SET_PROP: {
|
2023-11-23 23:42:08 +08:00
|
|
|
scope += `setAttr(n${effect.element}, ${JSON.stringify(
|
|
|
|
effect.name,
|
|
|
|
)}, undefined, ${expr})\n`
|
2023-11-24 11:07:31 +08:00
|
|
|
vaporHelpers.add('setAttr')
|
2023-11-23 23:42:08 +08:00
|
|
|
break
|
2023-11-24 11:07:31 +08:00
|
|
|
}
|
|
|
|
case IRNodeTypes.SET_TEXT: {
|
2023-11-23 23:42:08 +08:00
|
|
|
scope += `setText(n${effect.element}, undefined, ${expr})\n`
|
2023-11-24 11:07:31 +08:00
|
|
|
vaporHelpers.add('setText')
|
2023-11-23 23:42:08 +08:00
|
|
|
break
|
2023-11-24 11:07:31 +08:00
|
|
|
}
|
|
|
|
case IRNodeTypes.SET_EVENT: {
|
2023-11-23 23:42:08 +08:00
|
|
|
scope += `on(n${effect.element}, ${JSON.stringify(
|
|
|
|
effect.name,
|
|
|
|
)}, ${expr})\n`
|
2023-11-24 11:07:31 +08:00
|
|
|
vaporHelpers.add('on')
|
2023-11-23 23:42:08 +08:00
|
|
|
break
|
2023-11-24 11:07:31 +08:00
|
|
|
}
|
2023-11-23 23:42:08 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
scope += '})\n'
|
|
|
|
code += scope
|
|
|
|
}
|
|
|
|
|
2023-11-17 03:01:19 +08:00
|
|
|
code += 'return root'
|
|
|
|
|
2023-11-24 11:07:31 +08:00
|
|
|
if (vaporHelpers.size)
|
|
|
|
preamble =
|
|
|
|
`import { ${[...vaporHelpers].join(', ')} } from 'vue/vapor'\n` + preamble
|
|
|
|
if (helpers.size)
|
|
|
|
preamble = `import { ${[...helpers].join(', ')} } from 'vue'\n` + preamble
|
|
|
|
|
2023-11-17 03:01:19 +08:00
|
|
|
const functionName = options.ssr ? `ssrRender` : `render`
|
2023-11-24 11:07:31 +08:00
|
|
|
const isSetupInlined = !!options.inline
|
2023-11-17 03:01:19 +08:00
|
|
|
if (isSetupInlined) {
|
|
|
|
code = `(() => {\n${code}\n})();`
|
|
|
|
} else {
|
2023-11-17 17:35:49 +08:00
|
|
|
code = `${preamble}export function ${functionName}() {\n${code}\n}`
|
2023-11-17 03:01:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
code,
|
2023-11-23 23:42:08 +08:00
|
|
|
ast: ir as any,
|
|
|
|
preamble,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-24 11:07:31 +08:00
|
|
|
function genChildren(children: DynamicChildren) {
|
2023-11-23 23:42:08 +08:00
|
|
|
let str = ''
|
|
|
|
for (const [index, child] of Object.entries(children)) {
|
|
|
|
str += ` ${index}: [`
|
|
|
|
if (child.store) {
|
|
|
|
str += `n${child.id}`
|
|
|
|
}
|
|
|
|
if (Object.keys(child.children).length) {
|
2023-11-24 11:07:31 +08:00
|
|
|
str += `, {${genChildren(child.children)}}`
|
2023-11-23 23:42:08 +08:00
|
|
|
}
|
|
|
|
str += '],'
|
2023-11-17 03:01:19 +08:00
|
|
|
}
|
2023-11-23 23:42:08 +08:00
|
|
|
return str
|
2023-11-17 03:01:19 +08:00
|
|
|
}
|