Skip to content

Commit

Permalink
fix(compiler-core): fix whitespace management for slots with whitespa…
Browse files Browse the repository at this point in the history
…ce: 'preserve' (#3767)

fix #3766
  • Loading branch information
HcySunYang authored May 13, 2021
1 parent f3d3036 commit 47da921
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,48 @@ return function render(_ctx, _cache) {
}))
}"
`;

exports[`compiler: transform component slots with whitespace: 'preserve' implicit default slot 1`] = `
"const { createVNode: _createVNode, resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = Vue
return function render(_ctx, _cache) {
const _component_Comp = _resolveComponent(\\"Comp\\")
return (_openBlock(), _createBlock(_component_Comp, null, {
header: _withCtx(() => [\\" Header \\"]),
default: _withCtx(() => [
\\" \\",
_createVNode(\\"p\\")
]),
_: 1 /* STABLE */
}))
}"
`;

exports[`compiler: transform component slots with whitespace: 'preserve' named default slot + implicit whitespace content 1`] = `
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = Vue
return function render(_ctx, _cache) {
const _component_Comp = _resolveComponent(\\"Comp\\")
return (_openBlock(), _createBlock(_component_Comp, null, {
header: _withCtx(() => [\\" Header \\"]),
default: _withCtx(() => [\\" Default \\"]),
_: 1 /* STABLE */
}))
}"
`;

exports[`compiler: transform component slots with whitespace: 'preserve' should not generate whitespace only default slot 1`] = `
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = Vue
return function render(_ctx, _cache) {
const _component_Comp = _resolveComponent(\\"Comp\\")
return (_openBlock(), _createBlock(_component_Comp, null, {
header: _withCtx(() => [\\" Header \\"]),
footer: _withCtx(() => [\\" Footer \\"]),
_: 1 /* STABLE */
}))
}"
`;
68 changes: 66 additions & 2 deletions packages/compiler-core/__tests__/transforms/vSlot.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import {
ForNode,
ComponentNode,
VNodeCall,
SlotsExpression
SlotsExpression,
ObjectExpression,
SimpleExpressionNode
} from '../../src'
import { transformElement } from '../../src/transforms/transformElement'
import { transformOn } from '../../src/transforms/vOn'
Expand All @@ -27,7 +29,9 @@ import { transformFor } from '../../src/transforms/vFor'
import { transformIf } from '../../src/transforms/vIf'

function parseWithSlots(template: string, options: CompilerOptions = {}) {
const ast = parse(template)
const ast = parse(template, {
whitespace: options.whitespace
})
transform(ast, {
nodeTransforms: [
transformIf,
Expand Down Expand Up @@ -862,4 +866,64 @@ describe('compiler: transform component slots', () => {
})
})
})

describe(`with whitespace: 'preserve'`, () => {
test('named default slot + implicit whitespace content', () => {
const source = `
<Comp>
<template #header> Header </template>
<template #default> Default </template>
</Comp>
`
const { root } = parseWithSlots(source, {
whitespace: 'preserve'
})

expect(
`Extraneous children found when component already has explicitly named default slot.`
).not.toHaveBeenWarned()
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
})

test('implicit default slot', () => {
const source = `
<Comp>
<template #header> Header </template>
<p/>
</Comp>
`
const { root } = parseWithSlots(source, {
whitespace: 'preserve'
})

expect(
`Extraneous children found when component already has explicitly named default slot.`
).not.toHaveBeenWarned()
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
})

test('should not generate whitespace only default slot', () => {
const source = `
<Comp>
<template #header> Header </template>
<template #footer> Footer </template>
</Comp>
`
const { root } = parseWithSlots(source, {
whitespace: 'preserve'
})

// slots is vnodeCall's children as an ObjectExpression
const slots = (root as any).children[0].codegenNode.children
.properties as ObjectExpression['properties']

// should be: header, footer, _ (no default)
expect(slots.length).toBe(3)
expect(
slots.some(p => (p.key as SimpleExpressionNode).content === 'default')
).toBe(false)

expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
})
})
})
16 changes: 15 additions & 1 deletion packages/compiler-core/src/transforms/vSlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,13 @@ export function buildSlots(
if (!hasTemplateSlots) {
// implicit default slot (on component)
slotsProperties.push(buildDefaultSlotProperty(undefined, children))
} else if (implicitDefaultChildren.length) {
} else if (
implicitDefaultChildren.length &&
// #3766
// with whitespace: 'preserve', whitespaces between slots will end up in
// implicitDefaultChildren. Ignore if all implicit children are whitespaces.
implicitDefaultChildren.some(node => isNonWhitespaceContent(node))
) {
// implicit default slot (mixed with named slots)
if (hasNamedDefaultSlot) {
context.onError(
Expand Down Expand Up @@ -397,3 +403,11 @@ function hasForwardedSlots(children: TemplateChildNode[]): boolean {
}
return false
}

function isNonWhitespaceContent(node: TemplateChildNode): boolean {
if (node.type !== NodeTypes.TEXT && node.type !== NodeTypes.TEXT_CALL)
return true
return node.type === NodeTypes.TEXT
? !!node.content.trim()
: isNonWhitespaceContent(node.content)
}

0 comments on commit 47da921

Please sign in to comment.