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 = ` + + + + + ` + 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 = ` + + +

+ + ` + 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 = ` + + + + + ` + 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) }) }