diff --git a/docs/rules/require-explicit-slots.md b/docs/rules/require-explicit-slots.md index bffd1e3e8..7b05724db 100644 --- a/docs/rules/require-explicit-slots.md +++ b/docs/rules/require-explicit-slots.md @@ -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. diff --git a/lib/rules/require-explicit-slots.js b/lib/rules/require-explicit-slots.js index ed2d2edb9..aab09c494 100644 --- a/lib/rules/require-explicit-slots.js +++ b/lib/rules/require-explicit-slots.js @@ -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', @@ -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 {} } @@ -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, @@ -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 @@ -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, @@ -111,7 +141,7 @@ module.exports = { const slotNameAttr = utils.getAttribute(node, 'name') - if (slotNameAttr) { + if (slotNameAttr?.value) { slotName = slotNameAttr.value.value } diff --git a/tests/lib/rules/require-explicit-slots.js b/tests/lib/rules/require-explicit-slots.js index 91ce0ce0b..ebbc28818 100644 --- a/tests/lib/rules/require-explicit-slots.js +++ b/tests/lib/rules/require-explicit-slots.js @@ -62,7 +62,34 @@ tester.run('require-explicit-slots', rule, { }>() ` }, - + { + filename: 'test.vue', + code: ` + + ` + }, + { + filename: 'test.vue', + code: ` + + ` + }, { filename: 'test.vue', code: ` @@ -203,6 +230,44 @@ tester.run('require-explicit-slots', rule, { } ] }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + message: 'Slots must be explicitly defined.' + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + message: 'Slots must be explicitly defined.' + } + ] + }, { filename: 'test.vue', code: `