From 207eb981df3bbab5d63846d7456d0925ab82394c Mon Sep 17 00:00:00 2001 From: Flo Edelmann Date: Fri, 25 Oct 2024 10:53:55 +0200 Subject: [PATCH 1/4] Update development dependencies (#2582) --- eslint.config.js | 33 ++++++++++++++++++++------------ lib/rules/define-macros-order.js | 2 +- lib/rules/no-unused-refs.js | 2 +- lib/utils/indent-common.js | 16 ++++++++-------- lib/utils/indent-ts.js | 2 +- lib/utils/property-references.js | 2 +- lib/utils/selector.js | 2 +- package.json | 22 ++++++++++----------- 8 files changed, 45 insertions(+), 36 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 5ddca6175..ea76a9d73 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -42,6 +42,27 @@ module.exports = [ } }, + // turn off some rules from shared configs in all files + { + rules: { + 'eslint-plugin/require-meta-docs-recommended': 'off', // use `categories` instead + 'eslint-plugin/require-meta-schema-description': 'off', + + 'unicorn/filename-case': 'off', + 'unicorn/no-null': 'off', + 'unicorn/no-array-callback-reference': 'off', // doesn't work well with TypeScript's custom type guards + 'unicorn/no-useless-undefined': 'off', + 'unicorn/prefer-global-this': 'off', + 'unicorn/prefer-module': 'off', + 'unicorn/prefer-optional-catch-binding': 'off', // not supported by current ESLint parser version + 'unicorn/prefer-at': 'off', // turn off to prevent make breaking changes (ref: #2146) + 'unicorn/prefer-node-protocol': 'off', // turn off to prevent make breaking changes (ref: #2146) + 'unicorn/prefer-string-replace-all': 'off', // turn off to prevent make breaking changes (ref: #2146) + 'unicorn/prefer-top-level-await': 'off', // turn off to prevent make breaking changes (ref: #2146) + 'unicorn/prevent-abbreviations': 'off' + } + }, + { files: ['**/*.js'], languageOptions: { @@ -143,7 +164,6 @@ module.exports = [ 'error', { pattern: '^(enforce|require|disallow).*[^.]$' } ], - 'eslint-plugin/require-meta-docs-recommended': 'off', // use `categories` instead 'eslint-plugin/require-meta-fixable': [ 'error', { catchNoFixerButFixableProperty: true } @@ -184,17 +204,6 @@ module.exports = [ 'error', { checkArrowFunctions: false } ], - 'unicorn/filename-case': 'off', - 'unicorn/no-null': 'off', - 'unicorn/no-array-callback-reference': 'off', // doesn't work well with TypeScript's custom type guards - 'unicorn/no-useless-undefined': 'off', - 'unicorn/prefer-optional-catch-binding': 'off', // not supported by current ESLint parser version - 'unicorn/prefer-module': 'off', - 'unicorn/prevent-abbreviations': 'off', - 'unicorn/prefer-at': 'off', // turn off to prevent make breaking changes (ref: #2146) - 'unicorn/prefer-node-protocol': 'off', // turn off to prevent make breaking changes (ref: #2146) - 'unicorn/prefer-string-replace-all': 'off', // turn off to prevent make breaking changes (ref: #2146) - 'unicorn/prefer-top-level-await': 'off', // turn off to prevent make breaking changes (ref: #2146) 'internal/require-eslint-community': ['error'] } diff --git a/lib/rules/define-macros-order.js b/lib/rules/define-macros-order.js index 920b5c294..55e6140cd 100644 --- a/lib/rules/define-macros-order.js +++ b/lib/rules/define-macros-order.js @@ -193,7 +193,7 @@ function create(context) { const targetStatementIndex = moveTargetNodes.indexOf(targetStatement) - if (targetStatementIndex >= 0) { + if (targetStatementIndex !== -1) { moveTargetNodes = moveTargetNodes.slice(0, targetStatementIndex) } reportNotOnTop(should.name, moveTargetNodes, targetStatement) diff --git a/lib/rules/no-unused-refs.js b/lib/rules/no-unused-refs.js index 79d82a39d..4896ce25a 100644 --- a/lib/rules/no-unused-refs.js +++ b/lib/rules/no-unused-refs.js @@ -128,7 +128,7 @@ module.exports = { } case 'CallExpression': { const argIndex = parent.arguments.indexOf(node) - if (argIndex > -1) { + if (argIndex !== -1) { // `foo($refs)` hasUnknown = true } diff --git a/lib/utils/indent-common.js b/lib/utils/indent-common.js index bacf43e15..d2879b120 100644 --- a/lib/utils/indent-common.js +++ b/lib/utils/indent-common.js @@ -1531,8 +1531,13 @@ module.exports.defineVisitor = function create( const tokens = tokenStore.getTokensBetween(importToken, node.source) const fromIndex = tokens.map((t) => t.value).lastIndexOf('from') const { fromToken, beforeTokens, afterTokens } = - fromIndex >= 0 + fromIndex === -1 ? { + fromToken: null, + beforeTokens: [...tokens, tokenStore.getFirstToken(node.source)], + afterTokens: [] + } + : { fromToken: tokens[fromIndex], beforeTokens: tokens.slice(0, fromIndex), afterTokens: [ @@ -1540,11 +1545,6 @@ module.exports.defineVisitor = function create( tokenStore.getFirstToken(node.source) ] } - : { - fromToken: null, - beforeTokens: [...tokens, tokenStore.getFirstToken(node.source)], - afterTokens: [] - } /** @type {ImportSpecifier[]} */ const namedSpecifiers = [] @@ -1556,7 +1556,7 @@ module.exports.defineVisitor = function create( removeTokens.shift() for (const token of removeTokens) { const i = beforeTokens.indexOf(token) - if (i >= 0) { + if (i !== -1) { beforeTokens.splice(i, 1) } } @@ -1576,7 +1576,7 @@ module.exports.defineVisitor = function create( rightBrace ]) { const i = beforeTokens.indexOf(token) - if (i >= 0) { + if (i !== -1) { beforeTokens.splice(i, 1) } } diff --git a/lib/utils/indent-ts.js b/lib/utils/indent-ts.js index f021e8a10..314858c9a 100644 --- a/lib/utils/indent-ts.js +++ b/lib/utils/indent-ts.js @@ -663,7 +663,7 @@ function defineVisitor({ const [, ...removeTokens] = tokenStore.getTokens(child) for (const token of removeTokens) { const i = afterTokens.indexOf(token) - if (i >= 0) { + if (i !== -1) { afterTokens.splice(i, 1) } } diff --git a/lib/utils/property-references.js b/lib/utils/property-references.js index 57fe59975..99c73293b 100644 --- a/lib/utils/property-references.js +++ b/lib/utils/property-references.js @@ -343,7 +343,7 @@ function definePropertyReferenceExtractor( case 'CallExpression': { const argIndex = parent.arguments.indexOf(node) // `foo(arg)` - return !withInTemplate && argIndex > -1 + return !withInTemplate && argIndex !== -1 ? extractFromCall(parent, argIndex) : NEVER } diff --git a/lib/utils/selector.js b/lib/utils/selector.js index 7b1f15c3f..b5aa1adfc 100644 --- a/lib/utils/selector.js +++ b/lib/utils/selector.js @@ -543,7 +543,7 @@ function parseNth(pseudoNode) { .toLowerCase() const openParenIndex = argumentsText.indexOf('(') const closeParenIndex = argumentsText.lastIndexOf(')') - if (openParenIndex < 0 || closeParenIndex < 0) { + if (openParenIndex === -1 || closeParenIndex === -1) { throw new SelectorError( `Cannot parse An+B micro syntax (:nth-xxx() argument): ${argumentsText}.` ) diff --git a/package.json b/package.json index 626268933..28ba4bd78 100644 --- a/package.json +++ b/package.json @@ -67,9 +67,9 @@ }, "devDependencies": { "@ota-meshi/site-kit-eslint-editor-vue": "^0.2.4", - "@stylistic/eslint-plugin": "^2.6.0", + "@stylistic/eslint-plugin": "^2.9.0", "@types/eslint": "^8.56.2", - "@types/eslint-visitor-keys": "^3.3.0", + "@types/eslint-visitor-keys": "^3.3.2", "@types/natural-compare": "^1.4.3", "@types/node": "^14.18.63", "@types/semver": "^7.5.8", @@ -78,24 +78,24 @@ "@typescript-eslint/types": "^7.18.0", "assert": "^2.1.0", "env-cmd": "^10.1.0", - "esbuild": "^0.23.0", + "esbuild": "^0.24.0", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-eslint-plugin": "~6.2.0", - "eslint-plugin-import": "^2.29.1", + "eslint-plugin-eslint-plugin": "~6.3.1", + "eslint-plugin-import": "^2.31.0", "eslint-plugin-jsonc": "^2.16.0", "eslint-plugin-node-dependencies": "^0.12.0", "eslint-plugin-prettier": "^5.2.1", - "eslint-plugin-unicorn": "^55.0.0", + "eslint-plugin-unicorn": "^56.0.0", "eslint-plugin-vue": "file:.", "espree": "^9.6.1", "events": "^3.3.0", - "markdownlint-cli": "^0.41.0", - "mocha": "^10.7.0", - "nyc": "^17.0.0", + "markdownlint-cli": "^0.42.0", + "mocha": "^10.7.3", + "nyc": "^17.1.0", "pathe": "^1.1.2", "prettier": "^3.3.3", - "typescript": "^5.5.4", - "vitepress": "^1.3.1" + "typescript": "^5.6.3", + "vitepress": "^1.4.1" } } From 86300c489f5f32a5a919cc5bc3bb3a43aad831dd Mon Sep 17 00:00:00 2001 From: Wayne Zhang Date: Mon, 28 Oct 2024 18:01:11 +0800 Subject: [PATCH 2/4] fix(custom-event-name-casing): check defineEmits variable name in template (#2585) --- lib/rules/custom-event-name-casing.js | 9 ++++- tests/lib/rules/custom-event-name-casing.js | 45 +++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/lib/rules/custom-event-name-casing.js b/lib/rules/custom-event-name-casing.js index 09cc2fc36..5c33980ec 100644 --- a/lib/rules/custom-event-name-casing.js +++ b/lib/rules/custom-event-name-casing.js @@ -101,6 +101,7 @@ module.exports = { create(context) { /** @type {Map,emitReferenceIds:Set}>} */ const setupContexts = new Map() + let emitParamName = '' const options = context.options.length === 1 && typeof context.options[0] !== 'string' ? // For backward compatibility @@ -189,7 +190,11 @@ module.exports = { // cannot check return } - if (callee.type === 'Identifier' && callee.name === '$emit') { + + if ( + callee.type === 'Identifier' && + (callee.name === '$emit' || callee.name === emitParamName) + ) { verify(nameWithLoc) } } @@ -209,6 +214,8 @@ module.exports = { if (emitParam.type !== 'Identifier') { return } + emitParamName = emitParam.name + // const emit = defineEmits() const variable = findVariable( utils.getScope(context, emitParam), diff --git a/tests/lib/rules/custom-event-name-casing.js b/tests/lib/rules/custom-event-name-casing.js index 76f9156fd..6b6e84c80 100644 --- a/tests/lib/rules/custom-event-name-casing.js +++ b/tests/lib/rules/custom-event-name-casing.js @@ -329,6 +329,22 @@ tester.run('custom-event-name-casing', rule, { `, options: ['kebab-case'] + }, + // setup defineEmits + { + filename: 'test.vue', + code: ` + + + + `, + options: ['kebab-case'] } ], invalid: [ @@ -605,6 +621,35 @@ tester.run('custom-event-name-casing', rule, { line: 4 } ] + }, + { + // https://github.com/vuejs/eslint-plugin-vue/issues/2577 + filename: 'test.vue', + code: ` + + + + `, + errors: [ + { + message: "Custom event name 'foo-bar' must be camelCase.", + line: 4 + }, + { + message: "Custom event name 'foo-bar' must be camelCase.", + line: 8 + }, + { + message: "Custom event name 'foo-bar' must be camelCase.", + line: 9 + } + ] } ] }) From 9a56de8959dea8728aa3020324cd00990ae477be Mon Sep 17 00:00:00 2001 From: Yosuke Ota Date: Mon, 28 Oct 2024 19:01:29 +0900 Subject: [PATCH 3/4] Fix false negatives and false positives in `vue/require-valid-default-prop` rule (#2586) --- lib/rules/require-valid-default-prop.js | 93 +++++++++++----- tests/lib/rules/require-valid-default-prop.js | 102 ++++++++++++++++++ 2 files changed, 168 insertions(+), 27 deletions(-) diff --git a/lib/rules/require-valid-default-prop.js b/lib/rules/require-valid-default-prop.js index ca834d620..9ddef9216 100644 --- a/lib/rules/require-valid-default-prop.js +++ b/lib/rules/require-valid-default-prop.js @@ -230,7 +230,7 @@ module.exports = { } /** - * @param {*} node + * @param {Expression} node * @param {ComponentObjectProp | ComponentTypeProp | ComponentInferTypeProp} prop * @param {Iterable} expectedTypeNames */ @@ -249,17 +249,22 @@ module.exports = { }) } + /** + * @typedef {object} DefaultDefine + * @property {Expression} expression + * @property {'assignment'|'withDefaults'|'defaultProperty'} src + */ /** * @param {(ComponentObjectProp | ComponentTypeProp | ComponentInferTypeProp)[]} props - * @param {(propName: string) => Expression[]} otherDefaultProvider + * @param {(propName: string) => Iterable} otherDefaultProvider */ function processPropDefs(props, otherDefaultProvider) { /** @type {PropDefaultFunctionContext[]} */ const propContexts = [] for (const prop of props) { let typeList - /** @type {Expression[]} */ - const defExprList = [] + /** @type {DefaultDefine[]} */ + const defaultList = [] if (prop.type === 'object') { if (prop.value.type === 'ObjectExpression') { const type = getPropertyNode(prop.value, 'type') @@ -268,9 +273,12 @@ module.exports = { typeList = getTypes(type.value) const def = getPropertyNode(prop.value, 'default') - if (!def) continue - - defExprList.push(def.value) + if (def) { + defaultList.push({ + src: 'defaultProperty', + expression: def.value + }) + } } else { typeList = getTypes(prop.value) } @@ -278,10 +286,10 @@ module.exports = { typeList = prop.types } if (prop.propName != null) { - defExprList.push(...otherDefaultProvider(prop.propName)) + defaultList.push(...otherDefaultProvider(prop.propName)) } - if (defExprList.length === 0) continue + if (defaultList.length === 0) continue const typeNames = new Set( typeList.filter((item) => NATIVE_TYPES.has(item)) @@ -289,8 +297,8 @@ module.exports = { // There is no native types detected if (typeNames.size === 0) continue - for (const defExpr of defExprList) { - const defType = getValueType(defExpr) + for (const defaultDef of defaultList) { + const defType = getValueType(defaultDef.expression) if (!defType) continue @@ -298,6 +306,11 @@ module.exports = { if (typeNames.has('Function')) { continue } + if (defaultDef.src === 'assignment') { + // Factory functions cannot be used in default definitions with initial value assignments. + report(defaultDef.expression, prop, typeNames) + continue + } if (defType.expression) { if (!defType.returnType || typeNames.has(defType.returnType)) { continue @@ -311,18 +324,23 @@ module.exports = { }) } } else { - if ( - typeNames.has(defType.type) && - !FUNCTION_VALUE_TYPES.has(defType.type) - ) { - continue + if (typeNames.has(defType.type)) { + if (defaultDef.src === 'assignment') { + continue + } + if (!FUNCTION_VALUE_TYPES.has(defType.type)) { + // For Array and Object, defaults must be defined in the factory function. + continue + } } report( - defExpr, + defaultDef.expression, prop, - [...typeNames].map((type) => - FUNCTION_VALUE_TYPES.has(type) ? 'Function' : type - ) + defaultDef.src === 'assignment' + ? typeNames + : [...typeNames].map((type) => + FUNCTION_VALUE_TYPES.has(type) ? 'Function' : type + ) ) } } @@ -425,12 +443,19 @@ module.exports = { utils.getWithDefaultsPropExpressions(node) const defaultsByAssignmentPatterns = utils.getDefaultPropExpressionsForPropsDestructure(node) - const propContexts = processPropDefs(props, (propName) => - [ - defaultsByWithDefaults[propName], - defaultsByAssignmentPatterns[propName]?.expression - ].filter(utils.isDef) - ) + const propContexts = processPropDefs(props, function* (propName) { + const withDefaults = defaultsByWithDefaults[propName] + if (withDefaults) { + yield { src: 'withDefaults', expression: withDefaults } + } + const assignmentPattern = defaultsByAssignmentPatterns[propName] + if (assignmentPattern) { + yield { + src: 'assignment', + expression: assignmentPattern.expression + } + } + }) scriptSetupPropsContexts.push({ node, props: propContexts }) }, /** @@ -450,7 +475,21 @@ module.exports = { } }, onDefinePropsExit() { - scriptSetupPropsContexts.pop() + const data = scriptSetupPropsContexts.pop() + if (!data) { + return + } + for (const { + prop, + types: typeNames, + default: defType + } of data.props) { + for (const returnType of defType.returnTypes) { + if (typeNames.has(returnType.type)) continue + + report(returnType.node, prop, typeNames) + } + } } }) ) diff --git a/tests/lib/rules/require-valid-default-prop.js b/tests/lib/rules/require-valid-default-prop.js index 0f4fd1902..238460876 100644 --- a/tests/lib/rules/require-valid-default-prop.js +++ b/tests/lib/rules/require-valid-default-prop.js @@ -316,6 +316,21 @@ ruleTester.run('require-valid-default-prop', rule, { languageOptions: { parser: require('vue-eslint-parser') } + }, + { + filename: 'test.vue', + code: ` + + `, + languageOptions: { + parser: require('vue-eslint-parser') + } } ], @@ -1098,6 +1113,93 @@ ruleTester.run('require-valid-default-prop', rule, { line: 6 } ] + }, + { + filename: 'test.vue', + code: ` + + `, + languageOptions: { + parser: require('vue-eslint-parser') + }, + errors: [ + { + message: "Type of the default value for 'foo' prop must be a number.", + line: 3 + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + languageOptions: { + parser: require('vue-eslint-parser') + }, + errors: [ + { + message: "Type of the default value for 'foo' prop must be a array.", + line: 3 + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + languageOptions: { + parser: require('vue-eslint-parser') + }, + errors: [ + { + message: "Type of the default value for 'foo' prop must be a array.", + line: 7 + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + languageOptions: { + parser: require('vue-eslint-parser') + }, + errors: [ + { + message: "Type of the default value for 'foo' prop must be a array.", + line: 3 + } + ] } ] }) From 50bde65aa298cc26c3369077e5bb6c25399e4b8d Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Mon, 28 Oct 2024 19:15:48 +0900 Subject: [PATCH 4/4] 9.30.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 28ba4bd78..47a4381a9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-vue", - "version": "9.29.1", + "version": "9.30.0", "description": "Official ESLint plugin for Vue.js", "main": "lib/index.js", "types": "lib/index.d.ts", pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy