diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/vSkip.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/vSkip.spec.ts.snap
new file mode 100644
index 00000000000..113926c9b4b
--- /dev/null
+++ b/packages/compiler-core/__tests__/transforms/__snapshots__/vSkip.spec.ts.snap
@@ -0,0 +1,263 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`compiler: v-skip > transform > basic 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+ with (_ctx) {
+ const { createCommentVNode: _createCommentVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
+
+ return (_ctx.ok)
+ ? _createCommentVNode("v-skip", true)
+ : (_openBlock(), _createElementBlock("div", { key: 1 }))
+ }
+}"
+`;
+
+exports[`compiler: v-skip > transform > nested v-skip 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+ with (_ctx) {
+ const { createCommentVNode: _createCommentVNode, openBlock: _openBlock, createElementBlock: _createElementBlock, createElementVNode: _createElementVNode, Fragment: _Fragment } = _Vue
+
+ return (_ctx.ok)
+ ? (_openBlock(), _createElementBlock(_Fragment, { key: 0 }, [
+ (_ctx.nested)
+ ? _createCommentVNode("v-skip", true)
+ : (_openBlock(), _createElementBlock("span", { key: 1 }))
+ ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
+ : (_openBlock(), _createElementBlock("div", { key: 1 }, [
+ (_ctx.nested)
+ ? _createCommentVNode("v-skip", true)
+ : (_openBlock(), _createElementBlock("span", { key: 1 }))
+ ]))
+ }
+}"
+`;
+
+exports[`compiler: v-skip > transform > on component 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+ with (_ctx) {
+ const { withCtx: _withCtx, createCommentVNode: _createCommentVNode, resolveComponent: _resolveComponent, openBlock: _openBlock, createBlock: _createBlock } = _Vue
+
+ const _component_Comp = _resolveComponent("Comp")
+
+ return (_ctx.ok)
+ ? _createCommentVNode("v-skip", true)
+ : (_openBlock(), _createBlock(_component_Comp, { key: 1 }))
+ }
+}"
+`;
+
+exports[`compiler: v-skip > transform > on component with default slot 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+ with (_ctx) {
+ const { withCtx: _withCtx, resolveComponent: _resolveComponent, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, createBlock: _createBlock } = _Vue
+
+ const _component_Comp = _resolveComponent("Comp")
+
+ return (_ctx.ok)
+ ? (_openBlock(), _createElementBlock(_Fragment, { key: 0 }, ["foo"], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
+ : (_openBlock(), _createBlock(_component_Comp, { key: 1 }, {
+ default: _withCtx(() => ["foo"]),
+ _: 1 /* STABLE */
+ }))
+ }
+}"
+`;
+
+exports[`compiler: v-skip > transform > on component with multiple implicit slot 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+ with (_ctx) {
+ const { withCtx: _withCtx, createElementVNode: _createElementVNode, resolveComponent: _resolveComponent, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, createBlock: _createBlock } = _Vue
+
+ const _component_Comp = _resolveComponent("Comp")
+
+ return (_ctx.ok)
+ ? (_openBlock(), _createElementBlock(_Fragment, { key: 0 }, [
+ _createElementVNode("span"),
+ _createElementVNode("div")
+ ], 64 /* STABLE_FRAGMENT */))
+ : (_openBlock(), _createBlock(_component_Comp, { key: 1 }, {
+ foo: _withCtx(() => ["foo"]),
+ default: _withCtx(() => [
+ _createElementVNode("span"),
+ _createElementVNode("div")
+ ]),
+ _: 1 /* STABLE */
+ }))
+ }
+}"
+`;
+
+exports[`compiler: v-skip > transform > on component with multiple named slot 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+ with (_ctx) {
+ const { withCtx: _withCtx, resolveComponent: _resolveComponent, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, createBlock: _createBlock } = _Vue
+
+ const _component_Comp = _resolveComponent("Comp")
+
+ return (_ctx.ok)
+ ? (_openBlock(), _createElementBlock(_Fragment, { key: 0 }, ["default"], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
+ : (_openBlock(), _createBlock(_component_Comp, { key: 1 }, {
+ default: _withCtx(() => ["default"]),
+ foo: _withCtx(() => ["foo"]),
+ _: 1 /* STABLE */
+ }))
+ }
+}"
+`;
+
+exports[`compiler: v-skip > transform > on dynamic component 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+ with (_ctx) {
+ const { withCtx: _withCtx, renderSlot: _renderSlot, resolveDynamicComponent: _resolveDynamicComponent, openBlock: _openBlock, createBlock: _createBlock } = _Vue
+
+ return (_ctx.ok)
+ ? _renderSlot(_ctx.$slots, "default", { key: 0 })
+ : (_openBlock(), _createBlock(_resolveDynamicComponent(_ctx.Comp), { key: 1 }, {
+ default: _withCtx(() => [
+ _renderSlot(_ctx.$slots, "default", { key: 0 })
+ ]),
+ _: 3 /* FORWARDED */
+ }))
+ }
+}"
+`;
+
+exports[`compiler: v-skip > transform > v-else + v-skip 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+ with (_ctx) {
+ const { openBlock: _openBlock, createElementBlock: _createElementBlock, createCommentVNode: _createCommentVNode, Fragment: _Fragment } = _Vue
+
+ return (_ctx.ok)
+ ? (_openBlock(), _createElementBlock("div", { key: 0 }))
+ : (_openBlock(), _createElementBlock(_Fragment, { key: 1 }, [
+ (_ctx.nested)
+ ? _createCommentVNode("v-skip", true)
+ : (_openBlock(), _createElementBlock("div", { key: 1 }))
+ ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
+ }
+}"
+`;
+
+exports[`compiler: v-skip > transform > v-else-if + v-skip 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+ with (_ctx) {
+ const { openBlock: _openBlock, createElementBlock: _createElementBlock, createCommentVNode: _createCommentVNode, Fragment: _Fragment } = _Vue
+
+ return (_ctx.ok)
+ ? (_openBlock(), _createElementBlock("div", { key: 0 }))
+ : (_ctx.yes)
+ ? (_openBlock(), _createElementBlock(_Fragment, { key: 1 }, [
+ (_ctx.nested)
+ ? _createCommentVNode("v-skip", true)
+ : (_openBlock(), _createElementBlock("div", { key: 1 }))
+ ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
+ : _createCommentVNode("v-if", true)
+ }
+}"
+`;
+
+exports[`compiler: v-skip > transform > v-if + v-skip 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+ with (_ctx) {
+ const { createCommentVNode: _createCommentVNode, openBlock: _openBlock, createElementBlock: _createElementBlock, Fragment: _Fragment } = _Vue
+
+ return (_ctx.ok)
+ ? (_openBlock(), _createElementBlock(_Fragment, { key: 0 }, [
+ (_ctx.nested)
+ ? _createCommentVNode("v-skip", true)
+ : (_openBlock(), _createElementBlock("div", { key: 1 }))
+ ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
+ : _createCommentVNode("v-if", true)
+ }
+}"
+`;
+
+exports[`compiler: v-skip > transform > with component children 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+ with (_ctx) {
+ const { resolveComponent: _resolveComponent, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createElementBlock: _createElementBlock } = _Vue
+
+ const _component_Comp = _resolveComponent("Comp")
+
+ return (_ctx.ok)
+ ? (_openBlock(), _createBlock(_component_Comp, { key: 0 }))
+ : (_openBlock(), _createElementBlock("div", { key: 1 }, [
+ (_openBlock(), _createBlock(_component_Comp, { key: 0 }))
+ ]))
+ }
+}"
+`;
+
+exports[`compiler: v-skip > transform > with element children 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+ with (_ctx) {
+ const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
+
+ return (_ctx.ok)
+ ? (_openBlock(), _createElementBlock("span", { key: 0 }))
+ : (_openBlock(), _createElementBlock("div", { key: 1 }, [
+ (_openBlock(), _createElementBlock("span", { key: 0 }))
+ ]))
+ }
+}"
+`;
+
+exports[`compiler: v-skip > transform > with multiple children 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+ with (_ctx) {
+ const { createElementVNode: _createElementVNode, resolveComponent: _resolveComponent, createVNode: _createVNode, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
+
+ const _component_Comp = _resolveComponent("Comp")
+
+ return (_ctx.ok)
+ ? (_openBlock(), _createElementBlock(_Fragment, { key: 0 }, [
+ _createElementVNode("span"),
+ _createVNode(_component_Comp)
+ ], 64 /* STABLE_FRAGMENT */))
+ : (_openBlock(), _createElementBlock("div", { key: 1 }, [
+ _createElementVNode("span"),
+ _createVNode(_component_Comp)
+ ]))
+ }
+}"
+`;
+
+exports[`compiler: v-skip > transform > with text children 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+ with (_ctx) {
+ const { Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
+
+ return (_ctx.ok)
+ ? (_openBlock(), _createElementBlock(_Fragment, { key: 0 }, ["foo"], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
+ : (_openBlock(), _createElementBlock("div", { key: 1 }, "foo"))
+ }
+}"
+`;
diff --git a/packages/compiler-core/__tests__/transforms/vSkip.spec.ts b/packages/compiler-core/__tests__/transforms/vSkip.spec.ts
index 595c62f1a2b..15572cb9e1f 100644
--- a/packages/compiler-core/__tests__/transforms/vSkip.spec.ts
+++ b/packages/compiler-core/__tests__/transforms/vSkip.spec.ts
@@ -1,15 +1,18 @@
import {
type CompilerOptions,
type ElementNode,
+ ElementTypes,
+ type IfBranchNode,
type IfNode,
NodeTypes,
type RootNode,
type SimpleExpressionNode,
type SkipNode,
+ generate,
baseParse as parse,
transform,
- transformBind,
transformElement,
+ transformExpression,
} from '@vue/compiler-core'
import { transformIf } from '../../src/transforms/vIf'
import { transformFor } from '../../src/transforms/vFor'
@@ -18,7 +21,7 @@ import { transformSkip } from '../../src/transforms/vSkip'
export function parseWithSkipTransform(
template: string,
- options: CompilerOptions = {},
+ options: CompilerOptions = { prefixIdentifiers: true },
): {
root: RootNode
node: SkipNode
@@ -29,12 +32,10 @@ export function parseWithSkipTransform(
transformIf,
transformSkip,
transformFor,
+ transformExpression,
transformSlotOutlet,
transformElement,
],
- directiveTransforms: {
- bind: transformBind,
- },
...options,
})
return {
@@ -46,102 +47,151 @@ export function parseWithSkipTransform(
describe('compiler: v-skip', () => {
describe('transform', () => {
test('basic', () => {
- const { node } = parseWithSkipTransform(`
`)
+ const { root, node } = parseWithSkipTransform(``)
expect(node.type).toBe(NodeTypes.SKIP)
- expect((node.test as SimpleExpressionNode).content).toBe(`ok`)
- expect(node.consequent.children.length).toBe(0)
+ expect((node.test as SimpleExpressionNode).content).toBe(`_ctx.ok`)
+ expect(node.consequent.type === NodeTypes.JS_CALL_EXPRESSION).toBe(true)
expect(node.alternate.children.length).toBe(1)
expect(node.alternate.children[0].type).toBe(NodeTypes.ELEMENT)
expect((node.alternate.children[0] as ElementNode).tag).toBe(`div`)
+ expect(generate(root).code).toMatchSnapshot()
})
test('with text children', () => {
- const { node } = parseWithSkipTransform(`foo
`)
+ const { root, node } = parseWithSkipTransform(
+ `foo
`,
+ )
expect(node.type).toBe(NodeTypes.SKIP)
- expect((node.test as SimpleExpressionNode).content).toBe(`ok`)
- expect(node.consequent.children.length).toBe(1)
- expect(node.consequent.children[0].type).toBe(NodeTypes.TEXT)
- expect((node.consequent.children[0] as any).content).toBe(`foo`)
+ expect((node.test as SimpleExpressionNode).content).toBe(`_ctx.ok`)
+ expect((node.consequent as IfBranchNode).children.length).toBe(1)
+ expect((node.consequent as IfBranchNode).children[0].type).toBe(
+ NodeTypes.TEXT,
+ )
+ expect(
+ ((node.consequent as IfBranchNode).children[0] as any).content,
+ ).toBe(`foo`)
expect(node.alternate.children.length).toBe(1)
expect(node.alternate.children[0].type).toBe(NodeTypes.ELEMENT)
expect((node.alternate.children[0] as ElementNode).tag).toBe(`div`)
+ expect(generate(root).code).toMatchSnapshot()
})
test('with element children', () => {
- const { node } = parseWithSkipTransform(`
`)
+ const { root, node } = parseWithSkipTransform(
+ `
`,
+ )
expect(node.type).toBe(NodeTypes.SKIP)
- expect((node.test as SimpleExpressionNode).content).toBe(`ok`)
- expect(node.consequent.children.length).toBe(1)
- expect(node.consequent.children[0].type).toBe(NodeTypes.ELEMENT)
- expect((node.consequent.children[0] as ElementNode).tag).toBe(`span`)
+ expect((node.test as SimpleExpressionNode).content).toBe(`_ctx.ok`)
+ expect((node.consequent as IfBranchNode).children.length).toBe(1)
+ expect((node.consequent as IfBranchNode).children[0].type).toBe(
+ NodeTypes.ELEMENT,
+ )
+ expect(
+ ((node.consequent as IfBranchNode).children[0] as ElementNode).tag,
+ ).toBe(`span`)
expect(node.alternate.children.length).toBe(1)
expect(node.alternate.children[0].type).toBe(NodeTypes.ELEMENT)
expect((node.alternate.children[0] as ElementNode).tag).toBe(`div`)
+ expect(generate(root).code).toMatchSnapshot()
})
test('with component children', () => {
- const { node } = parseWithSkipTransform(`
`)
+ const { root, node } = parseWithSkipTransform(
+ `
`,
+ )
expect(node.type).toBe(NodeTypes.SKIP)
- expect((node.test as SimpleExpressionNode).content).toBe(`ok`)
- expect(node.consequent.children.length).toBe(1)
- expect(node.consequent.children[0].type).toBe(NodeTypes.ELEMENT)
- expect((node.consequent.children[0] as ElementNode).tag).toBe(`Comp`)
+ expect((node.test as SimpleExpressionNode).content).toBe(`_ctx.ok`)
+ expect((node.consequent as IfBranchNode).children.length).toBe(1)
+ expect((node.consequent as IfBranchNode).children[0].type).toBe(
+ NodeTypes.ELEMENT,
+ )
+ expect(
+ ((node.consequent as IfBranchNode).children[0] as ElementNode).tag,
+ ).toBe(`Comp`)
expect(node.alternate.children.length).toBe(1)
expect(node.alternate.children[0].type).toBe(NodeTypes.ELEMENT)
expect((node.alternate.children[0] as ElementNode).tag).toBe(`div`)
+ expect(generate(root).code).toMatchSnapshot()
})
test('with multiple children', () => {
- const { node } = parseWithSkipTransform(
+ const { root, node } = parseWithSkipTransform(
`
`,
)
expect(node.type).toBe(NodeTypes.SKIP)
- expect((node.test as SimpleExpressionNode).content).toBe(`ok`)
- expect(node.consequent.children.length).toBe(2)
- expect(node.consequent.children[0].type).toBe(NodeTypes.ELEMENT)
- expect((node.consequent.children[0] as ElementNode).tag).toBe(`span`)
- expect(node.consequent.children[1].type).toBe(NodeTypes.ELEMENT)
- expect((node.consequent.children[1] as ElementNode).tag).toBe(`Comp`)
+ expect((node.test as SimpleExpressionNode).content).toBe(`_ctx.ok`)
+ expect((node.consequent as IfBranchNode).children.length).toBe(2)
+ expect((node.consequent as IfBranchNode).children[0].type).toBe(
+ NodeTypes.ELEMENT,
+ )
+ expect(
+ ((node.consequent as IfBranchNode).children[0] as ElementNode).tag,
+ ).toBe(`span`)
+ expect((node.consequent as IfBranchNode).children[1].type).toBe(
+ NodeTypes.ELEMENT,
+ )
+ expect(
+ ((node.consequent as IfBranchNode).children[1] as ElementNode).tag,
+ ).toBe(`Comp`)
expect(node.alternate.children.length).toBe(1)
expect(node.alternate.children[0].type).toBe(NodeTypes.ELEMENT)
expect((node.alternate.children[0] as ElementNode).tag).toBe(`div`)
+ expect(generate(root).code).toMatchSnapshot()
})
test('nested v-skip', () => {
- const { node } = parseWithSkipTransform(
- ``,
+ const { root, node } = parseWithSkipTransform(
+ `
`,
)
expect(node.type).toBe(NodeTypes.SKIP)
- expect((node.test as SimpleExpressionNode).content).toBe(`ok`)
- expect(node.consequent.children.length).toBe(1)
- expect(node.consequent.children[0].type).toBe(NodeTypes.SKIP)
+ expect((node.test as SimpleExpressionNode).content).toBe(`_ctx.ok`)
+ expect((node.consequent as IfBranchNode).children.length).toBe(1)
+ expect((node.consequent as IfBranchNode).children[0].type).toBe(
+ NodeTypes.SKIP,
+ )
expect(
- ((node.consequent.children[0] as SkipNode).test as SimpleExpressionNode)
- .content,
- ).toBe(`nested`)
+ (
+ ((node.consequent as IfBranchNode).children[0] as SkipNode)
+ .test as SimpleExpressionNode
+ ).content,
+ ).toBe(`_ctx.nested`)
expect(node.alternate.children.length).toBe(1)
expect(node.alternate.children[0].type).toBe(NodeTypes.ELEMENT)
expect((node.alternate.children[0] as ElementNode).tag).toBe(`div`)
+ const nestedNode = (node.consequent as IfBranchNode)
+ .children[0] as SkipNode
+ expect(nestedNode.type).toBe(NodeTypes.SKIP)
+ expect((nestedNode.test as SimpleExpressionNode).content).toBe(
+ `_ctx.nested`,
+ )
+ expect(nestedNode.consequent.type === NodeTypes.JS_CALL_EXPRESSION).toBe(
+ true,
+ )
+ expect(nestedNode.alternate.children.length).toBe(1)
+ expect(nestedNode.alternate.children[0].type).toBe(NodeTypes.ELEMENT)
+ expect((nestedNode.alternate.children[0] as ElementNode).tag).toBe(`span`)
+ expect(generate(root).code).toMatchSnapshot()
})
test('v-if + v-skip', () => {
- const { node } = parseWithSkipTransform(
+ const { root, node } = parseWithSkipTransform(
``,
)
expect(node.type).toBe(NodeTypes.IF)
const ifNode = node as unknown as IfNode
const branch = ifNode.branches[0]
- expect((branch.condition as SimpleExpressionNode).content).toBe(`ok`)
+ expect((branch.condition as SimpleExpressionNode).content).toBe(`_ctx.ok`)
expect(branch.children.length).toBe(1)
// skipNode
expect(branch.children[0].type).toBe(NodeTypes.SKIP)
expect(
((branch.children[0] as SkipNode).test as SimpleExpressionNode).content,
- ).toBe(`nested`)
+ ).toBe(`_ctx.nested`)
+ expect(generate(root).code).toMatchSnapshot()
})
test('v-else + v-skip', () => {
- const { node } = parseWithSkipTransform(
+ const { root, node } = parseWithSkipTransform(
``,
)
expect(node.type).toBe(NodeTypes.IF)
@@ -152,66 +202,89 @@ describe('compiler: v-skip', () => {
expect(branch.children[0].type).toBe(NodeTypes.SKIP)
expect(
((branch.children[0] as SkipNode).test as SimpleExpressionNode).content,
- ).toBe(`nested`)
+ ).toBe(`_ctx.nested`)
+ expect(generate(root).code).toMatchSnapshot()
})
test('v-else-if + v-skip', () => {
- const { node } = parseWithSkipTransform(
+ const { root, node } = parseWithSkipTransform(
``,
)
expect(node.type).toBe(NodeTypes.IF)
const elseIfNode = node as unknown as IfNode
const branch = elseIfNode.branches[1]
- expect((branch.condition as SimpleExpressionNode).content).toBe(`yes`)
+ expect((branch.condition as SimpleExpressionNode).content).toBe(
+ `_ctx.yes`,
+ )
expect(branch.children.length).toBe(1)
// skipNode
expect(branch.children[0].type).toBe(NodeTypes.SKIP)
expect(
((branch.children[0] as SkipNode).test as SimpleExpressionNode).content,
- ).toBe(`nested`)
+ ).toBe(`_ctx.nested`)
+ expect(generate(root).code).toMatchSnapshot()
})
test('on component', () => {
- const { node } = parseWithSkipTransform(``)
+ const { root, node } = parseWithSkipTransform(``)
expect(node.type).toBe(NodeTypes.SKIP)
- expect((node.test as SimpleExpressionNode).content).toBe(`ok`)
- expect(node.consequent.children.length).toBe(0)
+ expect((node.test as SimpleExpressionNode).content).toBe(`_ctx.ok`)
+ expect(node.consequent.type === NodeTypes.JS_CALL_EXPRESSION).toBe(true)
expect(node.alternate.children.length).toBe(1)
- expect(node.alternate.children[0].type).toBe(NodeTypes.ELEMENT)
+ expect((node.alternate.children[0] as ElementNode).tagType).toBe(
+ ElementTypes.COMPONENT,
+ )
expect((node.alternate.children[0] as ElementNode).tag).toBe(`Comp`)
+ expect(generate(root).code).toMatchSnapshot()
})
test('on component with default slot', () => {
- const { node } = parseWithSkipTransform(`foo`)
+ const { root, node } = parseWithSkipTransform(
+ `foo`,
+ )
expect(node.type).toBe(NodeTypes.SKIP)
- expect((node.test as SimpleExpressionNode).content).toBe(`ok`)
- expect(node.consequent.children.length).toBe(1)
- expect(node.consequent.children[0].type).toBe(NodeTypes.TEXT)
- expect((node.consequent.children[0] as any).content).toBe(`foo`)
+ expect((node.test as SimpleExpressionNode).content).toBe(`_ctx.ok`)
+ expect((node.consequent as IfBranchNode).children.length).toBe(1)
+ expect((node.consequent as IfBranchNode).children[0].type).toBe(
+ NodeTypes.TEXT,
+ )
+ expect(
+ ((node.consequent as IfBranchNode).children[0] as any).content,
+ ).toBe(`foo`)
expect(node.alternate.children.length).toBe(1)
- expect(node.alternate.children[0].type).toBe(NodeTypes.ELEMENT)
+ expect((node.alternate.children[0] as ElementNode).tagType).toBe(
+ ElementTypes.COMPONENT,
+ )
expect((node.alternate.children[0] as ElementNode).tag).toBe(`Comp`)
+ expect(generate(root).code).toMatchSnapshot()
})
test('on component with multiple named slot', () => {
- const { node } = parseWithSkipTransform(
+ const { root, node } = parseWithSkipTransform(
`
default
foo
`,
)
expect(node.type).toBe(NodeTypes.SKIP)
- expect((node.test as SimpleExpressionNode).content).toBe(`ok`)
- expect(node.consequent.children.length).toBe(1)
- expect(node.consequent.children[0].type).toBe(NodeTypes.TEXT)
- expect((node.consequent.children[0] as any).content).toBe(`default`)
+ expect((node.test as SimpleExpressionNode).content).toBe(`_ctx.ok`)
+ expect((node.consequent as IfBranchNode).children.length).toBe(1)
+ expect((node.consequent as IfBranchNode).children[0].type).toBe(
+ NodeTypes.TEXT,
+ )
+ expect(
+ ((node.consequent as IfBranchNode).children[0] as any).content,
+ ).toBe(`default`)
expect(node.alternate.children.length).toBe(1)
- expect(node.alternate.children[0].type).toBe(NodeTypes.ELEMENT)
+ expect((node.alternate.children[0] as ElementNode).tagType).toBe(
+ ElementTypes.COMPONENT,
+ )
expect((node.alternate.children[0] as ElementNode).tag).toBe(`Comp`)
+ expect(generate(root).code).toMatchSnapshot()
})
test('on component with multiple implicit slot', () => {
- const { node } = parseWithSkipTransform(
+ const { root, node } = parseWithSkipTransform(
`
foo
@@ -219,19 +292,51 @@ describe('compiler: v-skip', () => {
`,
)
expect(node.type).toBe(NodeTypes.SKIP)
- expect((node.test as SimpleExpressionNode).content).toBe(`ok`)
- expect(node.consequent.children.length).toBe(2)
- expect(node.consequent.children[0].type).toBe(NodeTypes.ELEMENT)
- expect((node.consequent.children[0] as ElementNode).tag).toBe(`span`)
- expect(node.consequent.children[1].type).toBe(NodeTypes.ELEMENT)
- expect((node.consequent.children[1] as ElementNode).tag).toBe(`div`)
+ expect((node.test as SimpleExpressionNode).content).toBe(`_ctx.ok`)
+ expect((node.consequent as IfBranchNode).children.length).toBe(2)
+ expect((node.consequent as IfBranchNode).children[0].type).toBe(
+ NodeTypes.ELEMENT,
+ )
+ expect(
+ ((node.consequent as IfBranchNode).children[0] as ElementNode).tag,
+ ).toBe(`span`)
+ expect((node.consequent as IfBranchNode).children[1].type).toBe(
+ NodeTypes.ELEMENT,
+ )
+ expect(
+ ((node.consequent as IfBranchNode).children[1] as ElementNode).tag,
+ ).toBe(`div`)
expect(node.alternate.children.length).toBe(1)
- expect(node.alternate.children[0].type).toBe(NodeTypes.ELEMENT)
+ expect((node.alternate.children[0] as ElementNode).tagType).toBe(
+ ElementTypes.COMPONENT,
+ )
expect((node.alternate.children[0] as ElementNode).tag).toBe(`Comp`)
+ expect(generate(root).code).toMatchSnapshot()
})
- })
- describe.todo('codegen', () => {})
+ test('on dynamic component', () => {
+ const { root, node } = parseWithSkipTransform(
+ `
+
+ `,
+ )
+ expect(node.type).toBe(NodeTypes.SKIP)
+ expect((node.test as SimpleExpressionNode).content).toBe(`_ctx.ok`)
+ expect((node.consequent as IfBranchNode).children.length).toBe(1)
+ expect((node.consequent as IfBranchNode).children[0].type).toBe(
+ NodeTypes.ELEMENT,
+ )
+ expect(
+ ((node.consequent as IfBranchNode).children[0] as ElementNode).tag,
+ ).toBe(`slot`)
+ expect(node.alternate.children.length).toBe(1)
+ expect((node.alternate.children[0] as ElementNode).tagType).toBe(
+ ElementTypes.COMPONENT,
+ )
+ expect((node.alternate.children[0] as ElementNode).tag).toBe(`component`)
+ expect(generate(root).code).toMatchSnapshot()
+ })
+ })
describe.todo('errors', () => {})
})
diff --git a/packages/compiler-core/src/ast.ts b/packages/compiler-core/src/ast.ts
index fed8cd2cfc2..d2402331184 100644
--- a/packages/compiler-core/src/ast.ts
+++ b/packages/compiler-core/src/ast.ts
@@ -413,7 +413,7 @@ export interface FunctionExpression extends Node {
export interface SkipNode extends Node {
type: NodeTypes.SKIP
test: ExpressionNode
- consequent: IfBranchNode
+ consequent: IfBranchNode | CallExpression
alternate: IfBranchNode
newline: boolean
codegenNode?: ConditionalExpression
@@ -468,7 +468,7 @@ export interface TemplateLiteral extends Node {
export interface IfStatement extends Node {
type: NodeTypes.JS_IF_STATEMENT
test: ExpressionNode
- consequent: BlockStatement
+ consequent: BlockStatement | CallExpression
alternate: IfStatement | BlockStatement | ReturnStatement | undefined
}
diff --git a/packages/compiler-core/src/transform.ts b/packages/compiler-core/src/transform.ts
index ca395b55fcd..bfdc5bb3cf4 100644
--- a/packages/compiler-core/src/transform.ts
+++ b/packages/compiler-core/src/transform.ts
@@ -467,7 +467,9 @@ export function traverseNode(
}
break
case NodeTypes.SKIP:
- traverseNode(node.consequent, context)
+ const { consequent } = node
+ if (consequent.type === NodeTypes.IF_BRANCH)
+ traverseNode(consequent, context)
traverseNode(node.alternate, context)
break
case NodeTypes.IF_BRANCH:
diff --git a/packages/compiler-core/src/transforms/vSkip.ts b/packages/compiler-core/src/transforms/vSkip.ts
index 6121dfaa0a2..2f3f4d07531 100644
--- a/packages/compiler-core/src/transforms/vSkip.ts
+++ b/packages/compiler-core/src/transforms/vSkip.ts
@@ -7,6 +7,7 @@ import {
type SimpleExpressionNode,
type SkipNode,
type TemplateChildNode,
+ createCallExpression,
createConditionalExpression,
createSimpleExpression,
} from '../ast'
@@ -16,6 +17,7 @@ import {
createStructuralDirectiveTransform,
} from '../transform'
import {
+ CREATE_COMMENT,
ErrorCodes,
buildSlots,
createCompilerError,
@@ -34,10 +36,16 @@ export const transformSkip: NodeTransform = createStructuralDirectiveTransform(
(node, dir, context) => {
return processSkip(node, dir, context, skipNode => {
return () => {
+ const { consequent, alternate, test } = skipNode
+ const consequentNode =
+ consequent.type === NodeTypes.IF_BRANCH
+ ? createCodegenNodeForBranch(consequent, 0, context)
+ : consequent
+
skipNode.codegenNode = createConditionalExpression(
- dir.exp!,
- createCodegenNodeForBranch(skipNode.consequent, 0, context),
- createCodegenNodeForBranch(skipNode.alternate, 1, context),
+ test,
+ consequentNode,
+ createCodegenNodeForBranch(alternate, 1, context),
)
}
})
@@ -100,13 +108,20 @@ export function processSkip(
children = node.children
}
- const consequent: IfBranchNode = {
- type: NodeTypes.IF_BRANCH,
- loc: node.loc,
- condition: undefined,
- children,
- userKey: findProp(node, `key`),
- }
+ // if children is empty, create comment node
+ const consequent =
+ children.length !== 0
+ ? ({
+ type: NodeTypes.IF_BRANCH,
+ loc: node.loc,
+ condition: undefined,
+ children,
+ userKey: findProp(node, `key`),
+ } as IfBranchNode)
+ : createCallExpression(context.helper(CREATE_COMMENT), [
+ __DEV__ ? '"v-skip"' : '""',
+ 'true',
+ ])
const alternate: IfBranchNode = {
type: NodeTypes.IF_BRANCH,
diff --git a/packages/compiler-core/src/transforms/vSlot.ts b/packages/compiler-core/src/transforms/vSlot.ts
index 4c90dff1220..72d90120475 100644
--- a/packages/compiler-core/src/transforms/vSlot.ts
+++ b/packages/compiler-core/src/transforms/vSlot.ts
@@ -413,8 +413,10 @@ function hasForwardedSlots(children: TemplateChildNode[]): boolean {
if (hasForwardedSlots(child.branches)) return true
break
case NodeTypes.SKIP:
+ const consequent = child.consequent
if (
- hasForwardedSlots(child.consequent.children) ||
+ (consequent.type === NodeTypes.IF_BRANCH &&
+ hasForwardedSlots(consequent.children)) ||
hasForwardedSlots(child.alternate.children)
)
return true
diff --git a/packages/compiler-core/src/utils.ts b/packages/compiler-core/src/utils.ts
index b350298ca10..faaddb2b2c0 100644
--- a/packages/compiler-core/src/utils.ts
+++ b/packages/compiler-core/src/utils.ts
@@ -529,9 +529,11 @@ export function hasScopeRef(
case NodeTypes.IF:
return node.branches.some(b => hasScopeRef(b, ids))
case NodeTypes.SKIP:
+ const { consequent } = node
return (
hasScopeRef(node.test, ids) ||
- node.consequent.children.some(c => hasScopeRef(c, ids)) ||
+ (consequent.type === NodeTypes.IF_BRANCH &&
+ consequent.children.some(c => hasScopeRef(c, ids))) ||
node.alternate.children.some(c => hasScopeRef(c, ids))
)
case NodeTypes.IF_BRANCH:
diff --git a/packages/compiler-ssr/src/transforms/ssrVSkip.ts b/packages/compiler-ssr/src/transforms/ssrVSkip.ts
index 751d3336f03..e0780fa0c42 100644
--- a/packages/compiler-ssr/src/transforms/ssrVSkip.ts
+++ b/packages/compiler-ssr/src/transforms/ssrVSkip.ts
@@ -1,5 +1,6 @@
import {
type NodeTransform,
+ NodeTypes,
type SkipNode,
createIfStatement,
createStructuralDirectiveTransform,
@@ -15,10 +16,18 @@ export function ssrProcessSkip(
node: SkipNode,
context: SSRTransformContext,
): void {
+ const { consequent, alternate, test } = node
+
+ // if consequent is an if branch, process it as well
+ const consequentNode =
+ consequent.type === NodeTypes.IF_BRANCH
+ ? processIfBranch(consequent, context)
+ : consequent
+
const ifStatement = createIfStatement(
- node.test,
- processIfBranch(node.consequent, context),
- processIfBranch(node.alternate, context),
+ test,
+ consequentNode,
+ processIfBranch(alternate, context),
)
context.pushStatement(ifStatement)
}