From 838bc197e7777ee9db52e50f76f1eb6c8a86f2da Mon Sep 17 00:00:00 2001 From: waynzh Date: Thu, 3 Apr 2025 11:40:39 +0800 Subject: [PATCH 1/2] fix(no-dupe-keys): detect props destructure rename --- lib/rules/no-dupe-keys.js | 31 +++++++++++++++++++++++++++++++ tests/lib/rules/no-dupe-keys.js | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/lib/rules/no-dupe-keys.js b/lib/rules/no-dupe-keys.js index 01b85d9f5..1ad86d2a0 100644 --- a/lib/rules/no-dupe-keys.js +++ b/lib/rules/no-dupe-keys.js @@ -58,6 +58,33 @@ function isInsideInitializer(node, references) { ) } +/** + * Find if the prop is renamed in the pattern + * @param {Pattern} pattern - The destructuring pattern + * @param {string} propName - The original prop name + * @returns {boolean} - True if the prop is renamed + */ +function findRenamedProp(pattern, propName) { + if (!pattern || pattern.type !== 'ObjectPattern') { + return false + } + + for (const prop of pattern.properties) { + if (prop.type !== 'Property') continue + + if ( + prop.key.type === 'Identifier' && + prop.key.name === propName && + prop.value.type === 'Identifier' && + prop.value.name !== propName + ) { + return true + } + } + + return false +} + module.exports = { meta: { type: 'problem', @@ -118,6 +145,10 @@ module.exports = { for (const prop of props) { if (!prop.propName) continue + if (propsNode && findRenamedProp(propsNode, prop.propName)) { + continue + } + const variable = findVariable( utils.getScope(context, node), prop.propName diff --git a/tests/lib/rules/no-dupe-keys.js b/tests/lib/rules/no-dupe-keys.js index 124442ec2..2df95908c 100644 --- a/tests/lib/rules/no-dupe-keys.js +++ b/tests/lib/rules/no-dupe-keys.js @@ -466,7 +466,7 @@ ruleTester.run('no-dupe-keys', rule, { { filename: 'test.vue', code: ` - + `, @@ -475,7 +475,7 @@ ruleTester.run('no-dupe-keys', rule, { { filename: 'test.vue', code: ` - + `, @@ -500,6 +500,17 @@ ruleTester.run('no-dupe-keys', rule, { parser: require('vue-eslint-parser'), parserOptions: { parser: require.resolve('@typescript-eslint/parser') } } + }, + { + filename: 'test.vue', + code: ` + + `, + languageOptions: { parser: require('vue-eslint-parser') } } ], @@ -1105,6 +1116,24 @@ ruleTester.run('no-dupe-keys', rule, { line: 5 } ] + }, + { + filename: 'test.vue', + code: ` + + `, + languageOptions: { parser: require('vue-eslint-parser') }, + errors: [ + { + message: + "Duplicate key 'bar'. May cause name collision in script or template tag.", + line: 5 + } + ] } ] }) From 4470abc7539ec411ba80458d2c3d5c09766decec Mon Sep 17 00:00:00 2001 From: waynzh Date: Thu, 3 Apr 2025 11:50:10 +0800 Subject: [PATCH 2/2] refactor --- lib/rules/no-dupe-keys.js | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/rules/no-dupe-keys.js b/lib/rules/no-dupe-keys.js index 1ad86d2a0..ecfa787cf 100644 --- a/lib/rules/no-dupe-keys.js +++ b/lib/rules/no-dupe-keys.js @@ -59,14 +59,15 @@ function isInsideInitializer(node, references) { } /** - * Find if the prop is renamed in the pattern - * @param {Pattern} pattern - The destructuring pattern - * @param {string} propName - The original prop name - * @returns {boolean} - True if the prop is renamed + * Collects all renamed props from a pattern + * @param {Pattern | null} pattern - The destructuring pattern + * @returns {Set} - Set of prop names that have been renamed */ -function findRenamedProp(pattern, propName) { +function collectRenamedProps(pattern) { + const renamedProps = new Set() + if (!pattern || pattern.type !== 'ObjectPattern') { - return false + return renamedProps } for (const prop of pattern.properties) { @@ -74,15 +75,14 @@ function findRenamedProp(pattern, propName) { if ( prop.key.type === 'Identifier' && - prop.key.name === propName && prop.value.type === 'Identifier' && - prop.value.name !== propName + prop.key.name !== prop.value.name ) { - return true + renamedProps.add(prop.key.name) } } - return false + return renamedProps } module.exports = { @@ -142,10 +142,12 @@ module.exports = { node ] + const renamedProps = collectRenamedProps(propsNode) + for (const prop of props) { if (!prop.propName) continue - if (propsNode && findRenamedProp(propsNode, prop.propName)) { + if (renamedProps.has(prop.propName)) { continue }