From 8ba6d9a19dcf431b4ab048732c7d8a50c5b3a884 Mon Sep 17 00:00:00 2001 From: Kael Date: Sat, 7 Oct 2023 05:33:34 +1100 Subject: [PATCH 001/358] fix: replace typeParameters with typeArguments (#2292) --- lib/rules/define-emits-declaration.js | 3 ++- lib/rules/define-props-declaration.js | 3 ++- lib/rules/require-typed-ref.js | 4 +++- lib/rules/valid-define-emits.js | 8 +++---- lib/rules/valid-define-options.js | 5 ++-- lib/rules/valid-define-props.js | 8 +++---- lib/utils/indent-common.js | 16 +++++++------ lib/utils/indent-ts.js | 5 ++-- lib/utils/index.js | 16 +++++-------- lib/utils/ts-utils/ts-ast.js | 23 ++++++++----------- tests/integrations/eslint-plugin-import.js | 10 ++++---- .../util-types/ast/es-ast.ts | 10 ++++++-- 12 files changed, 57 insertions(+), 54 deletions(-) diff --git a/lib/rules/define-emits-declaration.js b/lib/rules/define-emits-declaration.js index 4daf4115c..478e1d840 100644 --- a/lib/rules/define-emits-declaration.js +++ b/lib/rules/define-emits-declaration.js @@ -47,7 +47,8 @@ module.exports = { } case 'runtime': { - if (node.typeParameters && node.typeParameters.params.length > 0) { + const typeArguments = node.typeArguments || node.typeParameters + if (typeArguments && typeArguments.params.length > 0) { context.report({ node, messageId: 'hasTypeArg' diff --git a/lib/rules/define-props-declaration.js b/lib/rules/define-props-declaration.js index c6fe4ffdc..d355bb2fc 100644 --- a/lib/rules/define-props-declaration.js +++ b/lib/rules/define-props-declaration.js @@ -47,7 +47,8 @@ module.exports = { } case 'runtime': { - if (node.typeParameters && node.typeParameters.params.length > 0) { + const typeArguments = node.typeArguments || node.typeParameters + if (typeArguments && typeArguments.params.length > 0) { context.report({ node, messageId: 'hasTypeArg' diff --git a/lib/rules/require-typed-ref.js b/lib/rules/require-typed-ref.js index 4ae12b040..2f691fec1 100644 --- a/lib/rules/require-typed-ref.js +++ b/lib/rules/require-typed-ref.js @@ -83,7 +83,9 @@ module.exports = { continue } - if (ref.node.typeParameters == null) { + const typeArguments = + ref.node.typeArguments || ref.node.typeParameters + if (typeArguments == null) { if ( ref.node.parent.type === 'VariableDeclarator' && ref.node.parent.id.type === 'Identifier' diff --git a/lib/rules/valid-define-emits.js b/lib/rules/valid-define-emits.js index 64d3fd15a..994302c60 100644 --- a/lib/rules/valid-define-emits.js +++ b/lib/rules/valid-define-emits.js @@ -47,8 +47,9 @@ module.exports = { onDefineEmitsEnter(node) { defineEmitsNodes.push(node) + const typeArguments = node.typeArguments || node.typeParameters if (node.arguments.length > 0) { - if (node.typeParameters && node.typeParameters.params.length > 0) { + if (typeArguments && typeArguments.params.length > 0) { // `defineEmits` has both a literal type and an argument. context.report({ node, @@ -59,10 +60,7 @@ module.exports = { emitsDefExpressions.add(node.arguments[0]) } else { - if ( - !node.typeParameters || - node.typeParameters.params.length === 0 - ) { + if (!typeArguments || typeArguments.params.length === 0) { emptyDefineEmits = node } } diff --git a/lib/rules/valid-define-options.js b/lib/rules/valid-define-options.js index 784ac3fbd..c1cd5b993 100644 --- a/lib/rules/valid-define-options.js +++ b/lib/rules/valid-define-options.js @@ -74,9 +74,10 @@ module.exports = { }) } - if (node.typeParameters) { + const typeArguments = node.typeArguments || node.typeParameters + if (typeArguments) { context.report({ - node: node.typeParameters, + node: typeArguments, messageId: 'typeArgs' }) } diff --git a/lib/rules/valid-define-props.js b/lib/rules/valid-define-props.js index 43cce1d3c..ecde56f15 100644 --- a/lib/rules/valid-define-props.js +++ b/lib/rules/valid-define-props.js @@ -48,8 +48,9 @@ module.exports = { onDefinePropsEnter(node) { definePropsNodes.push(node) + const typeArguments = node.typeArguments || node.typeParameters if (node.arguments.length > 0) { - if (node.typeParameters && node.typeParameters.params.length > 0) { + if (typeArguments && typeArguments.params.length > 0) { // `defineProps` has both a literal type and an argument. context.report({ node, @@ -60,10 +61,7 @@ module.exports = { propsDefExpressions.add(node.arguments[0]) } else { - if ( - !node.typeParameters || - node.typeParameters.params.length === 0 - ) { + if (!typeArguments || typeArguments.params.length === 0) { emptyDefineProps = node } } diff --git a/lib/utils/indent-common.js b/lib/utils/indent-common.js index 5b97f37b5..6119406e7 100644 --- a/lib/utils/indent-common.js +++ b/lib/utils/indent-common.js @@ -1168,21 +1168,22 @@ module.exports.defineVisitor = function create( }, /** @param {CallExpression} node */ CallExpression(node) { + const typeArguments = node.typeArguments || node.typeParameters const firstToken = tokenStore.getFirstToken(node) const rightToken = tokenStore.getLastToken(node) const leftToken = /** @type {Token} */ ( tokenStore.getTokenAfter( - node.typeParameters || node.callee, + typeArguments || node.callee, isOpeningParenToken ) ) - if (node.typeParameters) { - setOffset(tokenStore.getFirstToken(node.typeParameters), 1, firstToken) + if (typeArguments) { + setOffset(tokenStore.getFirstToken(typeArguments), 1, firstToken) } for (const optionalToken of tokenStore.getTokensBetween( - tokenStore.getLastToken(node.typeParameters || node.callee), + tokenStore.getLastToken(typeArguments || node.callee), leftToken, isOptionalToken )) { @@ -1694,19 +1695,20 @@ module.exports.defineVisitor = function create( }, /** @param {NewExpression} node */ NewExpression(node) { + const typeArguments = node.typeArguments || node.typeParameters const newToken = tokenStore.getFirstToken(node) const calleeToken = tokenStore.getTokenAfter(newToken) const rightToken = tokenStore.getLastToken(node) const leftToken = isClosingParenToken(rightToken) ? tokenStore.getFirstTokenBetween( - node.typeParameters || node.callee, + typeArguments || node.callee, rightToken, isOpeningParenToken ) : null - if (node.typeParameters) { - setOffset(tokenStore.getFirstToken(node.typeParameters), 1, calleeToken) + if (typeArguments) { + setOffset(tokenStore.getFirstToken(typeArguments), 1, calleeToken) } setOffset(calleeToken, 1, newToken) diff --git a/lib/utils/indent-ts.js b/lib/utils/indent-ts.js index c6f146f66..a4323b38f 100644 --- a/lib/utils/indent-ts.js +++ b/lib/utils/indent-ts.js @@ -332,9 +332,10 @@ function defineVisitor({ * @param {TSTypeReference | TSInstantiationExpression} node */ 'TSTypeReference, TSInstantiationExpression'(node) { - if (node.typeParameters) { + const typeArguments = node.typeArguments || node.typeParameters + if (typeArguments) { const firstToken = tokenStore.getFirstToken(node) - setOffset(tokenStore.getFirstToken(node.typeParameters), 1, firstToken) + setOffset(tokenStore.getFirstToken(typeArguments), 1, firstToken) } }, /** diff --git a/lib/utils/index.js b/lib/utils/index.js index 0c9a4ac99..8702c6260 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -2990,11 +2990,9 @@ function getComponentPropsFromDefineProps(context, node) { } ] } - if (node.typeParameters && node.typeParameters.params.length > 0) { - return getComponentPropsFromTypeDefine( - context, - node.typeParameters.params[0] - ) + const typeArguments = node.typeArguments || node.typeParameters + if (typeArguments && typeArguments.params.length > 0) { + return getComponentPropsFromTypeDefine(context, typeArguments.params[0]) } return [ { @@ -3025,11 +3023,9 @@ function getComponentEmitsFromDefineEmits(context, node) { } ] } - if (node.typeParameters && node.typeParameters.params.length > 0) { - return getComponentEmitsFromTypeDefine( - context, - node.typeParameters.params[0] - ) + const typeArguments = node.typeArguments || node.typeParameters + if (typeArguments && typeArguments.params.length > 0) { + return getComponentEmitsFromTypeDefine(context, typeArguments.params[0]) } return [ { diff --git a/lib/utils/ts-utils/ts-ast.js b/lib/utils/ts-utils/ts-ast.js index d019b77cd..8a95dd9b1 100644 --- a/lib/utils/ts-utils/ts-ast.js +++ b/lib/utils/ts-utils/ts-ast.js @@ -430,33 +430,28 @@ function inferRuntimeType(context, node, checked = new Set()) { return ['Array'] } case 'NonNullable': { - if (node.typeParameters && node.typeParameters.params[0]) { + const typeArguments = node.typeArguments || node.typeParameters + if (typeArguments && typeArguments.params[0]) { return inferRuntimeType( context, - node.typeParameters.params[0], + typeArguments.params[0], checked ).filter((t) => t !== 'null') } break } case 'Extract': { - if (node.typeParameters && node.typeParameters.params[1]) { - return inferRuntimeType( - context, - node.typeParameters.params[1], - checked - ) + const typeArguments = node.typeArguments || node.typeParameters + if (typeArguments && typeArguments.params[1]) { + return inferRuntimeType(context, typeArguments.params[1], checked) } break } case 'Exclude': case 'OmitThisParameter': { - if (node.typeParameters && node.typeParameters.params[0]) { - return inferRuntimeType( - context, - node.typeParameters.params[0], - checked - ) + const typeArguments = node.typeArguments || node.typeParameters + if (typeArguments && typeArguments.params[0]) { + return inferRuntimeType(context, typeArguments.params[0], checked) } break } diff --git a/tests/integrations/eslint-plugin-import.js b/tests/integrations/eslint-plugin-import.js index a408fdcdd..4e26508ce 100644 --- a/tests/integrations/eslint-plugin-import.js +++ b/tests/integrations/eslint-plugin-import.js @@ -30,10 +30,12 @@ describe('Integration with eslint-plugin-import', () => { if ( !semver.satisfies( process.version, - require(path.join( - __dirname, - 'eslint-plugin-import/node_modules/eslint/package.json' - )).engines.node + require( + path.join( + __dirname, + 'eslint-plugin-import/node_modules/eslint/package.json' + ) + ).engines.node ) ) { return diff --git a/typings/eslint-plugin-vue/util-types/ast/es-ast.ts b/typings/eslint-plugin-vue/util-types/ast/es-ast.ts index 944246411..db3a7f39c 100644 --- a/typings/eslint-plugin-vue/util-types/ast/es-ast.ts +++ b/typings/eslint-plugin-vue/util-types/ast/es-ast.ts @@ -519,7 +519,10 @@ export interface CallExpression extends HasParentNode { callee: Expression | Super arguments: (Expression | SpreadElement)[] optional: boolean - typeParameters?: TS.TSTypeParameterInstantiation + typeArguments?: TS.TSTypeParameterInstantiation + + /* @deprecated */ + typeParameters: never } export interface Super extends HasParentNode { type: 'Super' @@ -528,7 +531,10 @@ export interface NewExpression extends HasParentNode { type: 'NewExpression' callee: Expression arguments: (Expression | SpreadElement)[] - typeParameters?: TSTypeParameterInstantiation + typeArguments?: TSTypeParameterInstantiation + + /* @deprecated */ + typeParameters: never } interface BaseMemberExpression extends HasParentNode { type: 'MemberExpression' From 15b99af0552c89b5f94667cf5e2a0a92f603d08d Mon Sep 17 00:00:00 2001 From: Magomed Chemurziev Date: Wed, 11 Oct 2023 09:56:13 +0300 Subject: [PATCH 002/358] Insert `defineOptions` after import declarations in `vue/prefer-define-options` (#2275) --- lib/rules/prefer-define-options.js | 10 ++++++++- tests/lib/rules/prefer-define-options.js | 26 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/lib/rules/prefer-define-options.js b/lib/rules/prefer-define-options.js index cd9240445..90c83fa9a 100644 --- a/lib/rules/prefer-define-options.js +++ b/lib/rules/prefer-define-options.js @@ -34,9 +34,14 @@ module.exports = { let defineOptionsNode = null /** @type {ExportDefaultDeclaration | null} */ let exportDefaultDeclaration = null + /** @type {ImportDeclaration|null} */ + let lastImportDeclaration = null return utils.compositingVisitors( utils.defineScriptSetupVisitor(context, { + ImportDeclaration(node) { + lastImportDeclaration = node + }, onDefineOptionsEnter(node) { defineOptionsNode = node } @@ -109,10 +114,13 @@ module.exports = { }) } + /** @type {VStartTag | ImportDeclaration} */ + const insertAfterTag = lastImportDeclaration || scriptSetup.startTag + return [ fixer.removeRange(removeRange), fixer.insertTextAfter( - scriptSetup.startTag, + insertAfterTag, `\ndefineOptions(${sourceCode.getText(node.declaration)})\n` ) ] diff --git a/tests/lib/rules/prefer-define-options.js b/tests/lib/rules/prefer-define-options.js index 2fd9bdfec..f3d9d88b0 100644 --- a/tests/lib/rules/prefer-define-options.js +++ b/tests/lib/rules/prefer-define-options.js @@ -104,6 +104,32 @@ defineOptions({ name: 'Foo' }) line: 4 } ] + }, + { + filename: 'test.vue', + code: ` + + + `, + output: ` + + `, + errors: [ + { + message: 'Use `defineOptions` instead of default export.', + line: 7 + } + ] } ] }) From 467631e402e94b34c413f42be261c04f0d00f7c0 Mon Sep 17 00:00:00 2001 From: Yosuke Ota Date: Tue, 24 Oct 2023 17:41:43 +0900 Subject: [PATCH 003/358] Fixed false negatives for whitespace in `vue/require-toggle-inside-transition` rule (#2293) --- lib/rules/require-toggle-inside-transition.js | 3 ++- tests/lib/rules/require-toggle-inside-transition.js | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/rules/require-toggle-inside-transition.js b/lib/rules/require-toggle-inside-transition.js index 365fbc713..bdbb5c2dd 100644 --- a/lib/rules/require-toggle-inside-transition.js +++ b/lib/rules/require-toggle-inside-transition.js @@ -47,7 +47,8 @@ module.exports = { return utils.defineTemplateBodyVisitor(context, { /** @param {VElement} node */ "VElement[name='transition'] > VElement"(node) { - if (node.parent.children[0] !== node) { + const child = node.parent.children.find(utils.isVElement) + if (child !== node) { return } verifyInsideElement(node) diff --git a/tests/lib/rules/require-toggle-inside-transition.js b/tests/lib/rules/require-toggle-inside-transition.js index 73ec47206..0b64daaa3 100644 --- a/tests/lib/rules/require-toggle-inside-transition.js +++ b/tests/lib/rules/require-toggle-inside-transition.js @@ -97,6 +97,11 @@ tester.run('require-toggle-inside-transition', rule, { filename: 'test.vue', code: '', errors: [{ messageId: 'expected' }] + }, + { + filename: 'test.vue', + code: '', + errors: [{ messageId: 'expected' }] } ] }) From 66a678ffe1c58eac1cf52881b4f777f9a9bfde1c Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Tue, 24 Oct 2023 16:43:20 +0800 Subject: [PATCH 004/358] fix(component-name-in-template-casing): ignore vue template syntax (#2301) --- lib/rules/component-name-in-template-casing.js | 3 ++- lib/utils/index.js | 10 ++++++++++ lib/utils/vue-builtin-elements.js | 1 + .../rules/component-name-in-template-casing.js | 18 ++++++++++-------- 4 files changed, 23 insertions(+), 9 deletions(-) create mode 100644 lib/utils/vue-builtin-elements.js diff --git a/lib/rules/component-name-in-template-casing.js b/lib/rules/component-name-in-template-casing.js index fc1849b18..487af7714 100644 --- a/lib/rules/component-name-in-template-casing.js +++ b/lib/rules/component-name-in-template-casing.js @@ -123,7 +123,8 @@ module.exports = { if ( (!utils.isHtmlElementNode(node) && !utils.isSvgElementNode(node)) || utils.isHtmlWellKnownElementName(node.rawName) || - utils.isSvgWellKnownElementName(node.rawName) + utils.isSvgWellKnownElementName(node.rawName) || + utils.isVueBuiltInElementName(node.rawName) ) { return false } diff --git a/lib/utils/index.js b/lib/utils/index.js index 8702c6260..22119ae98 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -54,6 +54,7 @@ const VUE2_BUILTIN_COMPONENT_NAMES = new Set( const VUE3_BUILTIN_COMPONENT_NAMES = new Set( require('./vue3-builtin-components') ) +const VUE_BUILTIN_ELEMENT_NAMES = new Set(require('./vue-builtin-elements')) const path = require('path') const vueEslintParser = require('vue-eslint-parser') const { traverseNodes, getFallbackKeys, NS } = vueEslintParser.AST @@ -867,6 +868,15 @@ module.exports = { ) }, + /** + * Check whether the given name is Vue builtin element name or not. + * @param {string} name The name to check. + * @returns {boolean} `true` if the name is a builtin Vue element name + */ + isVueBuiltInElementName(name) { + return VUE_BUILTIN_ELEMENT_NAMES.has(name.toLowerCase()) + }, + /** * Check whether the given name is Vue builtin directive name or not. * @param {string} name The name to check. diff --git a/lib/utils/vue-builtin-elements.js b/lib/utils/vue-builtin-elements.js new file mode 100644 index 000000000..70ddb6b7d --- /dev/null +++ b/lib/utils/vue-builtin-elements.js @@ -0,0 +1 @@ +module.exports = ['template', 'slot', 'component'] diff --git a/tests/lib/rules/component-name-in-template-casing.js b/tests/lib/rules/component-name-in-template-casing.js index 3aa4ee43d..992063808 100644 --- a/tests/lib/rules/component-name-in-template-casing.js +++ b/tests/lib/rules/component-name-in-template-casing.js @@ -86,6 +86,10 @@ tester.run('component-name-in-template-casing', rule, { code: '', options: ['PascalCase', { registeredComponentsOnly: false }] }, + { + code: '', + options: ['PascalCase', { registeredComponentsOnly: false }] + }, // kebab-case { @@ -108,6 +112,10 @@ tester.run('component-name-in-template-casing', rule, { code: '', options: ['kebab-case', { registeredComponentsOnly: false }] }, + { + code: '', + options: ['kebab-case', { registeredComponentsOnly: false }] + }, // ignores { @@ -859,7 +867,7 @@ tester.run('component-name-in-template-casing', rule, { `, output: ` `, errors: [ @@ -1058,11 +1065,6 @@ tester.run('component-name-in-template-casing', rule, { message: 'Component name "hello-world5" is not PascalCase.', line: 18, column: 17 - }, - { - message: 'Component name "component" is not PascalCase.', - line: 19, - column: 17 } ] } From 5a298023a13f80025d889bff313c6a06faf39e74 Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Tue, 24 Oct 2023 17:54:31 +0900 Subject: [PATCH 005/358] 9.18.0 --- lib/utils/vue3-export-names.json | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/utils/vue3-export-names.json b/lib/utils/vue3-export-names.json index 15b72e2fc..f676e8440 100644 --- a/lib/utils/vue3-export-names.json +++ b/lib/utils/vue3-export-names.json @@ -276,6 +276,7 @@ "IframeHTMLAttributes", "ImgHTMLAttributes", "InsHTMLAttributes", + "InputTypeHTMLAttribute", "InputHTMLAttributes", "KeygenHTMLAttributes", "LabelHTMLAttributes", diff --git a/package.json b/package.json index eb3750f4a..274aa2da2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-vue", - "version": "9.17.0", + "version": "9.18.0", "description": "Official ESLint plugin for Vue.js", "main": "lib/index.js", "scripts": { From 5fc47e5151ec991b17a44e1c5ce21ba40553578a Mon Sep 17 00:00:00 2001 From: DevilTea Date: Fri, 27 Oct 2023 16:25:56 +0800 Subject: [PATCH 006/358] Fix require-toggle-inside-transition should respect "", "v-bind:key", attribute "appear" (#2303) Co-authored-by: williamc --- lib/rules/require-toggle-inside-transition.js | 12 +++++++++++- tests/lib/rules/require-toggle-inside-transition.js | 12 ++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/rules/require-toggle-inside-transition.js b/lib/rules/require-toggle-inside-transition.js index bdbb5c2dd..fc46f75f9 100644 --- a/lib/rules/require-toggle-inside-transition.js +++ b/lib/rules/require-toggle-inside-transition.js @@ -32,9 +32,18 @@ module.exports = { if (utils.isCustomComponent(element)) { return } + + /** @type VElement */ // @ts-expect-error + const parent = element.parent + if (utils.hasAttribute(parent, 'appear')) { + return + } + if ( + element.name !== 'slot' && !utils.hasDirective(element, 'if') && - !utils.hasDirective(element, 'show') + !utils.hasDirective(element, 'show') && + !utils.hasDirective(element, 'bind', 'key') ) { context.report({ node: element.startTag, @@ -51,6 +60,7 @@ module.exports = { if (child !== node) { return } + verifyInsideElement(node) } }) diff --git a/tests/lib/rules/require-toggle-inside-transition.js b/tests/lib/rules/require-toggle-inside-transition.js index 0b64daaa3..18a33aa3e 100644 --- a/tests/lib/rules/require-toggle-inside-transition.js +++ b/tests/lib/rules/require-toggle-inside-transition.js @@ -57,6 +57,18 @@ tester.run('require-toggle-inside-transition', rule, { { filename: 'test.vue', code: '' + }, + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: '' } ], invalid: [ From b3129f9f3551f1ac189a0ee84b43561b3d0e4cc2 Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Fri, 27 Oct 2023 17:28:30 +0900 Subject: [PATCH 007/358] 9.18.1 --- lib/utils/vue3-export-names.json | 18 +++++++++--------- package.json | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/utils/vue3-export-names.json b/lib/utils/vue3-export-names.json index f676e8440..af9cda39c 100644 --- a/lib/utils/vue3-export-names.json +++ b/lib/utils/vue3-export-names.json @@ -75,6 +75,15 @@ "Suspense", "SuspenseBoundary", "RootHydrateFunction", + "BaseTransitionProps", + "TransitionHooks", + "TransitionState", + "useTransitionState", + "BaseTransitionPropsValidators", + "BaseTransition", + "resolveTransitionHooks", + "setTransitionHooks", + "getTransitionRawChildren", "Renderer", "HydrationRenderer", "RootRenderFunction", @@ -135,15 +144,6 @@ "AppContext", "Plugin", "CreateAppFunction", - "BaseTransitionProps", - "TransitionHooks", - "TransitionState", - "useTransitionState", - "BaseTransitionPropsValidators", - "BaseTransition", - "resolveTransitionHooks", - "setTransitionHooks", - "getTransitionRawChildren", "TeleportProps", "Teleport", "resolveComponent", diff --git a/package.json b/package.json index 274aa2da2..35236e9fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-vue", - "version": "9.18.0", + "version": "9.18.1", "description": "Official ESLint plugin for Vue.js", "main": "lib/index.js", "scripts": { From 3e6644594303d5d9d7a20b9b0b42703acbd2dd07 Mon Sep 17 00:00:00 2001 From: Federico Giovagnoli Date: Wed, 29 Nov 2023 06:32:42 +0100 Subject: [PATCH 008/358] Add support for stricter `type-literal` option in `vue/define-emits-declaration` (#2315) --- docs/rules/define-emits-declaration.md | 46 ++++++++- lib/rules/define-emits-declaration.js | 35 ++++++- tests/lib/rules/define-emits-declaration.js | 105 +++++++++++++++++++- 3 files changed, 176 insertions(+), 10 deletions(-) diff --git a/docs/rules/define-emits-declaration.md b/docs/rules/define-emits-declaration.md index 2fd4b478f..7a11524c5 100644 --- a/docs/rules/define-emits-declaration.md +++ b/docs/rules/define-emits-declaration.md @@ -5,13 +5,15 @@ title: vue/define-emits-declaration description: enforce declaration style of `defineEmits` since: v9.5.0 --- + # vue/define-emits-declaration > enforce declaration style of `defineEmits` ## :book: Rule Details -This rule enforces `defineEmits` typing style which you should use `type-based` or `runtime` declaration. +This rule enforces `defineEmits` typing style which you should use `type-based`, strict `type-literal` +(introduced in Vue 3.3), or `runtime` declaration. This rule only works in setup script and `lang="ts"`. @@ -25,6 +27,12 @@ const emit = defineEmits<{ (e: 'update', value: string): void }>() +/* ✓ GOOD */ +const emit = defineEmits<{ + change: [id: number] + update: [value: string] +}>() + /* ✗ BAD */ const emit = defineEmits({ change: (id) => typeof id == 'number', @@ -41,10 +49,11 @@ const emit = defineEmits(['change', 'update']) ## :wrench: Options ```json - "vue/define-emits-declaration": ["error", "type-based" | "runtime"] + "vue/define-emits-declaration": ["error", "type-based" | "type-literal" | "runtime"] ``` -- `type-based` (default) enforces type-based declaration +- `type-based` (default) enforces type based declaration +- `type-literal` enforces strict "type literal" type based declaration - `runtime` enforces runtime declaration ### `runtime` @@ -72,6 +81,37 @@ const emit = defineEmits(['change', 'update']) +### `type-literal` + + + +```vue + +``` + + + ## :couple: Related Rules - [vue/define-props-declaration](./define-props-declaration.md) diff --git a/lib/rules/define-emits-declaration.js b/lib/rules/define-emits-declaration.js index 478e1d840..d17e76537 100644 --- a/lib/rules/define-emits-declaration.js +++ b/lib/rules/define-emits-declaration.js @@ -6,6 +6,11 @@ const utils = require('../utils') +/** + * @typedef {import('@typescript-eslint/types').TSESTree.TSTypeLiteral} TSTypeLiteral + * + */ + module.exports = { meta: { type: 'suggestion', @@ -17,12 +22,14 @@ module.exports = { fixable: null, schema: [ { - enum: ['type-based', 'runtime'] + enum: ['type-based', 'type-literal', 'runtime'] } ], messages: { - hasArg: 'Use type-based declaration instead of runtime declaration.', - hasTypeArg: 'Use runtime declaration instead of type-based declaration.' + hasArg: 'Use type based declaration instead of runtime declaration.', + hasTypeArg: 'Use runtime declaration instead of type based declaration.', + hasTypeCallArg: + 'Use new type literal declaration instead of the old call signature declaration.' } }, /** @param {RuleContext} context */ @@ -46,6 +53,28 @@ module.exports = { break } + case 'type-literal': { + if (node.arguments.length > 0) { + context.report({ + node, + messageId: 'hasArg' + }) + return + } + + const typeArguments = node.typeArguments || node.typeParameters + const param = /** @type {TSTypeLiteral} */ (typeArguments.params[0]) + for (const memberNode of param.members) { + if (memberNode.type !== 'TSPropertySignature') { + context.report({ + node: memberNode, + messageId: 'hasTypeCallArg' + }) + } + } + break + } + case 'runtime': { const typeArguments = node.typeArguments || node.typeParameters if (typeArguments && typeArguments.params.length > 0) { diff --git a/tests/lib/rules/define-emits-declaration.js b/tests/lib/rules/define-emits-declaration.js index 372fa1a4c..43ecb385b 100644 --- a/tests/lib/rules/define-emits-declaration.js +++ b/tests/lib/rules/define-emits-declaration.js @@ -63,6 +63,36 @@ tester.run('define-emits-declaration', rule, { `, options: ['runtime'] }, + { + filename: 'test.vue', + code: ` + + `, + options: ['type-based'], + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + } + }, + { + filename: 'test.vue', + code: ` + + `, + options: ['type-literal'], + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + } + }, { filename: 'test.vue', // ignore code without defineEmits @@ -82,7 +112,7 @@ tester.run('define-emits-declaration', rule, { code: ` + `, + options: ['type-literal'], + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + }, + errors: [ + { + message: 'Use type based declaration instead of runtime declaration.', line: 3 } ] @@ -142,10 +190,59 @@ tester.run('define-emits-declaration', rule, { }, errors: [ { - message: 'Use runtime declaration instead of type-based declaration.', + message: 'Use runtime declaration instead of type based declaration.', line: 3 } ] + }, + { + filename: 'test.vue', + code: ` + + `, + options: ['type-literal'], + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + }, + errors: [ + { + message: + 'Use new type literal declaration instead of the old call signature declaration.', + line: 4 + }, + { + message: + 'Use new type literal declaration instead of the old call signature declaration.', + line: 5 + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + options: ['type-literal'], + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + }, + errors: [ + { + message: + 'Use new type literal declaration instead of the old call signature declaration.', + line: 5 + } + ] } ] }) From 4db54a77e63ed7dfd4b734bf00338137f6387af2 Mon Sep 17 00:00:00 2001 From: Magomed Chemurziev Date: Wed, 29 Nov 2023 08:43:20 +0300 Subject: [PATCH 009/358] Add `vue/no-unused-emit-declarations` rule (#2312) Co-authored-by: Flo Edelmann --- docs/rules/index.md | 1 + docs/rules/no-unused-emit-declarations.md | 59 ++ docs/rules/require-explicit-emits.md | 4 + lib/index.js | 1 + lib/rules/no-unused-emit-declarations.js | 330 +++++++++ .../lib/rules/no-unused-emit-declarations.js | 693 ++++++++++++++++++ 6 files changed, 1088 insertions(+) create mode 100644 docs/rules/no-unused-emit-declarations.md create mode 100644 lib/rules/no-unused-emit-declarations.js create mode 100644 tests/lib/rules/no-unused-emit-declarations.js diff --git a/docs/rules/index.md b/docs/rules/index.md index 224cddc7d..a7d3047a3 100644 --- a/docs/rules/index.md +++ b/docs/rules/index.md @@ -251,6 +251,7 @@ For example: | [vue/no-undef-components](./no-undef-components.md) | disallow use of undefined components in `