`;
exports[`compiler: v-if > comment between branches 1`] = `
-"import { template as _template, fragment as _fragment, createIf as _createIf, prepend as _prepend } from 'vue/vapor';
+"import { template as _template, children as _children, createIf as _createIf, prepend as _prepend, renderEffect as _renderEffect, setText as _setText } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div>")
const t1 = _template("<!--foo--><p></p>")
const t2 = _template("<!--bar-->fine")
- const t3 = _fragment()
+ const t3 = _template("<input>")
const n0 = t3()
+ const { 0: [n5],} = _children(n0)
const n1 = _createIf(() => (_ctx.ok), () => {
const n2 = t0()
return n2
return n4
}))
_prepend(n0, n1)
+ _renderEffect(() => {
+ _setText(n5, _ctx.text)
+ })
return n0
}"
`;
import { ErrorCodes, NodeTypes } from '@vue/compiler-dom'
-import { IRNodeTypes, transformElement, transformVBind } from '../../src'
+import {
+ DynamicFlag,
+ IRNodeTypes,
+ transformElement,
+ transformVBind,
+} from '../../src'
import { makeCompile } from './_utils'
const compileWithVBind = makeCompile({
expect(ir.dynamic.children[0]).toMatchObject({
id: 1,
- referenced: true,
+ dynamicFlags: DynamicFlag.REFERENCED,
})
expect(ir.template[0]).toMatchObject({
type: IRNodeTypes.TEMPLATE_FACTORY,
<p v-else-if="orNot"/>
<!--bar-->
<template v-else>fine</template>
+ <input v-text="text" />
`)
expect(code).matchSnapshot()
expect(ir.template).lengthOf(4)
template: '<!--bar-->fine',
type: IRNodeTypes.TEMPLATE_FACTORY,
},
- { type: IRNodeTypes.FRAGMENT_FACTORY },
+ {
+ type: IRNodeTypes.TEMPLATE_FACTORY,
+ template: '<input>',
+ },
])
})
} from '@vue/compiler-dom'
import {
type BlockFunctionIRNode,
+ DynamicFlag,
type IRDynamicChildren,
IRNodeTypes,
type OperationNode,
let offset = 0
for (const [index, child] of Object.entries(children)) {
const childrenLength = Object.keys(child.children).length
- if (child.ghost && child.placeholder === null && childrenLength === 0) {
+ if (
+ child.dynamicFlags & DynamicFlag.NON_TEMPLATE ||
+ (child.dynamicFlags & DynamicFlag.INSERT &&
+ child.placeholder === null &&
+ childrenLength === 0)
+ ) {
offset--
continue
}
- code += ` ${Number(index) + offset}: [`
-
- const id = child.ghost ? child.placeholder : child.id
- if (id !== null) code += `n${id}`
-
+ const idx = Number(index) + offset
+ const id =
+ child.dynamicFlags & DynamicFlag.INSERT ? child.placeholder : child.id
const childrenString = childrenLength && genChildren(child.children)
- if (childrenString) code += `, ${childrenString}`
- code += '],'
+ if (id !== null || childrenString) {
+ code += ` ${idx}: [`
+ if (id !== null) code += `n${id}`
+ if (childrenString) code += `, ${childrenString}`
+ code += '],'
+ }
}
if (!code) return ''
export type BlockIRNode = RootIRNode | BlockFunctionIRNode
+export enum DynamicFlag {
+ NONE = 0,
+ /**
+ * This node is referenced and needs to be saved as a variable.
+ */
+ REFERENCED = 1,
+ /**
+ * This node is not generated from template, but is generated dynamically.
+ */
+ NON_TEMPLATE = 1 << 1,
+ /**
+ * This node needs to be inserted back into the template.
+ */
+ INSERT = 1 << 2,
+}
+
export interface IRDynamicInfo {
id: number | null
- referenced: boolean
- /** created by DOM API */
- ghost: boolean
+ dynamicFlags: DynamicFlag
placeholder: number | null
children: IRDynamicChildren
}
} from '@vue/compiler-dom'
import { EMPTY_OBJ, NOOP, extend, isArray, isString } from '@vue/shared'
import {
+ type BlockIRNode,
+ DynamicFlag,
+ type FragmentFactoryIRNode,
+ type HackOptions,
type IRDynamicInfo,
type IRExpression,
IRNodeTypes,
type OperationNode,
type RootIRNode,
-} from './ir'
-import type {
- BlockIRNode,
- FragmentFactoryIRNode,
- HackOptions,
- TemplateFactoryIRNode,
- VaporDirectiveNode,
+ type TemplateFactoryIRNode,
+ type VaporDirectiveNode,
} from './ir'
export type NodeTransform = (
onWarn: defaultOnWarn,
}
+export const genDefaultDynamic = (): IRDynamicInfo => ({
+ id: null,
+ dynamicFlags: 0,
+ placeholder: null,
+ children: {},
+})
+
// TODO use class for better perf
function createRootContext(
root: RootIRNode,
increaseId: () => globalId++,
reference() {
if (this.dynamic.id !== null) return this.dynamic.id
- this.dynamic.referenced = true
+ this.dynamic.dynamicFlags |= DynamicFlag.REFERENCED
return (this.dynamic.id = this.increaseId())
},
registerEffect(expressions, operations) {
template: '',
childrenTemplate: [],
- dynamic: {
- id: null,
- referenced: false,
- ghost: false,
- placeholder: null,
- children: {},
- },
- })
+ dynamic: genDefaultDynamic(),
+ } satisfies Partial<TransformContext<T>>)
return ctx
}
loc: root.loc,
template: [],
templateIndex: -1,
- dynamic: {
- id: null,
- referenced: true,
- ghost: true,
- placeholder: null,
- children: {},
- },
+ dynamic: extend(genDefaultDynamic(), {
+ dynamicFlags: DynamicFlag.REFERENCED | DynamicFlag.INSERT,
+ } satisfies Partial<IRDynamicInfo>),
effect: [],
operation: [],
}
node = context.node
}
}
+
switch (node.type) {
case NodeTypes.ROOT:
case NodeTypes.ELEMENT: {
const childContext = createContext(child, ctx, i)
transformNode(childContext)
ctx.childrenTemplate.push(childContext.template)
- if (
- childContext.dynamic.ghost ||
- childContext.dynamic.referenced ||
- childContext.dynamic.placeholder ||
- Object.keys(childContext.dynamic.children).length
- ) {
- ctx.dynamic.children[i] = childContext.dynamic
- }
+ ctx.dynamic.children[i] = childContext.dynamic
}
processDynamicChildren(ctx)
for (let index = 0; index < node.children.length; index++) {
const child = ctx.dynamic.children[index]
- if (!child || !child.ghost) {
+ if (!child || !(child.dynamicFlags & DynamicFlag.INSERT)) {
if (prevChildren.length) {
if (hasStatic) {
ctx.childrenTemplate[index - prevChildren.length] = `<!>`
import { NodeTypes, type SimpleExpressionNode } from '@vue/compiler-dom'
import type { NodeTransform } from '../transform'
-import { IRNodeTypes } from '../ir'
+import { DynamicFlag, IRNodeTypes } from '../ir'
export const transformInterpolation: NodeTransform = (node, ctx) => {
if (node.type !== NodeTypes.INTERPOLATION) return
)
} else {
const id = ctx.reference()
- ctx.dynamic.ghost = true
+ ctx.dynamic.dynamicFlags |= DynamicFlag.INSERT
ctx.registerOperation({
type: IRNodeTypes.CREATE_TEXT_NODE,
loc: node.loc,
import {
type TransformContext,
createStructuralDirectiveTransform,
+ genDefaultDynamic,
} from '../transform'
import {
type BlockFunctionIRNode,
+ DynamicFlag,
+ type IRDynamicInfo,
IRNodeTypes,
type OperationNode,
type VaporDirectiveNode,
if (dir.name === 'if') {
const id = context.reference()
- context.dynamic.ghost = true
+ context.dynamic.dynamicFlags |= DynamicFlag.INSERT
const [branch, onExit] = createIfBranch(node, context)
return () => {
})
}
} else {
+ context.dynamic.dynamicFlags |= DynamicFlag.NON_TEMPLATE
+
// check the adjacent v-if
const parent = context.parent!
const siblings = parent.node.children
const templates = parent.childrenTemplate
+ const siblingsDynamic = parent.dynamic.children
const comments = []
let sibling: TemplateChildNode | undefined
while (i-- >= -1) {
sibling = siblings[i]
- if (sibling) {
- if (sibling.type === NodeTypes.COMMENT) {
- __DEV__ && comments.unshift(sibling)
- templates[i] = null
- continue
- } else if (
- sibling.type === NodeTypes.TEXT &&
- !sibling.content.trim().length
- ) {
- templates[i] = null
- continue
- }
+ if (
+ sibling &&
+ (sibling.type === NodeTypes.COMMENT ||
+ (sibling.type === NodeTypes.TEXT && !sibling.content.trim().length))
+ ) {
+ if (__DEV__ && sibling.type === NodeTypes.COMMENT)
+ comments.unshift(sibling)
+ siblingsDynamic[i].dynamicFlags |= DynamicFlag.NON_TEMPLATE
+ templates[i] = null
+ } else {
+ break
}
- break
}
const { operation } = context.block
loc: node.loc,
node,
templateIndex: -1,
- dynamic: {
- id: null,
- referenced: true,
- ghost: true,
- placeholder: null,
- children: {},
- },
+ dynamic: extend(genDefaultDynamic(), {
+ dynamicFlags: DynamicFlag.REFERENCED | DynamicFlag.INSERT,
+ } satisfies Partial<IRDynamicInfo>),
effect: [],
operation: [],
}
const exitBlock = context.enterBlock(branch)
context.reference()
const onExit = () => {
- context.template += context.childrenTemplate.join('')
+ context.template += context.childrenTemplate.filter(Boolean).join('')
context.registerTemplate()
exitBlock()
}