diff --git a/docs/rules/multi-word-component-names.md b/docs/rules/multi-word-component-names.md index 25f862720..c54d199e9 100644 --- a/docs/rules/multi-word-component-names.md +++ b/docs/rules/multi-word-component-names.md @@ -169,6 +169,10 @@ export default { +## :couple: Related Rules + +- [vue/no-reserved-component-names](./no-reserved-component-names.md) + ## :books: Further Reading - [Style guide - Multi-word component names](https://vuejs.org/style-guide/rules-essential.html#use-multi-word-component-names) diff --git a/docs/rules/no-reserved-component-names.md b/docs/rules/no-reserved-component-names.md index 8a0813b94..80e430813 100644 --- a/docs/rules/no-reserved-component-names.md +++ b/docs/rules/no-reserved-component-names.md @@ -72,6 +72,10 @@ export default { +## :couple: Related Rules + +- [vue/multi-word-component-names](./multi-word-component-names.md) + ## :books: Further Reading - [List of html elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element) diff --git a/lib/rules/no-deprecated-router-link-tag-prop.js b/lib/rules/no-deprecated-router-link-tag-prop.js index 2feb0ee75..e2853f2af 100644 --- a/lib/rules/no-deprecated-router-link-tag-prop.js +++ b/lib/rules/no-deprecated-router-link-tag-prop.js @@ -45,7 +45,8 @@ module.exports = { uniqueItems: true, minItems: 1 } - } + }, + additionalProperties: false } ], messages: { diff --git a/lib/rules/no-dupe-keys.js b/lib/rules/no-dupe-keys.js index c812c5319..f959f4b66 100644 --- a/lib/rules/no-dupe-keys.js +++ b/lib/rules/no-dupe-keys.js @@ -4,10 +4,13 @@ */ 'use strict' +const { findVariable } = require('@eslint-community/eslint-utils') const utils = require('../utils') /** * @typedef {import('../utils').GroupName} GroupName + * @typedef {import('eslint').Scope.Variable} Variable + * @typedef {import('../utils').ComponentProp} ComponentProp */ /** @type {GroupName[]} */ @@ -39,24 +42,43 @@ module.exports = { const options = context.options[0] || {} const groups = new Set([...GROUP_NAMES, ...(options.groups || [])]) - return utils.executeOnVue(context, (obj) => { - /** @type {Set} */ - const usedNames = new Set() - const properties = utils.iterateProperties(obj, groups) - - for (const o of properties) { - if (usedNames.has(o.name)) { - context.report({ - node: o.node, - message: "Duplicated key '{{name}}'.", - data: { - name: o.name - } - }) + return utils.compositingVisitors( + utils.executeOnVue(context, (obj) => { + const properties = utils.iterateProperties(obj, groups) + /** @type {Set} */ + const usedNames = new Set() + for (const o of properties) { + if (usedNames.has(o.name)) { + context.report({ + node: o.node, + message: "Duplicated key '{{name}}'.", + data: { + name: o.name + } + }) + } + + usedNames.add(o.name) } + }), + utils.defineScriptSetupVisitor(context, { + onDefinePropsEnter(node, props) { + for (const prop of props) { + if (!prop.propName) continue - usedNames.add(o.name) - } - }) + const variable = findVariable(context.getScope(), prop.propName) + if (!variable || variable.defs.length === 0) continue + + context.report({ + node: variable.defs[0].node, + message: "Duplicated key '{{name}}'.", + data: { + name: prop.propName + } + }) + } + } + }) + ) } } diff --git a/lib/rules/no-duplicate-attr-inheritance.js b/lib/rules/no-duplicate-attr-inheritance.js index eea2bf765..a8661b091 100644 --- a/lib/rules/no-duplicate-attr-inheritance.js +++ b/lib/rules/no-duplicate-attr-inheritance.js @@ -26,12 +26,23 @@ module.exports = { /** @type {string | number | boolean | RegExp | BigInt | null} */ let inheritsAttrs = true - return Object.assign( - utils.executeOnVue(context, (node) => { - const inheritAttrsProp = utils.findProperty(node, 'inheritAttrs') + /** @param {ObjectExpression} node */ + function processOptions(node) { + const inheritAttrsProp = utils.findProperty(node, 'inheritAttrs') - if (inheritAttrsProp && inheritAttrsProp.value.type === 'Literal') { - inheritsAttrs = inheritAttrsProp.value.value + if (inheritAttrsProp && inheritAttrsProp.value.type === 'Literal') { + inheritsAttrs = inheritAttrsProp.value.value + } + } + + return utils.compositingVisitors( + utils.executeOnVue(context, processOptions), + utils.defineScriptSetupVisitor(context, { + onDefineOptionsEnter(node) { + if (node.arguments.length === 0) return + const define = node.arguments[0] + if (define.type !== 'ObjectExpression') return + processOptions(define) } }), utils.defineTemplateBodyVisitor(context, { diff --git a/lib/rules/no-undef-properties.js b/lib/rules/no-undef-properties.js index 36650f555..d7026eb62 100644 --- a/lib/rules/no-undef-properties.js +++ b/lib/rules/no-undef-properties.js @@ -127,6 +127,8 @@ module.exports = { /** @type { Set } */ this.reported = new Set() + + this.hasUnknownProperty = false } /** * Report @@ -135,6 +137,7 @@ module.exports = { * @param {boolean} [options.props] */ verifyReferences(references, options) { + if (this.hasUnknownProperty) return const report = this.report.bind(this) verifyUndefProperties(this.defineProperties, references, null) @@ -206,6 +209,10 @@ module.exports = { } }) } + + markAsHasUnknownProperty() { + this.hasUnknownProperty = true + } } /** @type {Map} */ @@ -280,6 +287,10 @@ module.exports = { const ctx = getVueComponentContext(programNode) for (const prop of props) { + if (prop.type === 'unknown') { + ctx.markAsHasUnknownProperty() + return + } if (!prop.propName) { continue } diff --git a/package.json b/package.json index 765d6c36b..ab4d9b718 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-vue", - "version": "9.13.0", + "version": "9.14.0", "description": "Official ESLint plugin for Vue.js", "main": "lib/index.js", "scripts": { diff --git a/tests/lib/rules/no-dupe-keys.js b/tests/lib/rules/no-dupe-keys.js index 26c9f7339..e1294032f 100644 --- a/tests/lib/rules/no-dupe-keys.js +++ b/tests/lib/rules/no-dupe-keys.js @@ -390,6 +390,32 @@ ruleTester.run('no-dupe-keys', rule, { }, } ` + }, + { + filename: 'test.vue', + code: ` + + `, + parser: require.resolve('vue-eslint-parser') + }, + { + filename: 'test.vue', + code: ` + + `, + parser: require.resolve('vue-eslint-parser'), + parserOptions: { parser: require.resolve('@typescript-eslint/parser') } } ], @@ -861,6 +887,85 @@ ruleTester.run('no-dupe-keys', rule, { line: 7 } ] + }, + { + filename: 'test.vue', + code: ` + + `, + parser: require.resolve('vue-eslint-parser'), + errors: [ + { + message: "Duplicated key 'foo'.", + line: 6 + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + parser: require.resolve('vue-eslint-parser'), + errors: [ + { + message: "Duplicated key 'baz'.", + line: 4 + }, + { + message: "Duplicated key 'foo'.", + line: 12 + }, + { + message: "Duplicated key 'bar'.", + line: 15 + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + parser: require.resolve('vue-eslint-parser'), + parserOptions: { parser: require.resolve('@typescript-eslint/parser') }, + errors: [ + { + message: "Duplicated key 'foo'.", + line: 8 + }, + { + message: "Duplicated key 'bar'.", + line: 9 + } + ] } ] }) diff --git a/tests/lib/rules/no-duplicate-attr-inheritance.js b/tests/lib/rules/no-duplicate-attr-inheritance.js index b4a00c228..7fc112123 100644 --- a/tests/lib/rules/no-duplicate-attr-inheritance.js +++ b/tests/lib/rules/no-duplicate-attr-inheritance.js @@ -79,6 +79,26 @@ ruleTester.run('no-duplicate-attr-inheritance', rule, { export default { } ` + }, + { + filename: 'test.vue', + code: ` + + + + ` + }, + { + filename: 'test.vue', + code: ` + + + ` } ], @@ -99,6 +119,38 @@ ruleTester.run('no-duplicate-attr-inheritance', rule, { `, errors: ['Set "inheritAttrs" to false.'] + }, + { + filename: 'test.vue', + code: ` + + + + `, + errors: [ + { + message: 'Set "inheritAttrs" to false.', + line: 7 + } + ] + }, + { + filename: 'test.vue', + code: ` + + + `, + errors: [ + { + message: 'Set "inheritAttrs" to false.', + line: 5 + } + ] } ] }) diff --git a/tests/lib/rules/no-undef-properties.js b/tests/lib/rules/no-undef-properties.js index 8b8ebbb1d..9a97fe1a9 100644 --- a/tests/lib/rules/no-undef-properties.js +++ b/tests/lib/rules/no-undef-properties.js @@ -6,6 +6,9 @@ const RuleTester = require('eslint').RuleTester const rule = require('../../../lib/rules/no-undef-properties') +const { + getTypeScriptFixtureTestOptions +} = require('../../test-utils/typescript') const tester = new RuleTester({ parser: require.resolve('vue-eslint-parser'), @@ -535,6 +538,26 @@ tester.run('no-undef-properties', rule, { }, }; ` + }, + + { + // unknown type + filename: 'test.vue', + code: ` + + + `, + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + } } ], @@ -1129,6 +1152,30 @@ tester.run('no-undef-properties', rule, { column: 46 } ] + }, + + { + // known type + filename: 'test.vue', + code: ` + + + `, + ...getTypeScriptFixtureTestOptions(), + errors: [ + { + message: "'unknown' is not defined.", + line: 11 + } + ] } ] })