diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
index e6d0e11d..2eb27e83 100644
--- a/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
+++ b/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
@@ -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`] = `
"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`] = `
"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
+ }))
+}"
+`;
diff --git a/packages/compiler-core/__tests__/transforms/vSlot.spec.ts b/packages/compiler-core/__tests__/transforms/vSlot.spec.ts
index c6c9c832..c1cb651d 100644
--- a/packages/compiler-core/__tests__/transforms/vSlot.spec.ts
+++ b/packages/compiler-core/__tests__/transforms/vSlot.spec.ts
@@ -130,7 +130,40 @@ describe('compiler: transform component slots', () => {
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
})
- test('named slots', () => {
+ test('on component named slot', () => {
+ const { root, slots } = parseWithSlots(
+ `{{ foo }}{{ bar }}`,
+ { 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(
`
@@ -191,6 +224,39 @@ describe('compiler: transform component slots', () => {
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
})
+ test('on component dynamically named slot', () => {
+ const { root, slots } = parseWithSlots(
+ `{{ foo }}{{ bar }}`,
+ { 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', () => {
const { root, slots } = parseWithSlots(
`
@@ -736,28 +802,5 @@ describe('compiler: transform component slots', () => {
}
})
})
-
- test('error on named slot on component', () => {
- const onError = jest.fn()
- const source = `foo`
- 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
- }
- }
- })
- })
})
})
diff --git a/packages/compiler-core/src/errors.ts b/packages/compiler-core/src/errors.ts
index 408847a5..5d66ddcf 100644
--- a/packages/compiler-core/src/errors.ts
+++ b/packages/compiler-core/src/errors.ts
@@ -76,7 +76,6 @@ export const enum ErrorCodes {
X_V_BIND_NO_EXPRESSION,
X_V_ON_NO_EXPRESSION,
X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET,
- X_V_SLOT_NAMED_SLOT_ON_COMPONENT,
X_V_SLOT_MIXED_SLOT_USAGE,
X_V_SLOT_DUPLICATE_SLOT_NAMES,
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_ON_NO_EXPRESSION]: `v-on is missing expression.`,
[ErrorCodes.X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET]: `Unexpected custom directive on outlet.`,
- [ErrorCodes.X_V_SLOT_NAMED_SLOT_ON_COMPONENT]:
- `Named v-slot on component. ` +
- `Named slots should use syntax nested inside the component.`,
[ErrorCodes.X_V_SLOT_MIXED_SLOT_USAGE]:
`Mixed v-slot usage on both the component and nested .` +
- `The default slot should also use syntax when there are other ` +
- `named slots to avoid scope ambiguity.`,
+ `When there are multiple named slots, all slots should use ` +
+ `syntax to avoid scope ambiguity.`,
[ErrorCodes.X_V_SLOT_DUPLICATE_SLOT_NAMES]: `Duplicate slot names found. `,
[ErrorCodes.X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN]:
`Extraneous children found when component already has explicitly named ` +
diff --git a/packages/compiler-core/src/transforms/vSlot.ts b/packages/compiler-core/src/transforms/vSlot.ts
index d02cda50..548e90fd 100644
--- a/packages/compiler-core/src/transforms/vSlot.ts
+++ b/packages/compiler-core/src/transforms/vSlot.ts
@@ -139,17 +139,17 @@ export function buildSlots(
hasDynamicSlots = hasScopeRef(node, context.identifiers)
}
- // 1. Check for default slot with slotProps on component itself.
+ // 1. Check for slot with slotProps on component itself.
//
- const onComponentDefaultSlot = findDir(node, 'slot', true)
- if (onComponentDefaultSlot) {
- const { arg, exp, loc } = onComponentDefaultSlot
- if (arg) {
- context.onError(
- createCompilerError(ErrorCodes.X_V_SLOT_NAMED_SLOT_ON_COMPONENT, loc)
+ const onComponentSlot = findDir(node, 'slot', true)
+ if (onComponentSlot) {
+ const { arg, exp } = onComponentSlot
+ slotsProperties.push(
+ createObjectProperty(
+ arg || createSimpleExpression('default', true),
+ buildSlotFn(exp, children, loc)
)
- }
- slotsProperties.push(buildDefaultSlotProperty(exp, children))
+ )
}
// 2. Iterate through children and check for template slots
@@ -174,8 +174,8 @@ export function buildSlots(
continue
}
- if (onComponentDefaultSlot) {
- // already has on-component default slot - this is incorrect usage.
+ if (onComponentSlot) {
+ // already has on-component slot - this is incorrect usage.
context.onError(
createCompilerError(ErrorCodes.X_V_SLOT_MIXED_SLOT_USAGE, slotDir.loc)
)
@@ -294,7 +294,7 @@ export function buildSlots(
}
}
- if (!onComponentDefaultSlot) {
+ if (!onComponentSlot) {
if (!hasTemplateSlots) {
// implicit default slot (on component)
slotsProperties.push(buildDefaultSlotProperty(undefined, children))