From df38770bfbfb00f0af0bc4b002e647e5099ce7e5 Mon Sep 17 00:00:00 2001
From: zhiyuanzmj <260480378@qq.com>
Date: Sun, 9 Mar 2025 08:53:37 +0800
Subject: [PATCH 1/5] feat(compiler-vapor): remove unused empty DOM from slots
when whitespace is preserve
---
.../__snapshots__/vSlot.spec.ts.snap | 65 +++++++++++++++++++
.../__tests__/transforms/vSlot.spec.ts | 56 ++++++++++++++++
.../compiler-vapor/src/transforms/vSlot.ts | 18 +++--
3 files changed, 134 insertions(+), 5 deletions(-)
diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
index bab5c104605..5357027052f 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
@@ -249,3 +249,68 @@ export function render(_ctx) {
return n1
}"
`;
+
+exports[`compiler: transform slot > with whitespace: 'preserve' > implicit default slot 1`] = `
+"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+const t0 = _template(" Header ")
+const t1 = _template(" ")
+const t2 = _template("
")
+
+export function render(_ctx) {
+ const _component_Comp = _resolveComponent("Comp")
+ const n4 = _createComponentWithFallback(_component_Comp, null, {
+ "header": () => {
+ const n0 = t0()
+ return n0
+ },
+ "default": () => {
+ const n2 = t1()
+ const n3 = t2()
+ return [n2, n3]
+ }
+ }, true)
+ return n4
+}"
+`;
+
+exports[`compiler: transform slot > with whitespace: 'preserve' > named default slot + implicit whitespace content 1`] = `
+"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+const t0 = _template(" Header ")
+const t1 = _template(" Default ")
+
+export function render(_ctx) {
+ const _component_Comp = _resolveComponent("Comp")
+ const n4 = _createComponentWithFallback(_component_Comp, null, {
+ "header": () => {
+ const n0 = t0()
+ return n0
+ },
+ "default": () => {
+ const n2 = t1()
+ return n2
+ }
+ }, true)
+ return n4
+}"
+`;
+
+exports[`compiler: transform slot > with whitespace: 'preserve' > should not generate whitespace only default slot 1`] = `
+"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+const t0 = _template(" Header ")
+const t1 = _template(" Footer ")
+
+export function render(_ctx) {
+ const _component_Comp = _resolveComponent("Comp")
+ const n4 = _createComponentWithFallback(_component_Comp, null, {
+ "header": () => {
+ const n0 = t0()
+ return n0
+ },
+ "footer": () => {
+ const n2 = t1()
+ return n2
+ }
+ }, true)
+ return n4
+}"
+`;
diff --git a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts
index caa45681a24..2e70298a864 100644
--- a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts
+++ b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts
@@ -519,4 +519,60 @@ describe('compiler: transform slot', () => {
})
})
})
+
+ describe(`with whitespace: 'preserve'`, () => {
+ test('named default slot + implicit whitespace content', () => {
+ const source = `
+
+ Header
+ Default
+
+ `
+ const { code } = compileWithSlots(source, {
+ whitespace: 'preserve',
+ })
+
+ expect(
+ `Extraneous children found when component already has explicitly named default slot.`,
+ ).not.toHaveBeenWarned()
+ // expect(code).toMatchSnapshot()
+ expect(code).toMatchSnapshot()
+ })
+
+ test('implicit default slot', () => {
+ const source = `
+
+ Header
+
+
+ `
+ const { code } = compileWithSlots(source, {
+ whitespace: 'preserve',
+ })
+
+ expect(
+ `Extraneous children found when component already has explicitly named default slot.`,
+ ).not.toHaveBeenWarned()
+ expect(code).toMatchSnapshot()
+ })
+
+ test('should not generate whitespace only default slot', () => {
+ const source = `
+
+ Header
+ Footer
+
+ `
+ const { code, ir } = compileWithSlots(source, {
+ whitespace: 'preserve',
+ })
+
+ const slots = (ir.block.operation[0] as any).slots[0].slots
+ // should be: header, footer (no default)
+ expect(Object.keys(slots).length).toBe(2)
+ expect(!!slots['default']).toBe(false)
+
+ expect(code).toMatchSnapshot()
+ })
+ })
})
diff --git a/packages/compiler-vapor/src/transforms/vSlot.ts b/packages/compiler-vapor/src/transforms/vSlot.ts
index d1bf1c6b05f..a375b79a94e 100644
--- a/packages/compiler-vapor/src/transforms/vSlot.ts
+++ b/packages/compiler-vapor/src/transforms/vSlot.ts
@@ -66,11 +66,19 @@ function transformComponentSlot(
) {
const { children } = node
const arg = dir && dir.arg
- const nonSlotTemplateChildren = children.filter(
- n =>
- isNonWhitespaceContent(node) &&
- !(n.type === NodeTypes.ELEMENT && n.props.some(isVSlot)),
- )
+
+ // whitespace: 'preserve'
+ let indexes: number[] = []
+ const nonSlotTemplateChildren = children.filter((n, i) => {
+ if (isNonWhitespaceContent(n)) {
+ return !(n.type === NodeTypes.ELEMENT && n.props.some(isVSlot))
+ } else {
+ indexes.push(i)
+ }
+ })
+ if (!nonSlotTemplateChildren.length) {
+ indexes.forEach(i => children.splice(i, 1))
+ }
const [block, onExit] = createSlotBlock(node, dir, context)
From 622c684eb5e80c47c8b69957116408c76f7032dd Mon Sep 17 00:00:00 2001
From: zhiyuanzmj <260480378@qq.com>
Date: Mon, 10 Mar 2025 15:52:51 +0800
Subject: [PATCH 2/5] fix: typo
---
packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts
index 2e70298a864..d02e543e116 100644
--- a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts
+++ b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts
@@ -535,7 +535,6 @@ describe('compiler: transform slot', () => {
expect(
`Extraneous children found when component already has explicitly named default slot.`,
).not.toHaveBeenWarned()
- // expect(code).toMatchSnapshot()
expect(code).toMatchSnapshot()
})
From e837a90b7cf824c32fe9fe0300fab030607e84a1 Mon Sep 17 00:00:00 2001
From: zhiyuanzmj <260480378@qq.com>
Date: Sat, 15 Mar 2025 16:18:09 +0800
Subject: [PATCH 3/5] fix: test
---
packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts
index 632f5f6db03..a066c7c9af5 100644
--- a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts
+++ b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts
@@ -545,7 +545,8 @@ describe('compiler: transform slot', () => {
whitespace: 'preserve',
})
- const slots = (ir.block.operation[0] as any).slots[0].slots
+ const slots = (ir.block.dynamic.children[0].operation as any).slots[0]
+ .slots
// should be: header, footer (no default)
expect(Object.keys(slots).length).toBe(2)
expect(!!slots['default']).toBe(false)
From f347af223e19998e15156c08ba12ac1b38178890 Mon Sep 17 00:00:00 2001
From: zhiyuanzmj <260480378@qq.com>
Date: Wed, 18 Jun 2025 09:53:41 +0800
Subject: [PATCH 4/5] refactor: use seen
---
.../compiler-vapor/src/transforms/transformText.ts | 4 ++--
packages/compiler-vapor/src/transforms/vSlot.ts | 11 +++++++----
2 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/packages/compiler-vapor/src/transforms/transformText.ts b/packages/compiler-vapor/src/transforms/transformText.ts
index 7cdb1b1a78c..a1e8132c848 100644
--- a/packages/compiler-vapor/src/transforms/transformText.ts
+++ b/packages/compiler-vapor/src/transforms/transformText.ts
@@ -18,10 +18,10 @@ import {
} from '../utils'
type TextLike = TextNode | InterpolationNode
-const seen = new WeakMap<
+export const seen: WeakMap<
TransformContext,
WeakSet
->()
+> = new WeakMap()
export const transformText: NodeTransform = (node, context) => {
if (!seen.has(context.root)) seen.set(context.root, new WeakSet())
diff --git a/packages/compiler-vapor/src/transforms/vSlot.ts b/packages/compiler-vapor/src/transforms/vSlot.ts
index a375b79a94e..dde310414f8 100644
--- a/packages/compiler-vapor/src/transforms/vSlot.ts
+++ b/packages/compiler-vapor/src/transforms/vSlot.ts
@@ -24,6 +24,7 @@ import {
type VaporDirectiveNode,
} from '../ir'
import { findDir, resolveExpression } from '../utils'
+import { seen } from './transformText'
export const transformVSlot: NodeTransform = (node, context) => {
if (node.type !== NodeTypes.ELEMENT) return
@@ -68,16 +69,18 @@ function transformComponentSlot(
const arg = dir && dir.arg
// whitespace: 'preserve'
- let indexes: number[] = []
- const nonSlotTemplateChildren = children.filter((n, i) => {
+ const emptyTextNodes: TemplateChildNode[] = []
+ const nonSlotTemplateChildren = children.filter(n => {
if (isNonWhitespaceContent(n)) {
return !(n.type === NodeTypes.ELEMENT && n.props.some(isVSlot))
} else {
- indexes.push(i)
+ emptyTextNodes.push(n)
}
})
if (!nonSlotTemplateChildren.length) {
- indexes.forEach(i => children.splice(i, 1))
+ emptyTextNodes.forEach(n => {
+ seen.get(context.root)!.add(n)
+ })
}
const [block, onExit] = createSlotBlock(node, dir, context)
From ebb1ae6f0c6d0c1c90821f96a6205d1fe3d98f36 Mon Sep 17 00:00:00 2001
From: zhiyuanzmj <260480378@qq.com>
Date: Thu, 19 Jun 2025 23:26:59 +0800
Subject: [PATCH 5/5] refactor: use markNonTemplate
---
.../src/transforms/transformText.ts | 15 +++++++++++----
packages/compiler-vapor/src/transforms/vSlot.ts | 4 ++--
2 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/packages/compiler-vapor/src/transforms/transformText.ts b/packages/compiler-vapor/src/transforms/transformText.ts
index a1e8132c848..5f858058f27 100644
--- a/packages/compiler-vapor/src/transforms/transformText.ts
+++ b/packages/compiler-vapor/src/transforms/transformText.ts
@@ -18,10 +18,17 @@ import {
} from '../utils'
type TextLike = TextNode | InterpolationNode
-export const seen: WeakMap<
+const seen = new WeakMap<
TransformContext,
WeakSet
-> = new WeakMap()
+>()
+
+export function markNonTemplate(
+ node: TemplateChildNode,
+ context: TransformContext,
+): void {
+ seen.get(context.root)!.add(node)
+}
export const transformText: NodeTransform = (node, context) => {
if (!seen.has(context.root)) seen.set(context.root, new WeakSet())
@@ -68,7 +75,7 @@ export const transformText: NodeTransform = (node, context) => {
prev.type === NodeTypes.TEXT
) {
// mark leading text node for skipping
- seen.get(context.root)!.add(prev)
+ markNonTemplate(prev, context)
}
}
}
@@ -143,7 +150,7 @@ function processTextContainer(
}
function createTextLikeExpression(node: TextLike, context: TransformContext) {
- seen.get(context.root)!.add(node)
+ markNonTemplate(node, context)
if (node.type === NodeTypes.TEXT) {
return createSimpleExpression(node.content, true, node.loc)
} else {
diff --git a/packages/compiler-vapor/src/transforms/vSlot.ts b/packages/compiler-vapor/src/transforms/vSlot.ts
index dde310414f8..d1f08d614e5 100644
--- a/packages/compiler-vapor/src/transforms/vSlot.ts
+++ b/packages/compiler-vapor/src/transforms/vSlot.ts
@@ -24,7 +24,7 @@ import {
type VaporDirectiveNode,
} from '../ir'
import { findDir, resolveExpression } from '../utils'
-import { seen } from './transformText'
+import { markNonTemplate } from './transformText'
export const transformVSlot: NodeTransform = (node, context) => {
if (node.type !== NodeTypes.ELEMENT) return
@@ -79,7 +79,7 @@ function transformComponentSlot(
})
if (!nonSlotTemplateChildren.length) {
emptyTextNodes.forEach(n => {
- seen.get(context.root)!.add(n)
+ markNonTemplate(n, context)
})
}