Skip to content

Commit e13089e

Browse files
authored
fix(require-explicit-slots): ignore attribute binding (#2591)
1 parent 54a99c5 commit e13089e

File tree

2 files changed

+96
-10
lines changed

2 files changed

+96
-10
lines changed

Diff for: lib/rules/require-explicit-slots.js

+45-10
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,21 @@ function getSlotsName(node) {
3535
return null
3636
}
3737

38+
/**
39+
* @param {VElement} node
40+
* @return {VAttribute | VDirective | undefined}
41+
*/
42+
function getSlotNameNode(node) {
43+
return node.startTag.attributes.find(
44+
(node) =>
45+
(!node.directive && node.key.name === 'name') ||
46+
(node.directive &&
47+
node.key.name.name === 'bind' &&
48+
node.key.argument?.type === 'VIdentifier' &&
49+
node.key.argument?.name === 'name')
50+
)
51+
}
52+
3853
module.exports = {
3954
meta: {
4055
type: 'problem',
@@ -68,6 +83,19 @@ module.exports = {
6883
}
6984
const slotsDefined = new Set()
7085

86+
/**
87+
* @param {VElement} node
88+
* @param {string | undefined} slotName
89+
*/
90+
function reportMissingSlot(node, slotName) {
91+
if (!slotsDefined.has(slotName)) {
92+
context.report({
93+
node,
94+
messageId: 'requireExplicitSlots'
95+
})
96+
}
97+
}
98+
7199
return utils.compositingVisitors(
72100
utils.defineScriptSetupVisitor(context, {
73101
onDefineSlotsEnter(node) {
@@ -137,20 +165,27 @@ module.exports = {
137165
}
138166
}),
139167
utils.defineTemplateBodyVisitor(context, {
168+
/** @param {VElement} node */
140169
"VElement[name='slot']"(node) {
141-
let slotName = 'default'
142-
143-
const slotNameAttr = utils.getAttribute(node, 'name')
170+
const nameNode = getSlotNameNode(node)
144171

145-
if (slotNameAttr?.value) {
146-
slotName = slotNameAttr.value.value
172+
// if no slot name is declared, default to 'default'
173+
if (!nameNode) {
174+
reportMissingSlot(node, 'default')
175+
return
147176
}
148177

149-
if (!slotsDefined.has(slotName)) {
150-
context.report({
151-
node,
152-
messageId: 'requireExplicitSlots'
153-
})
178+
if (nameNode.directive) {
179+
const expression = nameNode.value?.expression
180+
// ignore attribute binding except string literal
181+
if (!expression || !utils.isStringLiteral(expression)) {
182+
return
183+
}
184+
185+
const name = utils.getStringLiteralValue(expression) || undefined
186+
reportMissingSlot(node, name)
187+
} else {
188+
reportMissingSlot(node, nameNode.value?.value)
154189
}
155190
}
156191
})

Diff for: tests/lib/rules/require-explicit-slots.js

+51
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,37 @@ tester.run('require-explicit-slots', rule, {
160160
parser: null
161161
}
162162
}
163+
},
164+
// attribute binding
165+
{
166+
filename: 'test.vue',
167+
code: `
168+
<template>
169+
<div>
170+
<slot :name="'foo'"></slot>
171+
<slot :name="\`bar\`"></slot>
172+
</div>
173+
</template>
174+
<script setup lang="ts">
175+
defineSlots<{
176+
foo(props: { msg: string }): any
177+
bar(props: { msg: string }): any
178+
}>()
179+
</script>`
180+
},
181+
{
182+
filename: 'test.vue',
183+
code: `
184+
<template>
185+
<div>
186+
<slot :name="bar"></slot>
187+
</div>
188+
</template>
189+
<script setup lang="ts">
190+
defineSlots<{
191+
default(props: { msg: string }): any
192+
}>()
193+
</script>`
163194
}
164195
],
165196
invalid: [
@@ -291,6 +322,26 @@ tester.run('require-explicit-slots', rule, {
291322
}
292323
]
293324
},
325+
{
326+
// ignore attribute binding except string literal
327+
filename: 'test.vue',
328+
code: `
329+
<template>
330+
<div>
331+
<slot :name="'foo'"></slot>
332+
</div>
333+
</template>
334+
<script setup lang="ts">
335+
defineSlots<{
336+
default(props: { msg: string }): any
337+
}>()
338+
</script>`,
339+
errors: [
340+
{
341+
message: 'Slots must be explicitly defined.'
342+
}
343+
]
344+
},
294345
{
295346
filename: 'test.vue',
296347
code: `

0 commit comments

Comments
 (0)