`;
exports[`compile > directives > v-pre > self-closing v-pre 1`] = `
-"import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, effect as _effect, setAttr as _setAttr, setText as _setText } from 'vue/vapor';
+"import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, effect as _effect, setText as _setText, setAttr as _setAttr } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template(\\"<div></div><div><Comp></Comp></div>\\")
const n0 = t0()
- const { 1: [n1],} = _children(n0)
- const n2 = _createTextNode(bar)
- _append(n1, n2)
+ const { 1: [n2],} = _children(n0)
+ const n1 = _createTextNode(bar)
+ _append(n2, n1)
_effect(() => {
- _setAttr(n1, \\"id\\", undefined, foo)
+ _setText(n1, undefined, bar)
})
_effect(() => {
- _setText(n2, undefined, bar)
+ _setAttr(n2, \\"id\\", undefined, foo)
})
return n0
}"
`;
exports[`compile > directives > v-pre > should not affect siblings after it 1`] = `
-"import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, effect as _effect, setAttr as _setAttr, setText as _setText } from 'vue/vapor';
+"import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, effect as _effect, setText as _setText, setAttr as _setAttr } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template(\\"<div :id=\\\\\\"foo\\\\\\"><Comp></Comp>{{ bar }}</div><div><Comp></Comp></div>\\")
const n0 = t0()
- const { 1: [n1],} = _children(n0)
- const n2 = _createTextNode(bar.value)
- _append(n1, n2)
+ const { 1: [n2],} = _children(n0)
+ const n1 = _createTextNode(bar.value)
+ _append(n2, n1)
_effect(() => {
- _setAttr(n1, \\"id\\", undefined, foo.value)
+ _setText(n1, undefined, bar.value)
})
_effect(() => {
- _setText(n2, undefined, bar.value)
+ _setAttr(n2, \\"id\\", undefined, foo.value)
})
return n0
}"
`;
exports[`compile > dynamic root nodes and interpolation 1`] = `
-"import { template as _template, children as _children, createTextNode as _createTextNode, prepend as _prepend, insert as _insert, append as _append, effect as _effect, on as _on, setAttr as _setAttr, setText as _setText } from 'vue/vapor';
+"import { template as _template, children as _children, createTextNode as _createTextNode, prepend as _prepend, insert as _insert, append as _append, effect as _effect, setText as _setText, on as _on, setAttr as _setAttr } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template(\\"<button>foo<!>foo</button>\\")
const n0 = t0()
- const { 0: [n1, { 1: [n5],}],} = _children(n0)
+ const { 0: [n4, { 1: [n5],}],} = _children(n0)
+ const n1 = _createTextNode(count)
const n2 = _createTextNode(count)
const n3 = _createTextNode(count)
- const n4 = _createTextNode(count)
- _prepend(n1, n2)
- _insert(n3, n1, n5)
- _append(n1, n4)
+ _prepend(n4, n1)
+ _insert(n2, n4, n5)
+ _append(n4, n3)
_effect(() => {
- _on(n1, \\"click\\", handleClick)
- })
- _effect(() => {
- _setAttr(n1, \\"id\\", undefined, count)
+ _setText(n1, undefined, count)
})
_effect(() => {
_setText(n2, undefined, count)
_setText(n3, undefined, count)
})
_effect(() => {
- _setText(n4, undefined, count)
+ _on(n4, \\"click\\", handleClick)
+ })
+ _effect(() => {
+ _setAttr(n4, \\"id\\", undefined, count)
})
return n0
}"
type RootNode,
type TemplateChildNode,
type ElementNode,
- type AttributeNode,
type InterpolationNode,
type TransformOptions as BaseTransformOptions,
- type DirectiveNode,
type ParentNode,
type AllNode,
type CompilerCompatOptions,
NodeTypes,
defaultOnError,
defaultOnWarn,
- ErrorCodes,
- createCompilerError,
- DOMErrorCodes,
- createDOMCompilerError,
} from '@vue/compiler-dom'
-import { EMPTY_OBJ, NOOP, isArray, isVoidTag } from '@vue/shared'
+import { EMPTY_OBJ, NOOP, isArray } from '@vue/shared'
import {
type OperationNode,
type RootIRNode,
export type NodeTransform = (
node: RootNode | TemplateChildNode,
- context: TransformContext,
+ context: TransformContext<RootNode | TemplateChildNode>,
) => void | (() => void) | (() => void)[]
export type TransformOptions = HackOptions<BaseTransformOptions>
>
template: string
+ childrenTemplate: string[]
dynamic: IRDynamicInfo
inVOnce: boolean
},
template: '',
+ childrenTemplate: [],
registerTemplate() {
if (!ctx.template) return -1
index,
template: '',
+ childrenTemplate: [],
dynamic: {
id: null,
referenced: false,
}
const ctx = createRootContext(ir, root, options)
-
- // TODO: transform presets, see packages/compiler-core/src/transforms
transformNode(ctx)
+
+ if (ctx.node.type === NodeTypes.ROOT) {
+ ctx.registerTemplate()
+ }
if (ir.template.length === 0) {
ir.template.push({
type: IRNodeTypes.FRAGMENT_FACTORY,
const { nodeTransforms } = context.options
const exitFns = []
for (const nodeTransform of nodeTransforms) {
- // TODO nodeTransform type
- const onExit = nodeTransform(node, context as any)
+ const onExit = nodeTransform(node, context)
if (onExit) {
if (isArray(onExit)) {
exitFns.push(...onExit)
}
}
- if (node.type === NodeTypes.ROOT) {
- transformChildren(context as TransformContext<RootNode>)
- return
- }
-
- const parentChildren = context.parent!.node.children
+ const parentChildren = context.parent ? context.parent.node.children : []
const isFirst = index === 0
const isLast = index === parentChildren.length - 1
switch (node.type) {
+ case NodeTypes.ROOT:
case NodeTypes.ELEMENT: {
- transformElement(context as TransformContext<ElementNode>)
+ transformChildren(context as TransformContext<RootNode | ElementNode>)
break
}
case NodeTypes.TEXT: {
while (i--) {
exitFns[i]()
}
+
+ if (context.node.type === NodeTypes.ROOT)
+ context.template += context.childrenTemplate.join('')
}
function transformChildren(ctx: TransformContext<RootNode | ElementNode>) {
- const {
- node: { children },
- } = ctx
- const childrenTemplate: string[] = []
- children.forEach((child, index) => {
- const childContext = createContext(child, ctx, index)
+ const { children } = ctx.node
+ let i = 0
+ // const nodeRemoved = () => {
+ // i--
+ // }
+ for (; i < children.length; i++) {
+ const child = children[i]
+ const childContext = createContext(child, ctx, i)
transformNode(childContext)
-
- childrenTemplate.push(childContext.template)
+ ctx.childrenTemplate.push(childContext.template)
if (
childContext.dynamic.ghost ||
childContext.dynamic.referenced ||
childContext.dynamic.placeholder ||
Object.keys(childContext.dynamic.children).length
) {
- ctx.dynamic.children[index] = childContext.dynamic
- }
- })
-
- processDynamicChildren()
- ctx.template += childrenTemplate.join('')
-
- if (ctx.node.type === NodeTypes.ROOT) ctx.registerTemplate()
-
- function processDynamicChildren() {
- let prevChildren: IRDynamicInfo[] = []
- let hasStatic = false
- for (let index = 0; index < children.length; index++) {
- const child = ctx.dynamic.children[index]
-
- if (!child || !child.ghost) {
- if (prevChildren.length)
- if (hasStatic) {
- childrenTemplate[index - prevChildren.length] = `<!>`
- const anchor = (prevChildren[0].placeholder = ctx.increaseId())
-
- ctx.registerOperation({
- type: IRNodeTypes.INSERT_NODE,
- loc: ctx.node.loc,
- element: prevChildren.map((child) => child.id!),
- parent: ctx.reference(),
- anchor,
- })
- } else {
- ctx.registerOperation({
- type: IRNodeTypes.PREPEND_NODE,
- loc: ctx.node.loc,
- elements: prevChildren.map((child) => child.id!),
- parent: ctx.reference(),
- })
- }
- hasStatic = true
- prevChildren = []
- continue
- }
-
- prevChildren.push(child)
-
- if (index === children.length - 1) {
- ctx.registerOperation({
- type: IRNodeTypes.APPEND_NODE,
- loc: ctx.node.loc,
- elements: prevChildren.map((child) => child.id!),
- parent: ctx.reference(),
- })
- }
+ ctx.dynamic.children[i] = childContext.dynamic
}
}
+
+ processDynamicChildren(ctx)
}
-function transformElement(ctx: TransformContext<ElementNode>) {
+function processDynamicChildren(ctx: TransformContext<RootNode | ElementNode>) {
const { node } = ctx
- const { tag, props, children } = node
- ctx.template += `<${tag}`
+ let prevChildren: IRDynamicInfo[] = []
+ let hasStatic = false
+
+ for (let index = 0; index < node.children.length; index++) {
+ const child = ctx.dynamic.children[index]
+
+ if (!child || !child.ghost) {
+ if (prevChildren.length) {
+ if (hasStatic) {
+ ctx.childrenTemplate[index - prevChildren.length] = `<!>`
+ const anchor = (prevChildren[0].placeholder = ctx.increaseId())
- props.forEach((prop) => transformProp(prop, ctx))
- ctx.template += `>`
+ ctx.registerOperation({
+ type: IRNodeTypes.INSERT_NODE,
+ loc: node.loc,
+ element: prevChildren.map((child) => child.id!),
+ parent: ctx.reference(),
+ anchor,
+ })
+ } else {
+ ctx.registerOperation({
+ type: IRNodeTypes.PREPEND_NODE,
+ loc: node.loc,
+ elements: prevChildren.map((child) => child.id!),
+ parent: ctx.reference(),
+ })
+ }
+ }
+ hasStatic = true
+ prevChildren = []
+ continue
+ }
- if (children.length) transformChildren(ctx)
+ prevChildren.push(child)
- // TODO remove unnecessary close tag, e.g. if it's the last element of the template
- if (!isVoidTag(tag)) {
- ctx.template += `</${tag}>`
+ if (index === node.children.length - 1) {
+ ctx.registerOperation({
+ type: IRNodeTypes.APPEND_NODE,
+ loc: node.loc,
+ elements: prevChildren.map((child) => child.id!),
+ parent: ctx.reference(),
+ })
+ }
}
}
)
}
}
-
-function transformProp(
- node: DirectiveNode | AttributeNode,
- ctx: TransformContext<ElementNode>,
-): void {
- const { name } = node
-
- if (node.type === NodeTypes.ATTRIBUTE) {
- if (node.value) {
- ctx.template += ` ${name}="${node.value.content}"`
- } else {
- ctx.template += ` ${name}`
- }
- return
- }
-
- const { arg, exp, loc, modifiers } = node
-
- switch (name) {
- case 'bind': {
- if (
- !exp ||
- (exp.type === NodeTypes.SIMPLE_EXPRESSION && !exp.content.trim())
- ) {
- ctx.options.onError(
- createCompilerError(ErrorCodes.X_V_BIND_NO_EXPRESSION, loc),
- )
- return
- }
-
- if (exp === null) {
- // TODO: Vue 3.4 supported shorthand syntax
- // https://github.com/vuejs/core/pull/9451
- return
- } else if (!arg) {
- // TODO support v-bind="{}"
- return
- }
-
- ctx.registerEffect(
- [exp],
- [
- {
- type: IRNodeTypes.SET_PROP,
- loc: node.loc,
- element: ctx.reference(),
- name: arg,
- value: exp,
- },
- ],
- )
- break
- }
- case 'on': {
- if (!exp && !modifiers.length) {
- ctx.options.onError(
- createCompilerError(ErrorCodes.X_V_ON_NO_EXPRESSION, loc),
- )
- return
- }
-
- if (!arg) {
- // TODO support v-on="{}"
- return
- } else if (exp === undefined) {
- // TODO: support @foo
- // https://github.com/vuejs/core/pull/9451
- return
- }
-
- ctx.registerEffect(
- [exp],
- [
- {
- type: IRNodeTypes.SET_EVENT,
- loc: node.loc,
- element: ctx.reference(),
- name: arg,
- value: exp,
- modifiers,
- },
- ],
- )
- break
- }
- case 'html': {
- if (!exp) {
- ctx.options.onError(
- createDOMCompilerError(DOMErrorCodes.X_V_HTML_NO_EXPRESSION, loc),
- )
- }
- if (ctx.node.children.length) {
- ctx.options.onError(
- createDOMCompilerError(DOMErrorCodes.X_V_HTML_WITH_CHILDREN, loc),
- )
- ctx.node.children.length = 0
- }
-
- ctx.registerEffect(
- [exp],
- [
- {
- type: IRNodeTypes.SET_HTML,
- loc: node.loc,
- element: ctx.reference(),
- value: exp || '""',
- },
- ],
- )
- break
- }
- case 'text': {
- ctx.registerEffect(
- [exp],
- [
- {
- type: IRNodeTypes.SET_TEXT,
- loc: node.loc,
- element: ctx.reference(),
- value: exp || '""',
- },
- ],
- )
- break
- }
- case 'cloak': {
- // do nothing
- break
- }
- }
-}
--- /dev/null
+import {
+ type ElementNode,
+ type AttributeNode,
+ type DirectiveNode,
+ NodeTypes,
+ ErrorCodes,
+ createCompilerError,
+ DOMErrorCodes,
+ createDOMCompilerError,
+ ElementTypes,
+} from '@vue/compiler-dom'
+import { isVoidTag } from '@vue/shared'
+import { NodeTransform, TransformContext } from '../transform'
+import { IRNodeTypes } from '../ir'
+
+export const transformElement: NodeTransform = (node, ctx) => {
+ return function postTransformElement() {
+ node = ctx.node
+
+ if (
+ !(
+ node.type === NodeTypes.ELEMENT &&
+ (node.tagType === ElementTypes.ELEMENT ||
+ node.tagType === ElementTypes.COMPONENT)
+ )
+ ) {
+ return
+ }
+
+ const { tag, props } = node
+
+ ctx.template += `<${tag}`
+ props.forEach((prop) =>
+ transformProp(prop, ctx as TransformContext<ElementNode>),
+ )
+ ctx.template += `>`
+ ctx.template += ctx.childrenTemplate.join('')
+
+ // TODO remove unnecessary close tag, e.g. if it's the last element of the template
+ if (!isVoidTag(tag)) {
+ ctx.template += `</${tag}>`
+ }
+ }
+}
+
+function transformProp(
+ node: DirectiveNode | AttributeNode,
+ ctx: TransformContext<ElementNode>,
+): void {
+ const { name } = node
+
+ if (node.type === NodeTypes.ATTRIBUTE) {
+ if (node.value) {
+ ctx.template += ` ${name}="${node.value.content}"`
+ } else {
+ ctx.template += ` ${name}`
+ }
+ return
+ }
+
+ const { arg, exp, loc, modifiers } = node
+
+ switch (name) {
+ case 'bind': {
+ if (
+ !exp ||
+ (exp.type === NodeTypes.SIMPLE_EXPRESSION && !exp.content.trim())
+ ) {
+ ctx.options.onError(
+ createCompilerError(ErrorCodes.X_V_BIND_NO_EXPRESSION, loc),
+ )
+ return
+ }
+
+ if (exp === null) {
+ // TODO: Vue 3.4 supported shorthand syntax
+ // https://github.com/vuejs/core/pull/9451
+ return
+ } else if (!arg) {
+ // TODO support v-bind="{}"
+ return
+ }
+
+ ctx.registerEffect(
+ [exp],
+ [
+ {
+ type: IRNodeTypes.SET_PROP,
+ loc: node.loc,
+ element: ctx.reference(),
+ name: arg,
+ value: exp,
+ },
+ ],
+ )
+ break
+ }
+ case 'on': {
+ if (!exp && !modifiers.length) {
+ ctx.options.onError(
+ createCompilerError(ErrorCodes.X_V_ON_NO_EXPRESSION, loc),
+ )
+ return
+ }
+
+ if (!arg) {
+ // TODO support v-on="{}"
+ return
+ } else if (exp === undefined) {
+ // TODO: support @foo
+ // https://github.com/vuejs/core/pull/9451
+ return
+ }
+
+ ctx.registerEffect(
+ [exp],
+ [
+ {
+ type: IRNodeTypes.SET_EVENT,
+ loc: node.loc,
+ element: ctx.reference(),
+ name: arg,
+ value: exp,
+ modifiers,
+ },
+ ],
+ )
+ break
+ }
+ case 'html': {
+ if (!exp) {
+ ctx.options.onError(
+ createDOMCompilerError(DOMErrorCodes.X_V_HTML_NO_EXPRESSION, loc),
+ )
+ }
+ if (ctx.node.children.length) {
+ ctx.options.onError(
+ createDOMCompilerError(DOMErrorCodes.X_V_HTML_WITH_CHILDREN, loc),
+ )
+ ctx.childrenTemplate.length = 0
+ }
+
+ ctx.registerEffect(
+ [exp],
+ [
+ {
+ type: IRNodeTypes.SET_HTML,
+ loc: node.loc,
+ element: ctx.reference(),
+ value: exp || '""',
+ },
+ ],
+ )
+ break
+ }
+ case 'text': {
+ ctx.registerEffect(
+ [exp],
+ [
+ {
+ type: IRNodeTypes.SET_TEXT,
+ loc: node.loc,
+ element: ctx.reference(),
+ value: exp || '""',
+ },
+ ],
+ )
+ break
+ }
+ case 'cloak': {
+ // do nothing
+ break
+ }
+ }
+}