From 5303bd68c4087c485e387f3c0164be1b0f007824 Mon Sep 17 00:00:00 2001 From: ItMaga Date: Wed, 31 May 2023 01:43:15 +0300 Subject: [PATCH 1/4] Add `vue/require-macro-variable-name` rule --- docs/rules/index.md | 1 + docs/rules/require-macro-variable-name.md | 84 +++++ lib/index.js | 1 + lib/rules/require-macro-variable-name.js | 103 ++++++ .../lib/rules/require-macro-variable-name.js | 296 ++++++++++++++++++ 5 files changed, 485 insertions(+) create mode 100644 docs/rules/require-macro-variable-name.md create mode 100644 lib/rules/require-macro-variable-name.js create mode 100644 tests/lib/rules/require-macro-variable-name.js diff --git a/docs/rules/index.md b/docs/rules/index.md index c3e4b8ed7..d6469f3e0 100644 --- a/docs/rules/index.md +++ b/docs/rules/index.md @@ -262,6 +262,7 @@ For example: | [vue/require-direct-export](./require-direct-export.md) | require the component to be directly exported | | :hammer: | | [vue/require-emit-validator](./require-emit-validator.md) | require type definitions in emits | :bulb: | :hammer: | | [vue/require-expose](./require-expose.md) | require declare public properties using `expose` | :bulb: | :hammer: | +| [vue/require-macro-variable-name](./require-macro-variable-name.md) | require a certain macro variable name | :bulb: | :hammer: | | [vue/require-name-property](./require-name-property.md) | require a name property in Vue components | :bulb: | :hammer: | | [vue/require-prop-comment](./require-prop-comment.md) | require props to have a comment | | :hammer: | | [vue/script-indent](./script-indent.md) | enforce consistent indentation in ` +``` + + + + + +```vue + + +``` + + + +## :wrench: Options + +```json +{ + "vue/require-macro-variable-name": ["error", { + "defineProps": "props", + "defineEmits": "emit", + "defineSlots": "slots", + "useSlots": "slots", + "useAttrs": "attrs" + }] +} +``` + +- `defineProps` - The name of the macro variable for `defineProps`. default: `props` +- `defineEmits` - The name of the macro variable for `defineEmits`. default: `emit` +- `defineSlots` - The name of the macro variable for `defineSlots`. default: `slots` +- `useSlots` - The name of the macro variable for `useSlots`. default: `slots` +- `useAttrs` - The name of the macro variable for `useAttrs`. default: `attrs` + +### With custom macro variable names + + + +```vue + +``` + + + +## :mag: Implementation + +- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/require-macro-variable-name.js) +- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/require-macro-variable-name.js) diff --git a/lib/index.js b/lib/index.js index 8ac5d9383..6ca29d49f 100644 --- a/lib/index.js +++ b/lib/index.js @@ -181,6 +181,7 @@ module.exports = { 'require-emit-validator': require('./rules/require-emit-validator'), 'require-explicit-emits': require('./rules/require-explicit-emits'), 'require-expose': require('./rules/require-expose'), + 'require-macro-variable-name': require('./rules/require-macro-variable-name'), 'require-name-property': require('./rules/require-name-property'), 'require-prop-comment': require('./rules/require-prop-comment'), 'require-prop-type-constructor': require('./rules/require-prop-type-constructor'), diff --git a/lib/rules/require-macro-variable-name.js b/lib/rules/require-macro-variable-name.js new file mode 100644 index 000000000..98eac880a --- /dev/null +++ b/lib/rules/require-macro-variable-name.js @@ -0,0 +1,103 @@ +/** + * @author ItMaga + * See LICENSE file in root directory for full license. + */ +'use strict' + +const utils = require('../utils') + +const DEFAULT_OPTIONS = { + defineProps: 'props', + defineEmits: 'emit', + defineSlots: 'slots', + useSlots: 'slots', + useAttrs: 'attrs' +} + +module.exports = { + meta: { + hasSuggestions: true, + type: 'suggestion', + docs: { + description: 'require a certain macro variable name', + categories: undefined, + url: '' + }, + fixable: null, + schema: [ + { + type: 'object', + properties: { + defineProps: { + type: 'string' + }, + defineEmits: { + type: 'string' + }, + defineSlots: { + type: 'string' + }, + useSlots: { + type: 'string' + }, + useAttrs: { + type: 'string' + } + }, + additionalProperties: false + } + ], + messages: { + requireName: + 'The variable name of "{{macroName}}" must be "{{variableName}}".', + changeName: 'Change the variable name to "{{variableName}}".' + } + }, + /** @param {RuleContext} context */ + create(context) { + const options = context.options[0] || DEFAULT_OPTIONS + const relevantMacros = [...Object.keys(DEFAULT_OPTIONS), 'withDefaults'] + + return utils.defineScriptSetupVisitor(context, { + VariableDeclarator(node) { + if ( + node.init && + node.init.type === 'CallExpression' && + node.init.callee.type === 'Identifier' && + relevantMacros.includes(node.init.callee.name) + ) { + const macroName = + node.init.callee.name === 'withDefaults' + ? 'defineProps' + : node.init.callee.name + + if ( + node.id.type === 'Identifier' && + node.id.name !== options[macroName] + ) { + context.report({ + node: node.id, + loc: node.id.loc, + messageId: 'requireName', + data: { + macroName, + variableName: options[macroName] + }, + suggest: [ + { + messageId: 'changeName', + data: { + variableName: options[macroName] + }, + fix(fixer) { + return fixer.replaceText(node.id, options[macroName]) + } + } + ] + }) + } + } + } + }) + } +} diff --git a/tests/lib/rules/require-macro-variable-name.js b/tests/lib/rules/require-macro-variable-name.js new file mode 100644 index 000000000..6e9fe7b1c --- /dev/null +++ b/tests/lib/rules/require-macro-variable-name.js @@ -0,0 +1,296 @@ +/** + * @author ItMaga + * See LICENSE file in root directory for full license. + */ +'use strict' + +const RuleTester = require('eslint').RuleTester +const rule = require('../../../lib/rules/require-macro-variable-name') + +const tester = new RuleTester({ + parser: require.resolve('vue-eslint-parser'), + parserOptions: { + ecmaVersion: 2020, + sourceType: 'module' + } +}) + +const customOptions = { + defineProps: 'customProps', + defineEmits: 'customEmits', + defineSlots: 'customSlots', + useSlots: 'customUseSlots', + useAttrs: 'customUseAttrs' +} + +tester.run('require-macro-variable-name', rule, { + valid: [ + { + filename: 'test.vue', + code: ` + + ` + }, + { + filename: 'test.vue', + code: ` + + ` + }, + { + filename: 'test.vue', + code: ` + + ` + }, + { + filename: 'test.vue', + code: ` + + ` + }, + { + filename: 'test.vue', + code: ` + + ` + }, + { + filename: 'test.vue', + code: ` + + `, + options: [customOptions] + } + ], + invalid: [ + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + message: 'The variable name of "defineProps" must be "props".', + line: 3, + column: 15, + suggestions: [ + { + desc: 'Change the variable name to "props".', + output: ` + + ` + } + ] + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + message: 'The variable name of "defineEmits" must be "emit".', + line: 3, + column: 15, + suggestions: [ + { + desc: 'Change the variable name to "emit".', + output: ` + + ` + } + ] + }, + { + message: 'The variable name of "defineSlots" must be "slots".', + line: 4, + column: 15, + suggestions: [ + { + desc: 'Change the variable name to "slots".', + output: ` + + ` + } + ] + }, + { + message: 'The variable name of "useAttrs" must be "attrs".', + line: 5, + column: 15, + suggestions: [ + { + desc: 'Change the variable name to "attrs".', + output: ` + + ` + } + ] + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + message: 'The variable name of "useSlots" must be "slots".', + line: 3, + column: 15, + suggestions: [ + { + desc: 'Change the variable name to "slots".', + output: ` + + ` + } + ] + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + message: 'The variable name of "defineProps" must be "props".', + line: 3, + column: 15, + suggestions: [ + { + desc: 'Change the variable name to "props".', + output: ` + + ` + } + ] + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + message: `The variable name of "defineSlots" must be "${customOptions.defineSlots}".`, + line: 3, + column: 15, + suggestions: [ + { + desc: `Change the variable name to "${customOptions.defineSlots}".`, + output: ` + + ` + } + ] + }, + { + message: `The variable name of "useSlots" must be "${customOptions.useSlots}".`, + line: 4, + column: 15, + suggestions: [ + { + desc: `Change the variable name to "${customOptions.useSlots}".`, + output: ` + + ` + } + ] + }, + { + message: `The variable name of "useAttrs" must be "${customOptions.useAttrs}".`, + line: 5, + column: 15, + suggestions: [ + { + desc: `Change the variable name to "${customOptions.useAttrs}".`, + output: ` + + ` + } + ] + } + ], + options: [customOptions] + } + ] +}) From 3f3bcb251d629e0cb85e57a1b77dc73c7225e335 Mon Sep 17 00:00:00 2001 From: ItMaga Date: Wed, 31 May 2023 01:47:13 +0300 Subject: [PATCH 2/4] lint fixes --- lib/rules/require-macro-variable-name.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rules/require-macro-variable-name.js b/lib/rules/require-macro-variable-name.js index 98eac880a..3f05e0624 100644 --- a/lib/rules/require-macro-variable-name.js +++ b/lib/rules/require-macro-variable-name.js @@ -21,7 +21,7 @@ module.exports = { docs: { description: 'require a certain macro variable name', categories: undefined, - url: '' + url: 'https://eslint.vuejs.org/rules/require-macro-variable-name.html' }, fixable: null, schema: [ @@ -56,7 +56,7 @@ module.exports = { /** @param {RuleContext} context */ create(context) { const options = context.options[0] || DEFAULT_OPTIONS - const relevantMacros = [...Object.keys(DEFAULT_OPTIONS), 'withDefaults'] + const relevantMacros = new Set([...Object.keys(DEFAULT_OPTIONS), 'withDefaults']) return utils.defineScriptSetupVisitor(context, { VariableDeclarator(node) { @@ -64,7 +64,7 @@ module.exports = { node.init && node.init.type === 'CallExpression' && node.init.callee.type === 'Identifier' && - relevantMacros.includes(node.init.callee.name) + relevantMacros.has(node.init.callee.name) ) { const macroName = node.init.callee.name === 'withDefaults' From abdeaa729c42423e5501d362e3cdd54d4c931930 Mon Sep 17 00:00:00 2001 From: ItMaga Date: Wed, 31 May 2023 01:50:46 +0300 Subject: [PATCH 3/4] format fixes --- lib/rules/require-macro-variable-name.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/rules/require-macro-variable-name.js b/lib/rules/require-macro-variable-name.js index 3f05e0624..f6f4de734 100644 --- a/lib/rules/require-macro-variable-name.js +++ b/lib/rules/require-macro-variable-name.js @@ -56,7 +56,10 @@ module.exports = { /** @param {RuleContext} context */ create(context) { const options = context.options[0] || DEFAULT_OPTIONS - const relevantMacros = new Set([...Object.keys(DEFAULT_OPTIONS), 'withDefaults']) + const relevantMacros = new Set([ + ...Object.keys(DEFAULT_OPTIONS), + 'withDefaults' + ]) return utils.defineScriptSetupVisitor(context, { VariableDeclarator(node) { From ef97732fe77ddfd40ceab35126f26292c96c08fe Mon Sep 17 00:00:00 2001 From: ItMaga Date: Wed, 31 May 2023 23:12:29 +0300 Subject: [PATCH 4/4] optional options --- lib/rules/require-macro-variable-name.js | 15 ++++++---- .../lib/rules/require-macro-variable-name.js | 28 +++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/lib/rules/require-macro-variable-name.js b/lib/rules/require-macro-variable-name.js index f6f4de734..e52c1196c 100644 --- a/lib/rules/require-macro-variable-name.js +++ b/lib/rules/require-macro-variable-name.js @@ -29,19 +29,24 @@ module.exports = { type: 'object', properties: { defineProps: { - type: 'string' + type: 'string', + default: DEFAULT_OPTIONS.defineProps }, defineEmits: { - type: 'string' + type: 'string', + default: DEFAULT_OPTIONS.defineEmits }, defineSlots: { - type: 'string' + type: 'string', + default: DEFAULT_OPTIONS.defineSlots }, useSlots: { - type: 'string' + type: 'string', + default: DEFAULT_OPTIONS.useSlots }, useAttrs: { - type: 'string' + type: 'string', + default: DEFAULT_OPTIONS.useAttrs } }, additionalProperties: false diff --git a/tests/lib/rules/require-macro-variable-name.js b/tests/lib/rules/require-macro-variable-name.js index 6e9fe7b1c..c54c224a5 100644 --- a/tests/lib/rules/require-macro-variable-name.js +++ b/tests/lib/rules/require-macro-variable-name.js @@ -291,6 +291,34 @@ tester.run('require-macro-variable-name', rule, { } ], options: [customOptions] + }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + message: `The variable name of "useAttrs" must be "attrs".`, + line: 4, + column: 15, + suggestions: [ + { + desc: `Change the variable name to "attrs".`, + output: ` + + ` + } + ] + } + ], + options: [{ defineSlots: 'slotsCustom' }] } ] })