From 87b05ba6d72a89ba3efd0d78446c986944b51683 Mon Sep 17 00:00:00 2001 From: waynzh Date: Thu, 13 Feb 2025 15:07:51 +0800 Subject: [PATCH 1/9] feat: add no-import-compiler-macros rule --- lib/rules/no-import-compiler-macros.js | 94 ++++++++++++++ tests/lib/rules/no-import-compiler-macros.js | 128 +++++++++++++++++++ 2 files changed, 222 insertions(+) create mode 100644 lib/rules/no-import-compiler-macros.js create mode 100644 tests/lib/rules/no-import-compiler-macros.js diff --git a/lib/rules/no-import-compiler-macros.js b/lib/rules/no-import-compiler-macros.js new file mode 100644 index 000000000..17c125093 --- /dev/null +++ b/lib/rules/no-import-compiler-macros.js @@ -0,0 +1,94 @@ +/** + * @author Wayne Zhang + * See LICENSE file in root directory for full license. + */ +'use strict' + +const COMPILER_MACROS = new Set([ + 'defineProps', + 'defineEmits', + 'defineExpose', + 'withDefaults' +]) + +const VUE_MODULES = new Set(['@vue/runtime-core', '@vue/runtime-dom', 'vue']) + +/** + * @param {Token} node + */ +function isComma(node) { + return node.type === 'Punctuator' && node.value === ',' +} + +/** + * @param {Token} node + */ +function isLeftCurlyBrace(node) { + return node.type === 'Punctuator' && node.value === '{' +} + +module.exports = { + meta: { + type: 'problem', + docs: { + description: 'disallow importing vue compiler macros', + categories: undefined, + url: 'https://eslint.vuejs.org/rules/no-import-compiler-macros.html' + }, + fixable: 'code', + schema: [], + messages: { + noImportCompilerMacros: + "'{{name}}' is a compiler macro and no longer needs to be imported from '{{source}}'." + } + }, + /** + * @param {RuleContext} context + * @returns {RuleListener} + */ + create(context) { + const sourceCode = context.getSourceCode() + + return { + ImportDeclaration(node) { + if (node.specifiers.length === 0) return + + if (VUE_MODULES.has(node.source.value)) { + for (const specifier of node.specifiers) { + if ( + specifier.type === 'ImportSpecifier' && + COMPILER_MACROS.has(specifier.imported.name) + ) { + context.report({ + node: specifier, + messageId: 'noImportCompilerMacros', + data: { + name: specifier.imported.name, + source: node.source.value + }, + fix: (fixer) => { + const tokenAfter = sourceCode.getTokenAfter(specifier) + const tokenBefore = sourceCode.getTokenBefore(specifier) + + const hasCommaAfter = isComma(tokenAfter) + const isFirstSpecifier = isLeftCurlyBrace(tokenBefore) + + const codeStart = hasCommaAfter + ? tokenBefore.range[1] + : isFirstSpecifier + ? specifier.range[0] + : tokenBefore.range[0] + const codeEnd = hasCommaAfter + ? tokenAfter.range[1] + : specifier.range[1] + + return fixer.removeRange([codeStart, codeEnd]) + } + }) + } + } + } + } + } + } +} diff --git a/tests/lib/rules/no-import-compiler-macros.js b/tests/lib/rules/no-import-compiler-macros.js new file mode 100644 index 000000000..018052db7 --- /dev/null +++ b/tests/lib/rules/no-import-compiler-macros.js @@ -0,0 +1,128 @@ +/** + * @author Wayne Zhang + * See LICENSE file in root directory for full license. + */ +'use strict' + +const RuleTester = require('../../eslint-compat').RuleTester +const rule = require('../../../lib/rules/no-import-compiler-macros') + +const tester = new RuleTester({ + languageOptions: { + parser: require('vue-eslint-parser'), + ecmaVersion: 2020, + sourceType: 'module' + } +}) + +tester.run('no-import-compiler-macros', rule, { + valid: [ + { + filename: 'test.vue', + code: ` + + ` + }, + { + filename: 'test.vue', + code: ` + + ` + } + ], + invalid: [ + { + filename: 'test.vue', + code: ` + + `, + output: ` + + `, + errors: [ + { + messageId: 'noImportCompilerMacros', + data: { + name: 'defineProps', + source: 'vue' + }, + line: 3, + column: 26 + }, + { + messageId: 'noImportCompilerMacros', + data: { + name: 'defineEmits', + source: '@vue/runtime-core' + }, + line: 4, + column: 16 + }, + { + messageId: 'noImportCompilerMacros', + data: { + name: 'withDefaults', + source: '@vue/runtime-core' + }, + line: 4, + column: 34 + }, + { + messageId: 'noImportCompilerMacros', + data: { + name: 'defineExpose', + source: '@vue/runtime-dom' + }, + line: 5, + column: 16 + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + output: ` + + `, + errors: [ + { + messageId: 'noImportCompilerMacros', + data: { + name: 'defineProps', + source: 'vue' + }, + line: 3, + column: 16 + }, + { + messageId: 'noImportCompilerMacros', + data: { + name: 'withDefaults', + source: 'vue' + }, + line: 3, + column: 29 + } + ] + } + ] +}) From d692fdd89f8f3cd64913434b7b5c2de1a773c1be Mon Sep 17 00:00:00 2001 From: waynzh Date: Thu, 13 Feb 2025 15:08:22 +0800 Subject: [PATCH 2/9] feat: add docs --- docs/rules/index.md | 2 ++ docs/rules/no-import-compiler-macros.md | 48 +++++++++++++++++++++++++ lib/index.js | 1 + 3 files changed, 51 insertions(+) create mode 100644 docs/rules/no-import-compiler-macros.md diff --git a/docs/rules/index.md b/docs/rules/index.md index f170f8031..eb816a64e 100644 --- a/docs/rules/index.md +++ b/docs/rules/index.md @@ -233,6 +233,7 @@ For example: | [vue/no-deprecated-model-definition] | disallow deprecated `model` definition (in Vue.js 3.0.0+) | :bulb: | :warning: | | [vue/no-duplicate-attr-inheritance] | enforce `inheritAttrs` to be set to `false` when using `v-bind="$attrs"` | | :hammer: | | [vue/no-empty-component-block] | disallow the `