]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
test: update
authordaiwei <daiwei521@126.com>
Thu, 23 Jan 2025 07:59:51 +0000 (15:59 +0800)
committerdaiwei <daiwei521@126.com>
Thu, 23 Jan 2025 08:19:09 +0000 (16:19 +0800)
packages/compiler-core/__tests__/transforms/__snapshots__/vSkip.spec.ts.snap [new file with mode: 0644]
packages/compiler-core/__tests__/transforms/vSkip.spec.ts
packages/compiler-core/src/ast.ts
packages/compiler-core/src/transform.ts
packages/compiler-core/src/transforms/vSkip.ts
packages/compiler-core/src/transforms/vSlot.ts
packages/compiler-core/src/utils.ts
packages/compiler-ssr/src/transforms/ssrVSkip.ts

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 (file)
index 0000000..113926c
--- /dev/null
@@ -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"))
+  }
+}"
+`;
index 595c62f1a2b18cffb537fdf840f471fcf6803931..15572cb9e1fef25e070c4bb0033af65a3b312fd9 100644 (file)
@@ -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(`<div v-skip="ok"/>`)
+      const { root, node } = parseWithSkipTransform(`<div v-skip="ok"/>`)
       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(`<div v-skip="ok">foo</div>`)
+      const { root, node } = parseWithSkipTransform(
+        `<div v-skip="ok">foo</div>`,
+      )
       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(`<div v-skip="ok"><span/></div>`)
+      const { root, node } = parseWithSkipTransform(
+        `<div v-skip="ok"><span/></div>`,
+      )
       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(`<div v-skip="ok"><Comp/></div>`)
+      const { root, node } = parseWithSkipTransform(
+        `<div v-skip="ok"><Comp/></div>`,
+      )
       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(
         `<div v-skip="ok"><span/><Comp/></div>`,
       )
       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(
-        `<div v-skip="ok"><div v-skip="nested"/></div>`,
+      const { root, node } = parseWithSkipTransform(
+        `<div v-skip="ok"><span v-skip="nested"/></div>`,
       )
       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(
         `<div v-if="ok" v-skip="nested"/>`,
       )
       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(
         `<div v-if="ok"/><div v-else v-skip="nested"/>`,
       )
       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(
         `<div v-if="ok"/><div v-else-if="yes" v-skip="nested"/>`,
       )
       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(`<Comp v-skip="ok"/>`)
+      const { root, node } = parseWithSkipTransform(`<Comp v-skip="ok"/>`)
       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(`<Comp v-skip="ok">foo</Comp>`)
+      const { root, node } = parseWithSkipTransform(
+        `<Comp v-skip="ok">foo</Comp>`,
+      )
       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(
         `<Comp v-skip="ok">
           <template #default>default</template>
           <template #foo>foo</template>
         </Comp>`,
       )
       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(
         `<Comp v-skip="ok">
           <span/>
           <template #foo>foo</template>
@@ -219,19 +292,51 @@ describe('compiler: v-skip', () => {
         </Comp>`,
       )
       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(
+        `<component :is="Comp" v-skip="ok">
+          <slot/>
+        </component>`,
+      )
+      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', () => {})
 })
index fed8cd2cfc2e7384d9d6703529e58f7df8ea8d72..d2402331184491a6967c4fdac994cb5d5b8a6e2b 100644 (file)
@@ -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
 }
 
index ca395b55fcd6e9719cc7f5ee8909d0628ceb4791..bfdc5bb3cf409cb5d5e0ee48b8b0fe81f8107313 100644 (file)
@@ -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:
index 6121dfaa0a2177d7ba572a61a8db48029f834aaa..2f3f4d07531b6c5e9b59d1eb564861bffd318fa5 100644 (file)
@@ -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,
index 4c90dff122012574dffb2da5c2a860f4b596bbdf..72d9012047528cb157ba312cbb63646f4e68be65 100644 (file)
@@ -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
index b350298ca107a14f822f7e8e765851e8eef4bc28..faaddb2b2c08aec1f361ce01dc93010e9a446eda 100644 (file)
@@ -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:
index 751d3336f0304336639dc0c42adb5c01778e44f4..e0780fa0c42787633bebf50628200b455644c013 100644 (file)
@@ -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)
 }