_createVNode("div", null, [
_createCommentVNode("[p"),
_renderSlot(_ctx.$slots, "foo"),
+ _createCommentVNode("slot"),
_createCommentVNode("p]"),
_createCommentVNode("[p"),
_renderSlot(_ctx.$slots, "default"),
+ _createCommentVNode("slot"),
_createCommentVNode("p]"),
_createVNode("span")
])
_createVNode("div", null, [
_createCommentVNode("[p"),
(_ctx.foo)
- ? (_openBlock(), _createBlock("span", { key: 0 }))
+ ? (_openBlock(), _createBlock("span", { key: 0 }, [
+ _createCommentVNode("if")
+ ]))
: (_ctx.bar)
- ? (_openBlock(), _createBlock("span", { key: 1 }))
- : (_openBlock(), _createBlock("span", { key: 2 })),
+ ? (_openBlock(), _createBlock("span", { key: 1 }, [
+ _createCommentVNode("if--><!--if")
+ ]))
+ : (_openBlock(), _createBlock("span", { key: 2 }, [
+ _createCommentVNode("if--><!--if")
+ ])),
_createCommentVNode("p]"),
_createVNode("span")
])
(_ctx.foo1)
? (_openBlock(), _createBlock("span", { key: 0 }))
: _createCommentVNode("v-if", true),
+ _createCommentVNode("if"),
_createCommentVNode("p]"),
- _createVNode("span")
+ _createVNode("span"),
+ _createCommentVNode("if")
]))
: (_ctx.bar)
? (_openBlock(), _createBlock("span", { key: 1 }, [
(_ctx.bar1)
? (_openBlock(), _createBlock("span", { key: 0 }))
: _createCommentVNode("v-if", true),
+ _createCommentVNode("if"),
_createCommentVNode("p]"),
- _createVNode("span")
+ _createVNode("span"),
+ _createCommentVNode("if--><!--if")
]))
: (_openBlock(), _createBlock("span", { key: 2 }, [
_createCommentVNode("[p"),
(_ctx.bar2)
? (_openBlock(), _createBlock("span", { key: 0 }))
: _createCommentVNode("v-if", true),
+ _createCommentVNode("if"),
_createCommentVNode("p]"),
- _createVNode("span")
+ _createVNode("span"),
+ _createCommentVNode("if--><!--if")
])),
_createCommentVNode("p]"),
_createVNode("span")
_createTextVNode(" foo ")
], 64 /* STABLE_FRAGMENT */))
: _createCommentVNode("v-if", true),
+ _createCommentVNode("if"),
_createCommentVNode("p]"),
_createVNode("div")
]))
- : _createCommentVNode("v-if", true)
+ : _createCommentVNode("v-if", true),
+ _createCommentVNode("if")
]
}
}),
(_openBlock(true), _createBlock(_Fragment, null, _renderList(_ctx.items, (item) => {
return (_openBlock(), _createBlock("span"))
}), 256 /* UNKEYED_FRAGMENT */)),
+ _createCommentVNode("for"),
_createCommentVNode("p]"),
_createVNode("span")
])
test('if', () => {
expect(
getCompiledString(
- `<span v-if="count === 1">1</span>
- <span v-else-if="count === 2">2</span>
- <span v-else-if="count === 3">3</span>
- <span v-else>4</span>`,
+ `<component :is="tag">
+ <span v-if="count === 1">1</span>
+ <span v-else-if="count === 2">2</span>
+ <span v-else-if="count === 3">3</span>
+ <span v-else>4</span>
+ </component>`,
{
vapor: true,
},
),
).toMatchInlineSnapshot(`
"\`<!--[a-->\`)
- if (_ctx.count === 1) {
- _push(\`<span>1</span>\`)
- _push(\`<!--if-->\`)
- } else if (_ctx.count === 2) {
- _push(\`<span>2</span>\`)
- _push(\`<!--if--><!--if-->\`)
- } else if (_ctx.count === 3) {
- _push(\`<span>3</span>\`)
- _push(\`<!--if--><!--if--><!--if-->\`)
- } else {
- _push(\`<span>4</span>\`)
- _push(\`<!--if--><!--if--><!--if-->\`)
- }
- _push(\`<!--a]-->\`"
+ _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent(_ctx.tag), null, {
+ default: _withCtx((_, _push, _parent, _scopeId) => {
+ if (_push) {
+ if (_ctx.count === 1) {
+ _push(\`<span\${_scopeId}>1</span>\`)
+ _push(\`<!--if-->\`)
+ } else if (_ctx.count === 2) {
+ _push(\`<span\${_scopeId}>2</span>\`)
+ _push(\`<!--if--><!--if-->\`)
+ } else if (_ctx.count === 3) {
+ _push(\`<span\${_scopeId}>3</span>\`)
+ _push(\`<!--if--><!--if--><!--if-->\`)
+ } else {
+ _push(\`<span\${_scopeId}>4</span>\`)
+ _push(\`<!--if--><!--if--><!--if-->\`)
+ }
+ } else {
+ return [
+ (_ctx.count === 1)
+ ? (_openBlock(), _createBlock("span", { key: 0 }, [
+ _createTextVNode("1"),
+ _createCommentVNode("if")
+ ]))
+ : (_ctx.count === 2)
+ ? (_openBlock(), _createBlock("span", { key: 1 }, [
+ _createTextVNode("2"),
+ _createCommentVNode("if--><!--if")
+ ]))
+ : (_ctx.count === 3)
+ ? (_openBlock(), _createBlock("span", { key: 2 }, [
+ _createTextVNode("3"),
+ _createCommentVNode("if--><!--if--><!--if")
+ ]))
+ : (_openBlock(), _createBlock("span", { key: 3 }, [
+ _createTextVNode("4"),
+ _createCommentVNode("if--><!--if--><!--if")
+ ]))
+ ]
+ }
+ }),
+ _: 1 /* STABLE */
+ }), _parent)
+ _push(\`<!--dynamic-component--><!--a]-->\`"
`)
})
- test('if in ssr slot vnode fallback', () => {})
-
- test('for', () => {})
-
- test('for in ssr slot vnode fallback', () => {})
-
- test('slot', () => {})
-
- test('slot in ssr slot vnode fallback', () => {})
+ test('for', () => {
+ expect(
+ getCompiledString(
+ `<component :is="tag">
+ <span v-for="item in items">{{item}}</span>
+ </component>`,
+ {
+ vapor: true,
+ },
+ ),
+ ).toMatchInlineSnapshot(`
+ "\`<!--[a-->\`)
+ _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent(_ctx.tag), null, {
+ default: _withCtx((_, _push, _parent, _scopeId) => {
+ if (_push) {
+ _push(\`<!--[-->\`)
+ _ssrRenderList(_ctx.items, (item) => {
+ _push(\`<span\${
+ _scopeId
+ }>\${
+ _ssrInterpolate(item)
+ }</span>\`)
+ })
+ _push(\`<!--]--><!--for-->\`)
+ } else {
+ return [
+ (_openBlock(true), _createBlock(_Fragment, null, _renderList(_ctx.items, (item) => {
+ return (_openBlock(), _createBlock("span", null, _toDisplayString(item), 1 /* TEXT */))
+ }), 256 /* UNKEYED_FRAGMENT */)),
+ _createCommentVNode("for")
+ ]
+ }
+ }),
+ _: 1 /* STABLE */
+ }), _parent)
+ _push(\`<!--dynamic-component--><!--a]-->\`"
+ `)
+ })
- test('forwarded slot', () => {})
+ test('slot', () => {
+ expect(
+ getCompiledString(
+ `<div>
+ <slot name="foo"/>
+ <slot/>
+ </div>`,
+ { vapor: true },
+ ),
+ ).toMatchInlineSnapshot(`
+ "\`<div><!--[a-->\`)
+ _ssrRenderSlot(_ctx.$slots, "foo", {}, null, _push, _parent)
+ _push(\`<!--slot--><!--a]--><!--[a-->\`)
+ _ssrRenderSlot(_ctx.$slots, "default", {}, null, _push, _parent)
+ _push(\`<!--slot--><!--a]--></div>\`"
+ `)
+ })
- test('dynamic component', () => {})
+ test('forwarded slot', () => {
+ expect(
+ getCompiledString(
+ `<component :is="tag">
+ <slot name="foo"/>
+ <slot/>
+ </component>`,
+ { vapor: true },
+ ),
+ ).toMatchInlineSnapshot(`
+ "\`<!--[a-->\`)
+ _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent(_ctx.tag), null, {
+ default: _withCtx((_, _push, _parent, _scopeId) => {
+ if (_push) {
+ _ssrRenderSlot(_ctx.$slots, "foo", {}, null, _push, _parent, _scopeId)
+ _push(\`<!--slot-->\`)
+ _ssrRenderSlot(_ctx.$slots, "default", {}, null, _push, _parent, _scopeId)
+ _push(\`<!--slot-->\`)
+ } else {
+ return [
+ _renderSlot(_ctx.$slots, "foo"),
+ _createCommentVNode("slot"),
+ _renderSlot(_ctx.$slots, "default"),
+ _createCommentVNode("slot")
+ ]
+ }
+ }),
+ _: 3 /* FORWARDED */
+ }), _parent)
+ _push(\`<!--dynamic-component--><!--a]-->\`"
+ `)
+ })
- test('dynamic in ssr slot vnode fallback', () => {})
+ test('dynamic component', () => {
+ expect(
+ getCompiledString(
+ `<component is='tag'>
+ <div>
+ <component is="foo"/>
+ </div>
+ </component>`,
+ { vapor: true },
+ ),
+ ).toMatchInlineSnapshot(`
+ "\`<!--[a-->\`)
+ _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent("tag"), null, {
+ default: _withCtx((_, _push, _parent, _scopeId) => {
+ if (_push) {
+ _push(\`<div\${_scopeId}><!--[a-->\`)
+ _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent("foo"), null, null), _parent, _scopeId)
+ _push(\`<!--dynamic-component--><!--a]--></div>\`)
+ } else {
+ return [
+ _createVNode("div", null, [
+ _createCommentVNode("[a"),
+ (_openBlock(), _createBlock(_resolveDynamicComponent("foo"))),
+ _createCommentVNode("dynamic-component"),
+ _createCommentVNode("a]")
+ ])
+ ]
+ }
+ }),
+ _: 1 /* STABLE */
+ }), _parent)
+ _push(\`<!--dynamic-component--><!--a]-->\`"
+ `)
+ })
})
import {
CREATE_VNODE,
type CallExpression,
+ type CommentNode,
type CompilerOptions,
type ComponentNode,
DOMDirectiveTransforms,
} from './ssrTransformTransitionGroup'
import {
DYNAMIC_COMPONENT_ANCHOR_LABEL,
+ FOR_ANCHOR_LABEL,
+ IF_ANCHOR_LABEL,
+ SLOT_ANCHOR_LABEL,
extend,
isArray,
isObject,
if (vFor) {
wrapperProps.push(extend({}, vFor))
}
-
if (parentContext.vapor) {
- children = injectVaporInsertionAnchors(children, parent)
+ children = injectVaporAnchors(children, parent)
}
const wrapperNode: TemplateNode = {
// - hoists are not enabled for the client branch here
}
-function injectVaporInsertionAnchors(
+function injectVaporAnchors(
children: TemplateChildNode[],
parent: TemplateChildNode,
): TemplateChildNode[] {
processBlockNodeAnchor(children)
}
- const newChildren: TemplateChildNode[] = new Array(children.length * 3)
- let newIndex = 0
+ const newChildren: TemplateChildNode[] = []
for (let i = 0; i < children.length; i++) {
const child = children[i]
if (child.type !== NodeTypes.ELEMENT) {
- newChildren[newIndex++] = child
+ newChildren.push(child)
continue
}
const { tagType, props } = child
- let anchor: string | undefined
+ let insertionAnchor: string | undefined
if (
tagType === ElementTypes.COMPONENT ||
tagType === ElementTypes.SLOT ||
tagType === ElementTypes.TEMPLATE
) {
- anchor = child.anchor
+ insertionAnchor = child.anchor
} else if (tagType === ElementTypes.ELEMENT) {
let hasIf = false
let hasFor = false
}
if (hasIf) {
- anchor = (child as any as IfNode).anchor
- if (anchor) {
- // find sibling else-if/else branches
- // inject anchor after else-if/else branch if founded
- // otherwise inject after if node
- const lastBranchIndex = findLastIfBranchIndex(children, i)
- if (lastBranchIndex > i) {
- // inject anchor before if node
- newChildren[newIndex++] = createAnchorComment(`[${anchor}`)
-
- // copy branch nodes
- for (let j = i; j <= lastBranchIndex; j++) {
- const node = children[j] as PlainElementNode
- newChildren[newIndex++] = node
-
- node.children = injectVaporInsertionAnchors(node.children, node)
+ insertionAnchor = (child as any as IfNode).anchor
+ // find sibling else-if/else branches
+ // inject anchor after else-if/else branch if founded
+ // otherwise inject after if node
+ const lastBranchIndex = findLastIfBranchIndex(children, i)
+ if (lastBranchIndex > i) {
+ // inject anchor before if node
+ if (insertionAnchor) {
+ newChildren.push(createAnchor(`[${insertionAnchor}`))
+ }
+
+ // copy branch nodes
+ for (let j = i; j <= lastBranchIndex; j++) {
+ const node = children[j] as PlainElementNode
+ newChildren.push(node)
+
+ // inject block anchor
+ const blockAnchorLabel = getBlockAnchorLabel(node)
+ if (blockAnchorLabel) {
+ const isElse = node.props.some(p => p.name === 'else')
+ const repeatCount = j - i - (isElse ? 1 : 0) + 1
+ node.children.push(
+ createAnchor(
+ `<!--${blockAnchorLabel}-->`.repeat(repeatCount).slice(4, -3),
+ ),
+ )
}
- // inject anchor after branch nodes
- newChildren[newIndex++] = createAnchorComment(`${anchor}]`)
+ node.children = injectVaporAnchors(node.children, node)
+ }
- i = lastBranchIndex
- continue
+ // inject anchor after branch nodes
+ if (insertionAnchor) {
+ newChildren.push(createAnchor(`${insertionAnchor}]`))
}
+
+ i = lastBranchIndex
+ continue
}
} else if (hasFor) {
- anchor = (child as any as ForNode).anchor
+ insertionAnchor = (child as any as ForNode).anchor
}
}
// inject anchor before and after the child
- if (anchor) newChildren[newIndex++] = createAnchorComment(`[${anchor}`)
- newChildren[newIndex++] = child
- if (anchor) newChildren[newIndex++] = createAnchorComment(`${anchor}]`)
+ if (insertionAnchor) {
+ newChildren.push(createAnchor(`[${insertionAnchor}`))
+ }
+
+ newChildren.push(child)
- child.children = injectVaporInsertionAnchors(child.children, child)
+ // inject block anchor
+ const blockAnchorLabel = getBlockAnchorLabel(child)
+ if (blockAnchorLabel) newChildren.push(createAnchor(blockAnchorLabel))
+
+ // inject insertion anchor
+ if (insertionAnchor) {
+ newChildren.push(createAnchor(`${insertionAnchor}]`))
+ }
+
+ child.children = injectVaporAnchors(child.children, child)
}
- newChildren.length = newIndex
return newChildren
}
-function createAnchorComment(content: string): TemplateChildNode {
+function createAnchor(content: string): CommentNode {
return {
type: NodeTypes.COMMENT,
content,
return lastIndex
}
+function getBlockAnchorLabel(child: TemplateChildNode): string | undefined {
+ if (child.type !== NodeTypes.ELEMENT) return
+
+ if (child.tagType === ElementTypes.COMPONENT && child.tag === 'component') {
+ return DYNAMIC_COMPONENT_ANCHOR_LABEL
+ } else if (child.tagType === ElementTypes.SLOT) {
+ return SLOT_ANCHOR_LABEL
+ } else if (
+ child.props.some(
+ p => p.name === 'if' || p.name === 'else-if' || p.name === 'else',
+ )
+ ) {
+ return IF_ANCHOR_LABEL
+ } else if (child.props.some(p => p.name === 'for')) {
+ return FOR_ANCHOR_LABEL
+ }
+}
+
function clone(v: any): any {
if (isArray(v)) {
return v.map(clone)