diff --git a/lib/rules/no-dupe-keys.js b/lib/rules/no-dupe-keys.js
index f959f4b66..b007631f4 100644
--- a/lib/rules/no-dupe-keys.js
+++ b/lib/rules/no-dupe-keys.js
@@ -16,6 +16,48 @@ const utils = require('../utils')
/** @type {GroupName[]} */
const GROUP_NAMES = ['props', 'computed', 'data', 'methods', 'setup']
+/**
+ * Gets the props pattern node from given `defineProps()` node
+ * @param {CallExpression} node
+ * @returns {Pattern|null}
+ */
+function getPropsPattern(node) {
+ let target = node
+ if (
+ target.parent &&
+ target.parent.type === 'CallExpression' &&
+ target.parent.arguments[0] === target &&
+ target.parent.callee.type === 'Identifier' &&
+ target.parent.callee.name === 'withDefaults'
+ ) {
+ target = target.parent
+ }
+
+ if (
+ !target.parent ||
+ target.parent.type !== 'VariableDeclarator' ||
+ target.parent.init !== target
+ ) {
+ return null
+ }
+ return target.parent.id
+}
+
+/**
+ * Checks whether the initialization of the given variable declarator node contains one of the references.
+ * @param {VariableDeclarator} node
+ * @param {ESNode[]} references
+ */
+function isInsideInitializer(node, references) {
+ const init = node.init
+ if (!init) {
+ return false
+ }
+ return references.some(
+ (id) => init.range[0] <= id.range[0] && id.range[1] <= init.range[1]
+ )
+}
+
module.exports = {
meta: {
type: 'problem',
@@ -63,12 +105,27 @@ module.exports = {
}),
utils.defineScriptSetupVisitor(context, {
onDefinePropsEnter(node, props) {
+ const propsNode = getPropsPattern(node)
+ const propReferences = [
+ ...(propsNode ? extractReferences(propsNode) : []),
+ node
+ ]
+
for (const prop of props) {
if (!prop.propName) continue
const variable = findVariable(context.getScope(), prop.propName)
if (!variable || variable.defs.length === 0) continue
+ if (
+ variable.defs.some((def) => {
+ if (def.type !== 'Variable') return false
+ return isInsideInitializer(def.node, propReferences)
+ })
+ ) {
+ continue
+ }
+
context.report({
node: variable.defs[0].node,
message: "Duplicated key '{{name}}'.",
@@ -80,5 +137,32 @@ module.exports = {
}
})
)
+
+ /**
+ * Extracts references from the given node.
+ * @param {Pattern} node
+ * @returns {Identifier[]} References
+ */
+ function extractReferences(node) {
+ if (node.type === 'Identifier') {
+ const variable = findVariable(context.getScope(), node)
+ if (!variable) {
+ return []
+ }
+ return variable.references.map((ref) => ref.identifier)
+ }
+ if (node.type === 'ObjectPattern') {
+ return node.properties.flatMap((prop) =>
+ extractReferences(prop.type === 'Property' ? prop.value : prop)
+ )
+ }
+ if (node.type === 'AssignmentPattern') {
+ return extractReferences(node.left)
+ }
+ if (node.type === 'RestElement') {
+ return extractReferences(node.argument)
+ }
+ return []
+ }
}
}
diff --git a/tests/lib/rules/no-dupe-keys.js b/tests/lib/rules/no-dupe-keys.js
index e1294032f..29384f567 100644
--- a/tests/lib/rules/no-dupe-keys.js
+++ b/tests/lib/rules/no-dupe-keys.js
@@ -416,6 +416,86 @@ ruleTester.run('no-dupe-keys', rule, {
`,
parser: require.resolve('vue-eslint-parser'),
parserOptions: { parser: require.resolve('@typescript-eslint/parser') }
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ parser: require.resolve('vue-eslint-parser')
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ parser: require.resolve('vue-eslint-parser')
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ parser: require.resolve('vue-eslint-parser')
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ parser: require.resolve('vue-eslint-parser')
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ const {foo,bar} = defineProps(['foo', 'bar'])
+
+ `,
+ parser: require.resolve('vue-eslint-parser')
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ const {foo=42,bar='abc'} = defineProps(['foo', 'bar'])
+
+ `,
+ parser: require.resolve('vue-eslint-parser')
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ parser: require.resolve('vue-eslint-parser'),
+ parserOptions: { parser: require.resolve('@typescript-eslint/parser') }
}
],
@@ -912,7 +992,7 @@ ruleTester.run('no-dupe-keys', rule, {
+ `,
+ parser: require.resolve('vue-eslint-parser'),
+ errors: [
+ {
+ message: "Duplicated key 'bar'.",
+ line: 5
+ }
+ ]
}
]
})