8000 fix(require-explicit-slots): recognize slot names enclosed in quotes … · vuejs/eslint-plugin-vue@c2451ab · GitHub
[go: up one dir, main page]

Skip to content

Commit c2451ab

Browse files
waynzhFloEdelmann
andauthored
fix(require-explicit-slots): recognize slot names enclosed in quotes (#2397)
Co-authored-by: Flo Edelmann <git@flo-edelmann.de>
1 parent 16aba2c commit c2451ab

File tree

3 files changed

+103
-8
lines changed

3 files changed

+103
-8
lines changed

docs/rules/require-explicit-slots.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ since: v9.21.0
1111
1212
## :book: Rule Details
1313

14-
This rule enforces all slots used in the template to be defined once either in the `script setup` block with the [`defineSlots`](https://vuejs.org/api/sfc-script-setup.html) macro, or with the [`slots property`](https://vuejs.org/api/options-rendering.html#slots) in the Options API.
14+
This rule enforces all slots used in the template to be defined once either in the `script setup` block with the [`defineSlots`](https://vuejs.org/api/sfc-script-setup.html#defineslots) macro, or with the [`slots property`](https://vuejs.org/api/options-rendering.html#slots) in the Options API.
1515

1616
<eslint-code-block :rules="{'vue/require-explicit-slots': ['error']}">
1717

lib/rules/require-explicit-slots.js

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,33 @@ const utils = require('../utils')
88

99
/**
1010
* @typedef {import('@typescript-eslint/types').TSESTree.TypeNode} TypeNode
11+
* @typedef {import('@typescript-eslint/types').TSESTree.TypeElement} TypeElement
1112
*/
1213

14+
/**
15+
* @param {TypeElement} node
16+
* @return {string | null}
17+
*/
18+
function getSlotsName(node) {
19+
if (
20+
node.type !== 'TSMethodSignature' &&
21+
node.type !== 'TSPropertySignature'
22+
) {
23+
return null
24+
}
25+
26+
const key = node.key
27+
if (key.type === 'Literal') {
28+
return typeof key.value === 'string' ? key.value : null
29+
}
30+
31+
if (key.type === 'Identifier') {
32+
return key.name
33+
}
34+
35+
return null
36+
}
37+
1338
module.exports = {
1439
meta: {
1540
type: 'problem',
@@ -34,9 +59,9 @@ module.exports = {
3459
if (!documentFragment) {
3560
return {}
3661
}
37-
const scripts = documentFragment.children.filter(
38-
(element) => utils.isVElement(element) && element.name === 'script'
39-
)
62+
const scripts = documentFragment.children
63+
.filter(utils.isVElement)
64+
.filter((element) => element.name === 'script')
4065
if (scripts.every((script) => !utils.hasAttribute(script, 'lang', 'ts'))) {
4166
return {}
4267
}
@@ -54,7 +79,9 @@ module.exports = {
5479

5580
if (param.type === 'TSTypeLiteral') {
5681
for (const memberNode of param.members) {
57-
const slotName = memberNode.key.name
82+
const slotName = getSlotsName(memberNode)
83+
if (!slotName) continue
84+
5885
if (slotsDefined.has(slotName)) {
5986
context.report({
6087
node: memberNode,
@@ -75,6 +102,7 @@ module.exports = {
75102
if (!slotsProperty) return
76103

77104
const slotsTypeHelper =
105+
slotsProperty.value.type === 'TSAsExpression' &&
78106
slotsProperty.value.typeAnnotation?.typeName.name === 'SlotsType' &&
79107
slotsProperty.value.typeAnnotation
80108
if (!slotsTypeHelper) return
@@ -90,7 +118,9 @@ module.exports = {
90118

91119
if (param.type === 'TSTypeLiteral') {
92120
for (const memberNode of param.members) {
93-
const slotName = memberNode.key.name
121+
const slotName = getSlotsName(memberNode)
122+
if (!slotName) continue
123+
94124
if (slotsDefined.has(slotName)) {
95125
context.report({
96126
node: memberNode,
@@ -111,7 +141,7 @@ module.exports = {
111141

112142
const slotNameAttr = utils.getAttribute(node, 'name')
113143

114-
if (slotNameAttr) {
144+
if (slotNameAttr?.value) {
115145
slotName = slotNameAttr.value.value
116146
}
117147

tests/lib/rules/require-explicit-slots.js

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,34 @@ tester.run('require-explicit-slots', rule, {
6262
}>()
6363
</script>`
6464
},
65-
65+
{
66+
filename: 'test.vue',
67+
code: `
68+
<template>
69+
<div>
70+
<slot name="foo"></slot>
71+
</div>
72+
</template>
73+
<script setup lang="ts">
74+
defineSlots<{
75+
foo: (props: { msg: string }) => any
76+
}>()
77+
</script>`
78+
},
79+
{
80+
filename: 'test.vue',
81+
code: `
82+
<template>
83+
<div>
84+
<slot name="foo-bar"></slot>
85+
</div>
86+
</template>
87+
<script setup lang="ts">
88+
defineSlots<{
89+
'foo-bar'(props: { msg: string }): any
90+
}>()
91+
</script>`
92+
},
6693
{
6794
filename: 'test.vue',
6895
code: `
@@ -203,6 +230,44 @@ tester.run('require-explicit-slots', rule, {
203230
}
204231
]
205232
},
233+
{
234+
filename: 'test.vue',
235+
code: `
236+
<template>
237+
<div>
238+
<slot name="foo" />
239+
</div>
240+
</template>
241+
<script setup lang="ts">
242+
defineSlots<{
243+
default: (props: { msg: string }) => any
244+
}>()
245+
</script>`,
246+
errors: [
247+
{
248+
message: 'Slots must be explicitly defined.'
249+
}
250+
]
251+
},
252+
{
253+
filename: 'test.vue',
254+
code: `
255+
<template>
256+
<div>
257+
<slot name />
258+
</div>
259+
</template>
260+
<script setup lang="ts">
261+
defineSlots<{
262+
'foo-bar'(props: { msg: string }): any
263+
}>()
264+
</script>`,
265+
errors: [
266+
{
267+
message: 'Slots must be explicitly defined.'
268+
}
269+
]
270+
},
206271
{
207272
filename: 'test.vue',
208273
code: `

0 commit comments

Comments
 (0)
0