From 548f0883c3908a62113caa09e09eae371c052d73 Mon Sep 17 00:00:00 2001 From: waynzh Date: Sat, 2 Nov 2024 16:38:43 +0800 Subject: [PATCH 1/4] fix(require-explicit-slots): ignore attribute binding --- lib/rules/require-explicit-slots.js | 24 ++++++++++++++++++----- tests/lib/rules/require-explicit-slots.js | 13 ++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/lib/rules/require-explicit-slots.js b/lib/rules/require-explicit-slots.js index 0ae77b597..fcc6122b6 100644 --- a/lib/rules/require-explicit-slots.js +++ b/lib/rules/require-explicit-slots.js @@ -137,13 +137,27 @@ module.exports = { } }), utils.defineTemplateBodyVisitor(context, { + /** @param {VElement} node */ "VElement[name='slot']"(node) { - let slotName = 'default' - - const slotNameAttr = utils.getAttribute(node, 'name') + const nameNode = 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') + ) - if (slotNameAttr?.value) { - slotName = slotNameAttr.value.value + /** @type {string | undefined} */ + let slotName = '' + if (!nameNode) { + // If no slot name is declared, default to 'default' + slotName = 'default' + } else if (nameNode.directive) { + // ignore attribute binding + return + } else { + slotName = nameNode.value?.value } if (!slotsDefined.has(slotName)) { diff --git a/tests/lib/rules/require-explicit-slots.js b/tests/lib/rules/require-explicit-slots.js index ebbc28818..ff3fe5c12 100644 --- a/tests/lib/rules/require-explicit-slots.js +++ b/tests/lib/rules/require-explicit-slots.js @@ -160,6 +160,19 @@ tester.run('require-explicit-slots', rule, { parser: null } } + }, + // ignore attribute binding + { + filename: 'test.vue', + code: ` + + ` } ], invalid: [ From cd0dc00eed40e1597c4b826bedd810309b0e4a5d Mon Sep 17 00:00:00 2001 From: waynzh Date: Sat, 2 Nov 2024 17:01:32 +0800 Subject: [PATCH 2/4] feat: update test --- lib/rules/require-explicit-slots.js | 2 +- tests/lib/rules/require-explicit-slots.js | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/rules/require-explicit-slots.js b/lib/rules/require-explicit-slots.js index fcc6122b6..f3977ff4f 100644 --- a/lib/rules/require-explicit-slots.js +++ b/lib/rules/require-explicit-slots.js @@ -149,7 +149,7 @@ module.exports = { ) /** @type {string | undefined} */ - let slotName = '' + let slotName if (!nameNode) { // If no slot name is declared, default to 'default' slotName = 'default' diff --git a/tests/lib/rules/require-explicit-slots.js b/tests/lib/rules/require-explicit-slots.js index ff3fe5c12..61536b483 100644 --- a/tests/lib/rules/require-explicit-slots.js +++ b/tests/lib/rules/require-explicit-slots.js @@ -171,7 +171,9 @@ tester.run('require-explicit-slots', rule, { ` } ], From 62f749bb2ab610c5cc67a194daf1768f770b0934 Mon Sep 17 00:00:00 2001 From: waynzh Date: Wed, 6 Nov 2024 10:49:57 +0800 Subject: [PATCH 3/4] feat: ignore attribute binding except string literal --- lib/rules/require-explicit-slots.js | 64 +++++++++++++++-------- tests/lib/rules/require-explicit-slots.js | 38 +++++++++++++- 2 files changed, 80 insertions(+), 22 deletions(-) diff --git a/lib/rules/require-explicit-slots.js b/lib/rules/require-explicit-slots.js index f3977ff4f..2f2fcefcf 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) { @@ -139,32 +167,26 @@ module.exports = { utils.defineTemplateBodyVisitor(context, { /** @param {VElement} node */ "VElement[name='slot']"(node) { - const nameNode = 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') - ) + const nameNode = getSlotNameNode(node) - /** @type {string | undefined} */ - let slotName + // if no slot name is declared, default to 'default' if (!nameNode) { - // If no slot name is declared, default to 'default' - slotName = 'default' - } else if (nameNode.directive) { - // ignore attribute binding + reportMissingSlot(node, 'default') return - } else { - slotName = nameNode.value?.value } - 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 { + // check and report if slot name is declared or is undefined + reportMissingSlot(node, nameNode.value?.value) } } }) diff --git a/tests/lib/rules/require-explicit-slots.js b/tests/lib/rules/require-explicit-slots.js index 61536b483..92d1a1334 100644 --- a/tests/lib/rules/require-explicit-slots.js +++ b/tests/lib/rules/require-explicit-slots.js @@ -161,13 +161,29 @@ tester.run('require-explicit-slots', rule, { } } }, - // ignore attribute binding + // attribute binding { filename: 'test.vue', code: ` + ` + }, + { + filename: 'test.vue', + code: ` + `, + errors: [ + { + message: 'Slots must be explicitly defined.' + } + ] + }, { filename: 'test.vue', code: ` From 9af8bb1fd394ec1f2b2d04281bdc30c1762e12b5 Mon Sep 17 00:00:00 2001 From: waynzh Date: Wed, 6 Nov 2024 16:58:10 +0800 Subject: [PATCH 4/4] chore: remove comment --- lib/rules/require-explicit-slots.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/rules/require-explicit-slots.js b/lib/rules/require-explicit-slots.js index 2f2fcefcf..f87503bb7 100644 --- a/lib/rules/require-explicit-slots.js +++ b/lib/rules/require-explicit-slots.js @@ -185,7 +185,6 @@ module.exports = { const name = utils.getStringLiteralValue(expression) || undefined reportMissingSlot(node, name) } else { - // check and report if slot name is declared or is undefined reportMissingSlot(node, nameNode.value?.value) } }