From e13089e7feecdacf5f479ffc25491f685261d177 Mon Sep 17 00:00:00 2001 From: Wayne Zhang Date: Mon, 11 Nov 2024 12:03:09 +0800 Subject: [PATCH] fix(require-explicit-slots): ignore attribute binding (#2591) --- lib/rules/require-explicit-slots.js | 55 ++++++++++++++++++----- tests/lib/rules/require-explicit-slots.js | 51 +++++++++++++++++++++ 2 files changed, 96 insertions(+), 10 deletions(-) diff --git a/lib/rules/require-explicit-slots.js b/lib/rules/require-explicit-slots.js index 0ae77b597..f87503bb7 100644 --- a/lib/rules/require-explicit-slots.js +++ b/lib/rules/require-explicit-slots.js @@ -35,6 +35,21 @@ function getSlotsName(node) { return null } +/** + * @param {VElement} node + * @return {VAttribute | VDirective | undefined} + */ +function getSlotNameNode(node) { + return node.startTag.attributes.find( + (node) => + (!node.directive && node.key.name === 'name') || + (node.directive && + node.key.name.name === 'bind' && + node.key.argument?.type === 'VIdentifier' && + node.key.argument?.name === 'name') + ) +} + module.exports = { meta: { type: 'problem', @@ -68,6 +83,19 @@ module.exports = { } const slotsDefined = new Set() + /** + * @param {VElement} node + * @param {string | undefined} slotName + */ + function reportMissingSlot(node, slotName) { + if (!slotsDefined.has(slotName)) { + context.report({ + node, + messageId: 'requireExplicitSlots' + }) + } + } + return utils.compositingVisitors( utils.defineScriptSetupVisitor(context, { onDefineSlotsEnter(node) { @@ -137,20 +165,27 @@ module.exports = { } }), utils.defineTemplateBodyVisitor(context, { + /** @param {VElement} node */ "VElement[name='slot']"(node) { - let slotName = 'default' - - const slotNameAttr = utils.getAttribute(node, 'name') + const nameNode = getSlotNameNode(node) - if (slotNameAttr?.value) { - slotName = slotNameAttr.value.value + // if no slot name is declared, default to 'default' + if (!nameNode) { + reportMissingSlot(node, 'default') + return } - if (!slotsDefined.has(slotName)) { - context.report({ - node, - messageId: 'requireExplicitSlots' - }) + if (nameNode.directive) { + const expression = nameNode.value?.expression + // ignore attribute binding except string literal + if (!expression || !utils.isStringLiteral(expression)) { + return + } + + const name = utils.getStringLiteralValue(expression) || undefined + reportMissingSlot(node, name) + } else { + reportMissingSlot(node, nameNode.value?.value) } } }) diff --git a/tests/lib/rules/require-explicit-slots.js b/tests/lib/rules/require-explicit-slots.js index ebbc28818..92d1a1334 100644 --- a/tests/lib/rules/require-explicit-slots.js +++ b/tests/lib/rules/require-explicit-slots.js @@ -160,6 +160,37 @@ tester.run('require-explicit-slots', rule, { parser: null } } + }, + // attribute binding + { + filename: 'test.vue', + code: ` + + ` + }, + { + filename: 'test.vue', + code: ` + + ` } ], invalid: [ @@ -291,6 +322,26 @@ tester.run('require-explicit-slots', rule, { } ] }, + { + // ignore attribute binding except string literal + filename: 'test.vue', + code: ` + + `, + errors: [ + { + message: 'Slots must be explicitly defined.' + } + ] + }, { filename: 'test.vue', code: `