Explorar el Código

fix(compiler-vapor): preserve comment-only default slots while ignoring mixed slot comments (#14604)

edison hace 1 mes
padre
commit
c245dae45c

+ 29 - 0
packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts

@@ -3,6 +3,7 @@ import {
   IRNodeTypes,
   IRSlotType,
   transformChildren,
+  transformComment,
   transformElement,
   transformSlotOutlet,
   transformText,
@@ -22,6 +23,7 @@ const compileWithSlots = makeCompile({
     transformSlotOutlet,
     transformElement,
     transformVSlot,
+    transformComment,
     transformChildren,
   ],
   directiveTransforms: {
@@ -570,6 +572,33 @@ describe('compiler: transform slot', () => {
       expect(onError).not.toHaveBeenCalled()
     })
 
+    test('comment-only children should still generate implicit default slot', () => {
+      const { ir, code } = compileWithSlots(`<Comp><!--foo--></Comp>`)
+
+      expect(code).toContain(`<!--foo-->`)
+      expect(ir.block.dynamic.children[0].operation).toMatchObject({
+        type: IRNodeTypes.CREATE_COMPONENT_NODE,
+        slots: [
+          {
+            slotType: IRSlotType.STATIC,
+            slots: {
+              default: {
+                type: IRNodeTypes.BLOCK,
+              },
+            },
+          },
+        ],
+      })
+    })
+
+    test('comments should be excluded from mixed implicit default slot content', () => {
+      const { code } = compileWithSlots(
+        `<Comp><template #one>foo</template><!--bar--><span/></Comp>`,
+      )
+
+      expect(code).not.toContain(`<!--bar-->`)
+    })
+
     test('error on duplicated slot names', () => {
       const onError = vi.fn()
       const source = `<Comp><template #foo></template><template #foo></template></Comp>`

+ 20 - 1
packages/compiler-vapor/src/transforms/transformComment.ts

@@ -2,6 +2,7 @@ import {
   type CommentNode,
   type ElementNode,
   NodeTypes,
+  type RootNode,
   type TemplateChildNode,
   isCommentOrWhitespace,
 } from '@vue/compiler-dom'
@@ -9,10 +10,28 @@ import type { NodeTransform, TransformContext } from '../transform'
 import { DynamicFlag } from '../ir'
 import { escapeHtml } from '@vue/shared'
 
+const ignoredComments = new WeakMap<
+  TransformContext<RootNode>,
+  WeakSet<CommentNode>
+>()
+
+export function ignoreComment(
+  node: CommentNode,
+  context: TransformContext,
+): void {
+  let ignored = ignoredComments.get(context.root)
+  if (!ignored) {
+    ignoredComments.set(context.root, (ignored = new WeakSet()))
+  }
+  ignored.add(node)
+}
+
 export const transformComment: NodeTransform = (node, context) => {
   if (node.type !== NodeTypes.COMMENT) return
 
-  if (getSiblingIf(context as TransformContext<CommentNode>)) {
+  if (ignoredComments.get(context.root)?.has(node)) {
+    context.dynamic.flags |= DynamicFlag.NON_TEMPLATE
+  } else if (getSiblingIf(context as TransformContext<CommentNode>)) {
     context.comment.push(node)
     context.dynamic.flags |= DynamicFlag.NON_TEMPLATE
   } else {

+ 19 - 5
packages/compiler-vapor/src/transforms/vSlot.ts

@@ -7,7 +7,6 @@ import {
   type TemplateChildNode,
   createCompilerError,
   isTemplateNode,
-  isVSlot,
 } from '@vue/compiler-dom'
 import type { NodeTransform, TransformContext } from '../transform'
 import { newBlock } from './utils'
@@ -25,6 +24,7 @@ import {
 } from '../ir'
 import { findDir, resolveExpression } from '../utils'
 import { markNonTemplate } from './transformText'
+import { ignoreComment } from './transformComment'
 
 export const transformVSlot: NodeTransform = (node, context) => {
   if (node.type !== NodeTypes.ELEMENT) return
@@ -67,17 +67,23 @@ function transformComponentSlot(
 ) {
   const { children } = node
   const arg = dir && dir.arg
+  const hasTemplateSlots = children.some(isSlotTemplateChild)
 
   // whitespace: 'preserve'
   const emptyTextNodes: TemplateChildNode[] = []
   const nonSlotTemplateChildren = children.filter(n => {
+    if (isSlotTemplateChild(n)) {
+      return false
+    }
+    if (n.type === NodeTypes.COMMENT && hasTemplateSlots) {
+      ignoreComment(n, context)
+      return false
+    }
     if (isNonWhitespaceContent(n)) {
-      return !(
-        n.type === NodeTypes.COMMENT ||
-        (n.type === NodeTypes.ELEMENT && n.props.some(isVSlot))
-      )
+      return true
     } else {
       emptyTextNodes.push(n)
+      return false
     }
   })
   if (!nonSlotTemplateChildren.length) {
@@ -258,3 +264,11 @@ function isNonWhitespaceContent(node: TemplateChildNode): boolean {
   if (node.type !== NodeTypes.TEXT) return true
   return !!node.content.trim()
 }
+
+function isSlotTemplateChild(node: TemplateChildNode): node is ElementNode {
+  return (
+    node.type === NodeTypes.ELEMENT &&
+    isTemplateNode(node) &&
+    !!findDir(node, 'slot', true)
+  )
+}