Skip to content

Commit

Permalink
fix(require-explicit-slots): recognize slot names enclosed in quotes (#…
Browse files Browse the repository at this point in the history
…2397)

Co-authored-by: Flo Edelmann <git@flo-edelmann.de>
  • Loading branch information
waynzh and FloEdelmann committed Feb 19, 2024
1 parent 16aba2c commit c2451ab
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 8 deletions.
2 changes: 1 addition & 1 deletion docs/rules/require-explicit-slots.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ since: v9.21.0
## :book: Rule Details

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.
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.

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

Expand Down
42 changes: 36 additions & 6 deletions lib/rules/require-explicit-slots.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,33 @@ const utils = require('../utils')

/**
* @typedef {import('@typescript-eslint/types').TSESTree.TypeNode} TypeNode
* @typedef {import('@typescript-eslint/types').TSESTree.TypeElement} TypeElement
*/

/**
* @param {TypeElement} node
* @return {string | null}
*/
function getSlotsName(node) {
if (
node.type !== 'TSMethodSignature' &&
node.type !== 'TSPropertySignature'
) {
return null
}

const key = node.key
if (key.type === 'Literal') {
return typeof key.value === 'string' ? key.value : null
}

if (key.type === 'Identifier') {
return key.name
}

return null
}

module.exports = {
meta: {
type: 'problem',
Expand All @@ -34,9 +59,9 @@ module.exports = {
if (!documentFragment) {
return {}
}
const scripts = documentFragment.children.filter(
(element) => utils.isVElement(element) && element.name === 'script'
)
const scripts = documentFragment.children
.filter(utils.isVElement)
.filter((element) => element.name === 'script')
if (scripts.every((script) => !utils.hasAttribute(script, 'lang', 'ts'))) {
return {}
}
Expand All @@ -54,7 +79,9 @@ module.exports = {

if (param.type === 'TSTypeLiteral') {
for (const memberNode of param.members) {
const slotName = memberNode.key.name
const slotName = getSlotsName(memberNode)
if (!slotName) continue

if (slotsDefined.has(slotName)) {
context.report({
node: memberNode,
Expand All @@ -75,6 +102,7 @@ module.exports = {
if (!slotsProperty) return

const slotsTypeHelper =
slotsProperty.value.type === 'TSAsExpression' &&
slotsProperty.value.typeAnnotation?.typeName.name === 'SlotsType' &&
slotsProperty.value.typeAnnotation
if (!slotsTypeHelper) return
Expand All @@ -90,7 +118,9 @@ module.exports = {

if (param.type === 'TSTypeLiteral') {
for (const memberNode of param.members) {
const slotName = memberNode.key.name
const slotName = getSlotsName(memberNode)
if (!slotName) continue

if (slotsDefined.has(slotName)) {
context.report({
node: memberNode,
Expand All @@ -111,7 +141,7 @@ module.exports = {

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

if (slotNameAttr) {
if (slotNameAttr?.value) {
slotName = slotNameAttr.value.value
}

Expand Down
67 changes: 66 additions & 1 deletion tests/lib/rules/require-explicit-slots.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,34 @@ tester.run('require-explicit-slots', rule, {
}>()
</script>`
},

{
filename: 'test.vue',
code: `
<template>
<div>
<slot name="foo"></slot>
</div>
</template>
<script setup lang="ts">
defineSlots<{
foo: (props: { msg: string }) => any
}>()
</script>`
},
{
filename: 'test.vue',
code: `
<template>
<div>
<slot name="foo-bar"></slot>
</div>
</template>
<script setup lang="ts">
defineSlots<{
'foo-bar'(props: { msg: string }): any
}>()
</script>`
},
{
filename: 'test.vue',
code: `
Expand Down Expand Up @@ -203,6 +230,44 @@ tester.run('require-explicit-slots', rule, {
}
]
},
{
filename: 'test.vue',
code: `
<template>
<div>
<slot name="foo" />
</div>
</template>
<script setup lang="ts">
defineSlots<{
default: (props: { msg: string }) => any
}>()
</script>`,
errors: [
{
message: 'Slots must be explicitly defined.'
}
]
},
{
filename: 'test.vue',
code: `
<template>
<div>
<slot name />
</div>
</template>
<script setup lang="ts">
defineSlots<{
'foo-bar'(props: { msg: string }): any
}>()
</script>`,
errors: [
{
message: 'Slots must be explicitly defined.'
}
]
},
{
filename: 'test.vue',
code: `
Expand Down

0 comments on commit c2451ab

Please sign in to comment.