vue3-core/packages/compiler-vapor/src/generate.ts

120 lines
3.3 KiB
TypeScript
Raw Normal View History

2023-11-24 11:15:33 +08:00
import type { CodegenOptions, CodegenResult } from '@vue/compiler-dom'
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-24 11:15:33 +08:00
options: CodegenOptions = {},
2023-11-17 03:01:19 +08:00
): CodegenResult {
let code = ''
let preamble = ''
2023-11-17 03:01:19 +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-24 11:15:33 +08:00
// TODO multiple-template
2023-11-23 23:42:08 +08:00
code += `const root = t0()\n`
if (ir.children[0]) {
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: {
// 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`
vaporHelpers.add('insert')
2023-11-23 23:42:08 +08:00
}
break
}
}
for (const [expr, effects] of Object.entries(ir.effect)) {
2023-11-24 11:39:49 +08:00
// TODO don't use watchEffect from vue/core, implement `effect` function in runtime-vapor package
2023-11-23 23:42:08 +08:00
let scope = `watchEffect(() => {\n`
helpers.add('watchEffect')
2023-11-23 23:42:08 +08:00
for (const effect of effects) {
switch (effect.type) {
case IRNodeTypes.SET_PROP: {
2023-11-23 23:42:08 +08:00
scope += `setAttr(n${effect.element}, ${JSON.stringify(
effect.name,
)}, undefined, ${expr})\n`
vaporHelpers.add('setAttr')
2023-11-23 23:42:08 +08:00
break
}
case IRNodeTypes.SET_TEXT: {
2023-11-23 23:42:08 +08:00
scope += `setText(n${effect.element}, undefined, ${expr})\n`
vaporHelpers.add('setText')
2023-11-23 23:42:08 +08:00
break
}
case IRNodeTypes.SET_EVENT: {
2023-11-23 23:42:08 +08:00
scope += `on(n${effect.element}, ${JSON.stringify(
effect.name,
)}, ${expr})\n`
vaporHelpers.add('on')
2023-11-23 23:42:08 +08:00
break
}
2023-11-23 23:42:08 +08:00
}
}
scope += '})\n'
code += scope
}
2023-11-17 03:01:19 +08:00
code += 'return root'
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`
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,
}
}
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) {
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
}