fix(compiler-core): fix whitespace management for slots with whitespace: 'preserve' (#3767)
fix #3766
This commit is contained in:
parent
f3d30363ec
commit
47da92146c
@ -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 */
|
||||||
|
}))
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
@ -9,7 +9,9 @@ import {
|
|||||||
ForNode,
|
ForNode,
|
||||||
ComponentNode,
|
ComponentNode,
|
||||||
VNodeCall,
|
VNodeCall,
|
||||||
SlotsExpression
|
SlotsExpression,
|
||||||
|
ObjectExpression,
|
||||||
|
SimpleExpressionNode
|
||||||
} from '../../src'
|
} from '../../src'
|
||||||
import { transformElement } from '../../src/transforms/transformElement'
|
import { transformElement } from '../../src/transforms/transformElement'
|
||||||
import { transformOn } from '../../src/transforms/vOn'
|
import { transformOn } from '../../src/transforms/vOn'
|
||||||
@ -27,7 +29,9 @@ import { transformFor } from '../../src/transforms/vFor'
|
|||||||
import { transformIf } from '../../src/transforms/vIf'
|
import { transformIf } from '../../src/transforms/vIf'
|
||||||
|
|
||||||
function parseWithSlots(template: string, options: CompilerOptions = {}) {
|
function parseWithSlots(template: string, options: CompilerOptions = {}) {
|
||||||
const ast = parse(template)
|
const ast = parse(template, {
|
||||||
|
whitespace: options.whitespace
|
||||||
|
})
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
nodeTransforms: [
|
nodeTransforms: [
|
||||||
transformIf,
|
transformIf,
|
||||||
@ -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()
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -311,7 +311,13 @@ export function buildSlots(
|
|||||||
if (!hasTemplateSlots) {
|
if (!hasTemplateSlots) {
|
||||||
// implicit default slot (on component)
|
// implicit default slot (on component)
|
||||||
slotsProperties.push(buildDefaultSlotProperty(undefined, children))
|
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)
|
// implicit default slot (mixed with named slots)
|
||||||
if (hasNamedDefaultSlot) {
|
if (hasNamedDefaultSlot) {
|
||||||
context.onError(
|
context.onError(
|
||||||
@ -397,3 +403,11 @@ function hasForwardedSlots(children: TemplateChildNode[]): boolean {
|
|||||||
}
|
}
|
||||||
return false
|
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)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user