}"
`;
-exports[`compiler: transform component slots explicit default slot 1`] = `
-"const { toString, resolveComponent, createVNode, createBlock, openBlock } = Vue
-
-return function render() {
- const _ctx = this
- const _component_Comp = resolveComponent(\\"Comp\\")
-
- return (openBlock(), createBlock(_component_Comp, null, {
- default: ({ foo }) => [toString(foo), toString(_ctx.bar)],
- _compiled: true
- }))
-}"
-`;
-
exports[`compiler: transform component slots implicit default slot 1`] = `
"const { createVNode, resolveComponent, createBlock, openBlock } = Vue
}"
`;
+exports[`compiler: transform component slots named slots w/ implicit default slot 1`] = `
+"const _Vue = Vue
+
+return function render() {
+ with (this) {
+ const { createVNode: _createVNode, resolveComponent: _resolveComponent, createBlock: _createBlock, openBlock: _openBlock } = _Vue
+
+ const _component_Comp = _resolveComponent(\\"Comp\\")
+
+ return (_openBlock(), _createBlock(_component_Comp, null, {
+ one: () => [\\"foo\\"],
+ default: () => [
+ \\"bar\\",
+ _createVNode(\\"span\\")
+ ],
+ _compiled: true
+ }))
+ }
+}"
+`;
+
exports[`compiler: transform component slots nested slots scoping 1`] = `
"const { toString, resolveComponent, createVNode, createBlock, openBlock } = Vue
}))
}"
`;
+
+exports[`compiler: transform component slots on-component default slot 1`] = `
+"const { toString, resolveComponent, createVNode, createBlock, openBlock } = Vue
+
+return function render() {
+ const _ctx = this
+ const _component_Comp = resolveComponent(\\"Comp\\")
+
+ return (openBlock(), createBlock(_component_Comp, null, {
+ default: ({ foo }) => [toString(foo), toString(_ctx.bar)],
+ _compiled: true
+ }))
+}"
+`;
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
})
- test('explicit default slot', () => {
+ test('on-component default slot', () => {
const { root, slots } = parseWithSlots(
`<Comp v-slot="{ foo }">{{ foo }}{{ bar }}</Comp>`,
{ prefixIdentifiers: true }
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
})
+ test('named slots w/ implicit default slot', () => {
+ const { root, slots } = parseWithSlots(
+ `<Comp>
+ <template #one>foo</template>bar<span/>
+ </Comp>`
+ )
+ expect(slots).toMatchObject(
+ createSlotMatcher({
+ one: {
+ type: NodeTypes.JS_FUNCTION_EXPRESSION,
+ params: undefined,
+ returns: [
+ {
+ type: NodeTypes.TEXT,
+ content: `foo`
+ }
+ ]
+ },
+ default: {
+ type: NodeTypes.JS_FUNCTION_EXPRESSION,
+ params: undefined,
+ returns: [
+ {
+ type: NodeTypes.TEXT,
+ content: `bar`
+ },
+ {
+ type: NodeTypes.ELEMENT,
+ tag: `span`
+ }
+ ]
+ }
+ })
+ )
+ expect(generate(root).code).toMatchSnapshot()
+ })
+
test('dynamically named slots', () => {
const { root, slots } = parseWithSlots(
`<Comp>
})
describe('errors', () => {
- test('error on extraneous children w/ named slots', () => {
+ test('error on extraneous children w/ named default slot', () => {
const onError = jest.fn()
const source = `<Comp><template #default>foo</template>bar</Comp>`
parseWithSlots(source, { onError })
const index = source.indexOf('bar')
expect(onError.mock.calls[0][0]).toMatchObject({
- code: ErrorCodes.X_V_SLOT_EXTRANEOUS_NON_SLOT_CHILDREN,
+ code: ErrorCodes.X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN,
loc: {
source: `bar`,
start: {
X_V_SLOT_NAMED_SLOT_ON_COMPONENT,
X_V_SLOT_MIXED_SLOT_USAGE,
X_V_SLOT_DUPLICATE_SLOT_NAMES,
- X_V_SLOT_EXTRANEOUS_NON_SLOT_CHILDREN,
+ X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN,
X_V_SLOT_MISPLACED,
X_V_MODEL_NO_EXPRESSION,
X_V_MODEL_MALFORMED_EXPRESSION,
`The default slot should also use <template> syntax when there are other ` +
`named slots to avoid scope ambiguity.`,
[ErrorCodes.X_V_SLOT_DUPLICATE_SLOT_NAMES]: `Duplicate slot names found. `,
- [ErrorCodes.X_V_SLOT_EXTRANEOUS_NON_SLOT_CHILDREN]:
- `Extraneous children found when component has explicit slots. ` +
- `These children will be ignored.`,
+ [ErrorCodes.X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN]:
+ `Extraneous children found when component already has explicitly named ` +
+ `default slot. These children will be ignored.`,
[ErrorCodes.X_V_SLOT_MISPLACED]: `v-slot can only be used on components or <template> tags.`,
[ErrorCodes.X_V_MODEL_NO_EXPRESSION]: `v-model is missing expression.`,
[ErrorCodes.X_V_MODEL_MALFORMED_EXPRESSION]: `v-model value must be a valid JavaScript member expression.`,
// 1. Check for default slot with slotProps on component itself.
// <Comp v-slot="{ prop }"/>
- const explicitDefaultSlot = findDir(node, 'slot', true)
- if (explicitDefaultSlot) {
- const { arg, exp, loc } = explicitDefaultSlot
+ 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)
// 2. Iterate through children and check for template slots
// <template v-slot:foo="{ prop }">
let hasTemplateSlots = false
- let extraneousChild: TemplateChildNode | undefined = undefined
+ let hasNamedDefaultSlot = false
+ const implicitDefaultChildren: TemplateChildNode[] = []
const seenSlotNames = new Set<string>()
+
for (let i = 0; i < children.length; i++) {
const slotElement = children[i]
let slotDir
!(slotDir = findDir(slotElement, 'slot', true))
) {
// not a <template v-slot>, skip.
- if (slotElement.type !== NodeTypes.COMMENT && !extraneousChild) {
- extraneousChild = slotElement
+ if (slotElement.type !== NodeTypes.COMMENT) {
+ implicitDefaultChildren.push(slotElement)
}
continue
}
- if (explicitDefaultSlot) {
+ if (onComponentDefaultSlot) {
// already has on-component default slot - this is incorrect usage.
context.onError(
createCompilerError(ErrorCodes.X_V_SLOT_MIXED_SLOT_USAGE, slotDir.loc)
continue
}
seenSlotNames.add(staticSlotName)
+ if (staticSlotName === 'default') {
+ hasNamedDefaultSlot = true
+ }
}
slotsProperties.push(createObjectProperty(slotName, slotFunction))
}
}
- if (hasTemplateSlots && extraneousChild) {
- context.onError(
- createCompilerError(
- ErrorCodes.X_V_SLOT_EXTRANEOUS_NON_SLOT_CHILDREN,
- extraneousChild.loc
- )
- )
- }
-
- if (!explicitDefaultSlot && !hasTemplateSlots) {
- // implicit default slot.
- slotsProperties.push(buildDefaultSlot(undefined, children, loc))
+ if (!onComponentDefaultSlot) {
+ if (!hasTemplateSlots) {
+ // implicit default slot (on component)
+ slotsProperties.push(buildDefaultSlot(undefined, children, loc))
+ } else if (implicitDefaultChildren.length) {
+ // implicit default slot (mixed with named slots)
+ if (hasNamedDefaultSlot) {
+ context.onError(
+ createCompilerError(
+ ErrorCodes.X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN,
+ implicitDefaultChildren[0].loc
+ )
+ )
+ } else {
+ slotsProperties.push(
+ buildDefaultSlot(undefined, implicitDefaultChildren, loc)
+ )
+ }
+ }
}
let slots: ObjectExpression | CallExpression = createObjectExpression(