fix(compiler-core/slots): should support on-component named slots
This commit is contained in:
parent
20f4965b45
commit
a022b63605
@ -113,20 +113,6 @@ return function render(_ctx, _cache) {
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compiler: transform component slots named slots 1`] = `
|
|
||||||
"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
|
|
||||||
|
|
||||||
return function render(_ctx, _cache) {
|
|
||||||
const _component_Comp = _resolveComponent(\\"Comp\\")
|
|
||||||
|
|
||||||
return (_openBlock(), _createBlock(_component_Comp, null, {
|
|
||||||
one: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)]),
|
|
||||||
two: _withCtx(({ bar }) => [_toDisplayString(_ctx.foo), _toDisplayString(bar)]),
|
|
||||||
_: 1
|
|
||||||
}))
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`compiler: transform component slots named slots w/ implicit default slot 1`] = `
|
exports[`compiler: transform component slots named slots w/ implicit default slot 1`] = `
|
||||||
"const _Vue = Vue
|
"const _Vue = Vue
|
||||||
|
|
||||||
@ -171,6 +157,32 @@ return function render(_ctx, _cache) {
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: transform component slots on component dynamically named slot 1`] = `
|
||||||
|
"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
|
||||||
|
|
||||||
|
return function render(_ctx, _cache) {
|
||||||
|
const _component_Comp = _resolveComponent(\\"Comp\\")
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(_component_Comp, null, {
|
||||||
|
[_ctx.named]: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)]),
|
||||||
|
_: 1
|
||||||
|
}))
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: transform component slots on component named slot 1`] = `
|
||||||
|
"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
|
||||||
|
|
||||||
|
return function render(_ctx, _cache) {
|
||||||
|
const _component_Comp = _resolveComponent(\\"Comp\\")
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(_component_Comp, null, {
|
||||||
|
named: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)]),
|
||||||
|
_: 1
|
||||||
|
}))
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`compiler: transform component slots on-component default slot 1`] = `
|
exports[`compiler: transform component slots on-component default slot 1`] = `
|
||||||
"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
|
"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
|
||||||
|
|
||||||
@ -183,3 +195,17 @@ return function render(_ctx, _cache) {
|
|||||||
}))
|
}))
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: transform component slots template named slots 1`] = `
|
||||||
|
"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
|
||||||
|
|
||||||
|
return function render(_ctx, _cache) {
|
||||||
|
const _component_Comp = _resolveComponent(\\"Comp\\")
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(_component_Comp, null, {
|
||||||
|
one: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)]),
|
||||||
|
two: _withCtx(({ bar }) => [_toDisplayString(_ctx.foo), _toDisplayString(bar)]),
|
||||||
|
_: 1
|
||||||
|
}))
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
@ -130,7 +130,40 @@ describe('compiler: transform component slots', () => {
|
|||||||
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
|
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('named slots', () => {
|
test('on component named slot', () => {
|
||||||
|
const { root, slots } = parseWithSlots(
|
||||||
|
`<Comp v-slot:named="{ foo }">{{ foo }}{{ bar }}</Comp>`,
|
||||||
|
{ prefixIdentifiers: true }
|
||||||
|
)
|
||||||
|
expect(slots).toMatchObject(
|
||||||
|
createSlotMatcher({
|
||||||
|
named: {
|
||||||
|
type: NodeTypes.JS_FUNCTION_EXPRESSION,
|
||||||
|
params: {
|
||||||
|
type: NodeTypes.COMPOUND_EXPRESSION,
|
||||||
|
children: [`{ `, { content: `foo` }, ` }`]
|
||||||
|
},
|
||||||
|
returns: [
|
||||||
|
{
|
||||||
|
type: NodeTypes.INTERPOLATION,
|
||||||
|
content: {
|
||||||
|
content: `foo`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NodeTypes.INTERPOLATION,
|
||||||
|
content: {
|
||||||
|
content: `_ctx.bar`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('template named slots', () => {
|
||||||
const { root, slots } = parseWithSlots(
|
const { root, slots } = parseWithSlots(
|
||||||
`<Comp>
|
`<Comp>
|
||||||
<template v-slot:one="{ foo }">
|
<template v-slot:one="{ foo }">
|
||||||
@ -191,6 +224,39 @@ describe('compiler: transform component slots', () => {
|
|||||||
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
|
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('on component dynamically named slot', () => {
|
||||||
|
const { root, slots } = parseWithSlots(
|
||||||
|
`<Comp v-slot:[named]="{ foo }">{{ foo }}{{ bar }}</Comp>`,
|
||||||
|
{ prefixIdentifiers: true }
|
||||||
|
)
|
||||||
|
expect(slots).toMatchObject(
|
||||||
|
createSlotMatcher({
|
||||||
|
'[_ctx.named]': {
|
||||||
|
type: NodeTypes.JS_FUNCTION_EXPRESSION,
|
||||||
|
params: {
|
||||||
|
type: NodeTypes.COMPOUND_EXPRESSION,
|
||||||
|
children: [`{ `, { content: `foo` }, ` }`]
|
||||||
|
},
|
||||||
|
returns: [
|
||||||
|
{
|
||||||
|
type: NodeTypes.INTERPOLATION,
|
||||||
|
content: {
|
||||||
|
content: `foo`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NodeTypes.INTERPOLATION,
|
||||||
|
content: {
|
||||||
|
content: `_ctx.bar`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
test('named slots w/ implicit default slot', () => {
|
test('named slots w/ implicit default slot', () => {
|
||||||
const { root, slots } = parseWithSlots(
|
const { root, slots } = parseWithSlots(
|
||||||
`<Comp>
|
`<Comp>
|
||||||
@ -736,28 +802,5 @@ describe('compiler: transform component slots', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('error on named slot on component', () => {
|
|
||||||
const onError = jest.fn()
|
|
||||||
const source = `<Comp v-slot:foo>foo</Comp>`
|
|
||||||
parseWithSlots(source, { onError })
|
|
||||||
const index = source.indexOf('v-slot')
|
|
||||||
expect(onError.mock.calls[0][0]).toMatchObject({
|
|
||||||
code: ErrorCodes.X_V_SLOT_NAMED_SLOT_ON_COMPONENT,
|
|
||||||
loc: {
|
|
||||||
source: `v-slot:foo`,
|
|
||||||
start: {
|
|
||||||
offset: index,
|
|
||||||
line: 1,
|
|
||||||
column: index + 1
|
|
||||||
},
|
|
||||||
end: {
|
|
||||||
offset: index + 10,
|
|
||||||
line: 1,
|
|
||||||
column: index + 11
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -76,7 +76,6 @@ export const enum ErrorCodes {
|
|||||||
X_V_BIND_NO_EXPRESSION,
|
X_V_BIND_NO_EXPRESSION,
|
||||||
X_V_ON_NO_EXPRESSION,
|
X_V_ON_NO_EXPRESSION,
|
||||||
X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET,
|
X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET,
|
||||||
X_V_SLOT_NAMED_SLOT_ON_COMPONENT,
|
|
||||||
X_V_SLOT_MIXED_SLOT_USAGE,
|
X_V_SLOT_MIXED_SLOT_USAGE,
|
||||||
X_V_SLOT_DUPLICATE_SLOT_NAMES,
|
X_V_SLOT_DUPLICATE_SLOT_NAMES,
|
||||||
X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN,
|
X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN,
|
||||||
@ -163,13 +162,10 @@ export const errorMessages: { [code: number]: string } = {
|
|||||||
[ErrorCodes.X_V_BIND_NO_EXPRESSION]: `v-bind is missing expression.`,
|
[ErrorCodes.X_V_BIND_NO_EXPRESSION]: `v-bind is missing expression.`,
|
||||||
[ErrorCodes.X_V_ON_NO_EXPRESSION]: `v-on is missing expression.`,
|
[ErrorCodes.X_V_ON_NO_EXPRESSION]: `v-on is missing expression.`,
|
||||||
[ErrorCodes.X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET]: `Unexpected custom directive on <slot> outlet.`,
|
[ErrorCodes.X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET]: `Unexpected custom directive on <slot> outlet.`,
|
||||||
[ErrorCodes.X_V_SLOT_NAMED_SLOT_ON_COMPONENT]:
|
|
||||||
`Named v-slot on component. ` +
|
|
||||||
`Named slots should use <template v-slot> syntax nested inside the component.`,
|
|
||||||
[ErrorCodes.X_V_SLOT_MIXED_SLOT_USAGE]:
|
[ErrorCodes.X_V_SLOT_MIXED_SLOT_USAGE]:
|
||||||
`Mixed v-slot usage on both the component and nested <template>.` +
|
`Mixed v-slot usage on both the component and nested <template>.` +
|
||||||
`The default slot should also use <template> syntax when there are other ` +
|
`When there are multiple named slots, all slots should use <template> ` +
|
||||||
`named slots to avoid scope ambiguity.`,
|
`syntax to avoid scope ambiguity.`,
|
||||||
[ErrorCodes.X_V_SLOT_DUPLICATE_SLOT_NAMES]: `Duplicate slot names found. `,
|
[ErrorCodes.X_V_SLOT_DUPLICATE_SLOT_NAMES]: `Duplicate slot names found. `,
|
||||||
[ErrorCodes.X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN]:
|
[ErrorCodes.X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN]:
|
||||||
`Extraneous children found when component already has explicitly named ` +
|
`Extraneous children found when component already has explicitly named ` +
|
||||||
|
@ -139,17 +139,17 @@ export function buildSlots(
|
|||||||
hasDynamicSlots = hasScopeRef(node, context.identifiers)
|
hasDynamicSlots = hasScopeRef(node, context.identifiers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Check for default slot with slotProps on component itself.
|
// 1. Check for slot with slotProps on component itself.
|
||||||
// <Comp v-slot="{ prop }"/>
|
// <Comp v-slot="{ prop }"/>
|
||||||
const onComponentDefaultSlot = findDir(node, 'slot', true)
|
const onComponentSlot = findDir(node, 'slot', true)
|
||||||
if (onComponentDefaultSlot) {
|
if (onComponentSlot) {
|
||||||
const { arg, exp, loc } = onComponentDefaultSlot
|
const { arg, exp } = onComponentSlot
|
||||||
if (arg) {
|
slotsProperties.push(
|
||||||
context.onError(
|
createObjectProperty(
|
||||||
createCompilerError(ErrorCodes.X_V_SLOT_NAMED_SLOT_ON_COMPONENT, loc)
|
arg || createSimpleExpression('default', true),
|
||||||
|
buildSlotFn(exp, children, loc)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
|
||||||
slotsProperties.push(buildDefaultSlotProperty(exp, children))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Iterate through children and check for template slots
|
// 2. Iterate through children and check for template slots
|
||||||
@ -174,8 +174,8 @@ export function buildSlots(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onComponentDefaultSlot) {
|
if (onComponentSlot) {
|
||||||
// already has on-component default slot - this is incorrect usage.
|
// already has on-component slot - this is incorrect usage.
|
||||||
context.onError(
|
context.onError(
|
||||||
createCompilerError(ErrorCodes.X_V_SLOT_MIXED_SLOT_USAGE, slotDir.loc)
|
createCompilerError(ErrorCodes.X_V_SLOT_MIXED_SLOT_USAGE, slotDir.loc)
|
||||||
)
|
)
|
||||||
@ -294,7 +294,7 @@ export function buildSlots(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!onComponentDefaultSlot) {
|
if (!onComponentSlot) {
|
||||||
if (!hasTemplateSlots) {
|
if (!hasTemplateSlots) {
|
||||||
// implicit default slot (on component)
|
// implicit default slot (on component)
|
||||||
slotsProperties.push(buildDefaultSlotProperty(undefined, children))
|
slotsProperties.push(buildDefaultSlotProperty(undefined, children))
|
||||||
|
Loading…
Reference in New Issue
Block a user