refactor: helpers, import type, cleanup

This commit is contained in:
三咲智子 Kevin Deng 2023-11-24 11:07:31 +08:00
parent 79fc4025c3
commit d02629efa2
No known key found for this signature in database
GPG Key ID: 69992F2250DFD93E
6 changed files with 140 additions and 109 deletions

View File

@ -3,7 +3,7 @@
exports[`basic 1`] = ` exports[`basic 1`] = `
"import { defineComponent as _defineComponent } from 'vue' "import { defineComponent as _defineComponent } from 'vue'
import { watchEffect } from 'vue' import { watchEffect } from 'vue'
import { template, setAttr, setText, children, on, insert } from 'vue/vapor' import { template, insert, setText, on } from 'vue/vapor'
const t0 = template(\`<h1 id=\\"title\\">Counter</h1><p>Count: </p><p>Double: </p><button>Increment</button>\`) const t0 = template(\`<h1 id=\\"title\\">Counter</h1><p>Count: </p><p>Double: </p><button>Increment</button>\`)
import { ref, computed } from 'vue' import { ref, computed } from 'vue'

View File

@ -1,7 +1,7 @@
import { import {
CodegenResult, type CodegenResult,
CompilerOptions, type CompilerOptions,
RootNode, type RootNode,
baseParse, baseParse,
} from '@vue/compiler-dom' } from '@vue/compiler-dom'
import { isString } from '@vue/shared' import { isString } from '@vue/shared'

View File

@ -1,9 +1,9 @@
import { import type {
CodegenContext, CodegenContext,
CodegenOptions, CodegenOptions,
CodegenResult, CodegenResult,
} from '@vue/compiler-dom' } from '@vue/compiler-dom'
import { DynamicChildren, IRNodeTypes, RootIRNode } from './transform' import { type DynamicChildren, type RootIRNode, IRNodeTypes } from './ir'
// IR -> JS codegen // IR -> JS codegen
export function generate( export function generate(
@ -13,26 +13,28 @@ export function generate(
} = {}, } = {},
): CodegenResult { ): CodegenResult {
let code = '' let code = ''
let preamble = `import { watchEffect } from 'vue' let preamble = ''
import { template, setAttr, setText, children, on, insert } from 'vue/vapor'\n`
const isSetupInlined = !!options.inline const { helpers, vaporHelpers } = ir
if (ir.template.length) {
preamble += ir.template preamble += ir.template
.map((template, i) => `const t${i} = template(\`${template.template}\`)\n`) .map(
.join('') (template, i) => `const t${i} = template(\`${template.template}\`)\n`,
)
.join('')
vaporHelpers.add('template')
}
code += `const root = t0()\n` code += `const root = t0()\n`
if (ir.children[0]) { if (ir.children[0]) {
code += `const {${genChildrens( code += `const {${genChildren(ir.children[0].children)}} = children(root)\n`
ir.children[0].children, vaporHelpers.add('children')
)}} = children(root)\n`
} }
for (const opration of ir.opration) { for (const opration of ir.opration) {
switch (opration.type) { switch (opration.type) {
case IRNodeTypes.TEXT_NODE: { case IRNodeTypes.TEXT_NODE: {
// TODO handle by runtime: document.createTextNode
code += `const n${opration.id} = document.createTextNode(${opration.content})\n` code += `const n${opration.id} = document.createTextNode(${opration.content})\n`
break break
} }
@ -46,6 +48,7 @@ import { template, setAttr, setText, children, on, insert } from 'vue/vapor'\n`
anchor = `, 0 /* InsertPosition.FIRST */` anchor = `, 0 /* InsertPosition.FIRST */`
} }
code += `insert(n${opration.element}, n${opration.parent}${anchor})\n` code += `insert(n${opration.element}, n${opration.parent}${anchor})\n`
vaporHelpers.add('insert')
} }
break break
} }
@ -53,21 +56,28 @@ import { template, setAttr, setText, children, on, insert } from 'vue/vapor'\n`
for (const [expr, effects] of Object.entries(ir.effect)) { for (const [expr, effects] of Object.entries(ir.effect)) {
let scope = `watchEffect(() => {\n` let scope = `watchEffect(() => {\n`
helpers.add('watchEffect')
for (const effect of effects) { for (const effect of effects) {
switch (effect.type) { switch (effect.type) {
case IRNodeTypes.SET_PROP: case IRNodeTypes.SET_PROP: {
scope += `setAttr(n${effect.element}, ${JSON.stringify( scope += `setAttr(n${effect.element}, ${JSON.stringify(
effect.name, effect.name,
)}, undefined, ${expr})\n` )}, undefined, ${expr})\n`
vaporHelpers.add('setAttr')
break break
case IRNodeTypes.SET_TEXT: }
case IRNodeTypes.SET_TEXT: {
scope += `setText(n${effect.element}, undefined, ${expr})\n` scope += `setText(n${effect.element}, undefined, ${expr})\n`
vaporHelpers.add('setText')
break break
case IRNodeTypes.SET_EVENT: }
case IRNodeTypes.SET_EVENT: {
scope += `on(n${effect.element}, ${JSON.stringify( scope += `on(n${effect.element}, ${JSON.stringify(
effect.name, effect.name,
)}, ${expr})\n` )}, ${expr})\n`
vaporHelpers.add('on')
break break
}
} }
} }
scope += '})\n' scope += '})\n'
@ -76,7 +86,14 @@ import { template, setAttr, setText, children, on, insert } from 'vue/vapor'\n`
code += 'return root' 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
const functionName = options.ssr ? `ssrRender` : `render` const functionName = options.ssr ? `ssrRender` : `render`
const isSetupInlined = !!options.inline
if (isSetupInlined) { if (isSetupInlined) {
code = `(() => {\n${code}\n})();` code = `(() => {\n${code}\n})();`
} else { } else {
@ -90,7 +107,7 @@ import { template, setAttr, setText, children, on, insert } from 'vue/vapor'\n`
} }
} }
function genChildrens(children: DynamicChildren) { function genChildren(children: DynamicChildren) {
let str = '' let str = ''
for (const [index, child] of Object.entries(children)) { for (const [index, child] of Object.entries(children)) {
str += ` ${index}: [` str += ` ${index}: [`
@ -98,7 +115,7 @@ function genChildrens(children: DynamicChildren) {
str += `n${child.id}` str += `n${child.id}`
} }
if (Object.keys(child.children).length) { if (Object.keys(child.children).length) {
str += `, {${genChildrens(child.children)}}` str += `, {${genChildren(child.children)}}`
} }
str += '],' str += '],'
} }

View File

@ -2,3 +2,4 @@ export { parse } from '@vue/compiler-dom'
export { transform } from './transform' export { transform } from './transform'
export { generate } from './generate' export { generate } from './generate'
export { compile } from './compile' export { compile } from './compile'
export * from './ir'

View File

@ -0,0 +1,72 @@
import type { SourceLocation } from '@vue/compiler-dom'
export const enum IRNodeTypes {
ROOT,
TEMPLATE_GENERATOR,
SET_PROP,
SET_TEXT,
SET_EVENT,
INSERT_NODE,
TEXT_NODE,
}
export interface IRNode {
type: IRNodeTypes
loc: SourceLocation
}
export interface RootIRNode extends IRNode {
type: IRNodeTypes.ROOT
template: Array<TemplateGeneratorIRNode>
children: DynamicChildren
effect: Record<string, EffectNode[]>
opration: OprationNode[]
helpers: Set<string>
vaporHelpers: Set<string>
}
export interface TemplateGeneratorIRNode extends IRNode {
type: IRNodeTypes.TEMPLATE_GENERATOR
template: string
}
export interface SetPropIRNode extends IRNode {
type: IRNodeTypes.SET_PROP
element: number
name: string
}
export interface SetTextIRNode extends IRNode {
type: IRNodeTypes.SET_TEXT
element: number
}
export interface SetEventIRNode extends IRNode {
type: IRNodeTypes.SET_EVENT
element: number
name: string
}
export interface TextNodeIRNode extends IRNode {
type: IRNodeTypes.TEXT_NODE
id: number
content: string
}
export interface InsertNodeIRNode extends IRNode {
type: IRNodeTypes.INSERT_NODE
element: number
parent: number
anchor: number | 'first' | 'last'
}
export type EffectNode = SetPropIRNode | SetTextIRNode | SetEventIRNode
export type OprationNode = TextNodeIRNode | InsertNodeIRNode
export interface DynamicChild {
id: number | null
store: boolean
children: DynamicChildren
}
export type DynamicChildren = Record<number, DynamicChild>

View File

@ -1,85 +1,21 @@
import { import type {
type NodeTypes, NodeTypes,
RootNode, RootNode,
Node, Node,
TemplateChildNode, TemplateChildNode,
ElementNode, ElementNode,
AttributeNode, AttributeNode,
SourceLocation,
InterpolationNode, InterpolationNode,
TransformOptions, TransformOptions,
DirectiveNode, DirectiveNode,
} from '@vue/compiler-dom' } from '@vue/compiler-dom'
import {
export const enum IRNodeTypes { type DynamicChildren,
ROOT, type EffectNode,
TEMPLATE_GENERATOR, type OprationNode,
SET_PROP, type RootIRNode,
SET_TEXT, IRNodeTypes,
SET_EVENT, } from './ir'
INSERT_NODE,
TEXT_NODE,
}
export interface IRNode {
type: IRNodeTypes
loc: SourceLocation
}
export interface RootIRNode extends IRNode {
type: IRNodeTypes.ROOT
template: Array<TemplateGeneratorIRNode>
children: DynamicChildren
effect: Record<string, EffectNode[]>
opration: OprationNode[]
helpers: Set<string>
}
export interface TemplateGeneratorIRNode extends IRNode {
type: IRNodeTypes.TEMPLATE_GENERATOR
template: string
}
export interface SetPropIRNode extends IRNode {
type: IRNodeTypes.SET_PROP
element: number
name: string
}
export interface SetTextIRNode extends IRNode {
type: IRNodeTypes.SET_TEXT
element: number
}
export interface SetEventIRNode extends IRNode {
type: IRNodeTypes.SET_EVENT
element: number
name: string
}
export interface TextNodeIRNode extends IRNode {
type: IRNodeTypes.TEXT_NODE
id: number
content: string
}
export interface InsertNodeIRNode extends IRNode {
type: IRNodeTypes.INSERT_NODE
element: number
parent: number
anchor: number | 'first' | 'last'
}
export type EffectNode = SetPropIRNode | SetTextIRNode | SetEventIRNode
export type OprationNode = TextNodeIRNode | InsertNodeIRNode
export interface DynamicChild {
id: number | null
store: boolean
children: DynamicChildren
}
export type DynamicChildren = Record<number, DynamicChild>
export interface TransformContext<T extends Node = Node> { export interface TransformContext<T extends Node = Node> {
node: T node: T
@ -87,15 +23,17 @@ export interface TransformContext<T extends Node = Node> {
root: TransformContext<RootNode> root: TransformContext<RootNode>
index: number index: number
options: TransformOptions options: TransformOptions
ir: RootIRNode // ir: RootIRNode
template: string template: string
children: DynamicChildren children: DynamicChildren
store: boolean store: boolean
ghost: boolean ghost: boolean
getElementId(): number getElementId(): number
registerEffect(expr: string, effectNode: EffectNode): void
registerTemplate(): number registerTemplate(): number
registerEffect(expr: string, effectNode: EffectNode): void
registerOpration(...oprations: OprationNode[]): void
helper(name: string): string
} }
function createRootContext( function createRootContext(
@ -104,7 +42,7 @@ function createRootContext(
options: TransformOptions, options: TransformOptions,
): TransformContext<RootNode> { ): TransformContext<RootNode> {
let i = 0 let i = 0
const { effect: bindings } = ir const { effect, opration, helpers, vaporHelpers } = ir
const ctx: TransformContext<RootNode> = { const ctx: TransformContext<RootNode> = {
node, node,
@ -112,15 +50,14 @@ function createRootContext(
index: 0, index: 0,
root: undefined as any, // set later root: undefined as any, // set later
options, options,
ir,
children: {}, children: {},
store: false, store: false,
ghost: false, ghost: false,
getElementId: () => i++, getElementId: () => i++,
registerEffect(expr, effectNode) { registerEffect(expr, effectNode) {
if (!bindings[expr]) bindings[expr] = [] if (!effect[expr]) effect[expr] = []
bindings[expr].push(effectNode) effect[expr].push(effectNode)
}, },
template: '', template: '',
@ -137,6 +74,14 @@ function createRootContext(
}) })
return ir.template.length - 1 return ir.template.length - 1
}, },
registerOpration(...node) {
opration.push(...node)
},
// TODO not used yet
helper(name, vapor = true) {
;(vapor ? vaporHelpers : helpers).add(name)
return name
},
} }
ctx.root = ctx ctx.root = ctx
return ctx return ctx
@ -178,12 +123,6 @@ export function transform(
root: RootNode, root: RootNode,
options: TransformOptions = {}, options: TransformOptions = {},
): RootIRNode { ): RootIRNode {
// {
// type: IRNodeTypes.TEMPLATE_GENERATOR,
// template,
// loc: root.loc
// }
const ir: RootIRNode = { const ir: RootIRNode = {
type: IRNodeTypes.ROOT, type: IRNodeTypes.ROOT,
loc: root.loc, loc: root.loc,
@ -191,7 +130,8 @@ export function transform(
children: {}, children: {},
effect: Object.create(null), effect: Object.create(null),
opration: [], opration: [],
helpers: new Set(['template']), helpers: new Set([]),
vaporHelpers: new Set([]),
} }
const ctx = createRootContext(ir, root, options) const ctx = createRootContext(ir, root, options)
transformChildren(ctx, true) transformChildren(ctx, true)
@ -303,7 +243,7 @@ function transformInterpolation(
anchor = isFirst ? 'first' : 'last' anchor = isFirst ? 'first' : 'last'
} }
ctx.ir.opration.push( ctx.registerOpration(
{ {
type: IRNodeTypes.TEXT_NODE, type: IRNodeTypes.TEXT_NODE,
loc: node.loc, loc: node.loc,
@ -379,6 +319,7 @@ function transformProp(
} }
} }
// TODO: reference packages/compiler-core/src/transforms/transformExpression.ts
function processExpression(ctx: TransformContext, expr: string) { function processExpression(ctx: TransformContext, expr: string) {
if (ctx.options.bindingMetadata?.[expr] === 'setup-ref') { if (ctx.options.bindingMetadata?.[expr] === 'setup-ref') {
expr += '.value' expr += '.value'