Skip to content

Commit e4a6bdb

Browse files
committed
Merge branch 'master' into rewrite-ts
# Conflicts: # lib/plugin.js # tests/lib/rules/block-order.ts # tools/update-lib-plugin.js
2 parents 9c58e1f + 4b4630b commit e4a6bdb

26 files changed

+743
-857
lines changed

.changeset/gorgeous-colts-bathe.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"eslint-plugin-vue": patch
3+
---
4+
5+
Updates resources

.changeset/rich-bags-turn.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

.changeset/rich-zebras-type.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"eslint-plugin-vue": minor
3+
---
4+
5+
Changed `vue/no-negated-v-if-condition` suggestion to autofix

CHANGELOG.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# eslint-plugin-vue
22

3+
## 10.6.2
4+
5+
### Patch Changes
6+
7+
- Fixed false positives in non-intersecting conditions in [`vue/no-duplicate-class-names`](https://eslint.vuejs.org/rules/no-duplicate-class-names.html) and correctly detect duplicates in combining expressions ([#2980](https://github.com/vuejs/eslint-plugin-vue/pull/2980))
8+
- Fixed false positives for `TSImportType` in [`vue/script-indent`](https://eslint.vuejs.org/rules/script-indent.html) rule ([#2969](https://github.com/vuejs/eslint-plugin-vue/pull/2969))
9+
- Improved performance and type safety in [`vue/prefer-use-template-ref`](https://eslint.vuejs.org/rules/prefer-use-template-ref.html) ([#2982](https://github.com/vuejs/eslint-plugin-vue/pull/2982))
10+
311
## 10.6.1
412

513
### Patch Changes
@@ -11,15 +19,12 @@
1119
### Minor Changes
1220

1321
- Updated [`vue/no-import-compiler-macros`](https://eslint.vuejs.org/rules/no-import-compiler-macros.html) to clarify that macros are not allowed outside `<script setup>` ([#2938](https://github.com/vuejs/eslint-plugin-vue/pull/2938))
14-
1522
- Added new [`vue/no-duplicate-class-names`](https://eslint.vuejs.org/rules/no-duplicate-class-names.html) rule ([#2934](https://github.com/vuejs/eslint-plugin-vue/pull/2934))
1623

1724
### Patch Changes
1825

1926
- Fixed [`vue/no-v-html`](https://eslint.vuejs.org/rules/no-v-html.html) rule to allow ignoring call expressions ([#2950](https://github.com/vuejs/eslint-plugin-vue/pull/2950))
20-
2127
- Improved [`vue/define-macros-order`](https://eslint.vuejs.org/rules/define-macros-order.html) error messages to distinguish between macro placement and ordering issues ([#2953](https://github.com/vuejs/eslint-plugin-vue/pull/2953))
22-
2328
- Updated dependency [postcss-selector-parser](https://github.com/postcss/postcss-selector-parser) to v7.1.0 ([#2947](https://github.com/vuejs/eslint-plugin-vue/pull/2947))
2429

2530
## 10.5.1

docs/rules/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ For example:
238238
| [vue/no-empty-component-block] | disallow the `<template>` `<script>` `<style>` block to be empty | :wrench: | :hammer: |
239239
| [vue/no-import-compiler-macros] | disallow importing Vue compiler macros | :wrench: | :warning: |
240240
| [vue/no-multiple-objects-in-class] | disallow passing multiple objects in an array to class | | :hammer: |
241-
| [vue/no-negated-v-if-condition] | disallow negated conditions in v-if/v-else | :bulb: | :hammer: |
241+
| [vue/no-negated-v-if-condition] | disallow negated conditions in v-if/v-else | :wrench: | :hammer: |
242242
| [vue/no-potential-component-option-typo] | disallow a potential typo in your component property | :bulb: | :hammer: |
243243
| [vue/no-ref-object-reactivity-loss] | disallow usages of ref objects that can lead to loss of reactivity | | :warning: |
244244
| [vue/no-restricted-block] | disallow specific block | | :hammer: |

docs/rules/no-negated-v-if-condition.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ since: v10.4.0
1010

1111
> disallow negated conditions in v-if/v-else
1212
13-
- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
13+
- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
1414

1515
## :book: Rule Details
1616

1717
This rule disallows negated conditions in `v-if` and `v-else-if` directives which have an `v-else` branch.
1818

1919
Negated conditions make the code less readable. When there's an `else` clause, it's better to use a positive condition and switch the branches.
2020

21-
<eslint-code-block :rules="{'vue/no-negated-v-if-condition': ['error']}">
21+
<eslint-code-block fix :rules="{'vue/no-negated-v-if-condition': ['error']}">
2222

2323
```vue
2424
<template>

lib/rules/no-duplicate-class-names.js

Lines changed: 69 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,21 @@ const utils = require('../utils')
1111
* @param {VDirective} node
1212
* @param {Expression} [expression]
1313
* @param {boolean} [unconditional=true] whether the expression is unconditional
14-
* @return {IterableIterator<{ node: Literal | TemplateElement, unconditional: boolean }>}
14+
* @param {Expression} [parentExpr] parent expression for context
15+
* @return {IterableIterator<{ node: Literal | TemplateElement, unconditional: boolean, parentExpr?: Expression }>}
1516
*/
16-
function* extractClassNodes(node, expression, unconditional = true) {
17+
function* extractClassNodes(
18+
node,
19+
expression,
20+
unconditional = true,
21+
parentExpr
22+
) {
1723
const nodeExpression = expression ?? node.value?.expression
1824
if (!nodeExpression) return
1925

2026
switch (nodeExpression.type) {
2127
case 'Literal': {
22-
yield { node: nodeExpression, unconditional }
28+
yield { node: nodeExpression, unconditional, parentExpr }
2329
break
2430
}
2531
case 'ObjectExpression': {
@@ -29,42 +35,76 @@ function* extractClassNodes(node, expression, unconditional = true) {
2935
prop.key?.type === 'Literal' &&
3036
typeof prop.key.value === 'string'
3137
) {
32-
yield { node: prop.key, unconditional: false }
38+
yield {
39+
node: prop.key,
40+
unconditional: false,
41+
parentExpr: nodeExpression
42+
}
3343
}
3444
}
3545
break
3646
}
3747
case 'ArrayExpression': {
3848
for (const element of nodeExpression.elements) {
3949
if (!element || element.type === 'SpreadElement') continue
40-
yield* extractClassNodes(node, element, unconditional)
50+
yield* extractClassNodes(node, element, unconditional, nodeExpression)
4151
}
4252
break
4353
}
4454
case 'ConditionalExpression': {
45-
yield* extractClassNodes(node, nodeExpression.consequent, false)
46-
yield* extractClassNodes(node, nodeExpression.alternate, false)
55+
yield* extractClassNodes(
56+
node,
57+
nodeExpression.consequent,
58+
false,
59+
nodeExpression
60+
)
61+
yield* extractClassNodes(
62+
node,
63+
nodeExpression.alternate,
64+
false,
65+
nodeExpression
66+
)
4767
break
4868
}
4969
case 'TemplateLiteral': {
5070
for (const quasi of nodeExpression.quasis) {
51-
yield { node: quasi, unconditional }
71+
yield { node: quasi, unconditional, parentExpr: nodeExpression }
5272
}
5373
for (const expr of nodeExpression.expressions) {
54-
yield* extractClassNodes(node, expr, unconditional)
74+
yield* extractClassNodes(node, expr, unconditional, nodeExpression)
5575
}
5676
break
5777
}
5878
case 'BinaryExpression': {
5979
if (nodeExpression.operator === '+') {
60-
yield* extractClassNodes(node, nodeExpression.left, unconditional)
61-
yield* extractClassNodes(node, nodeExpression.right, unconditional)
80+
yield* extractClassNodes(
81+
node,
82+
nodeExpression.left,
83+
unconditional,
84+
nodeExpression
85+
)
86+
yield* extractClassNodes(
87+
node,
88+
nodeExpression.right,
89+
unconditional,
90+
nodeExpression
91+
)
6292
}
6393
break
6494
}
6595
case 'LogicalExpression': {
66-
yield* extractClassNodes(node, nodeExpression.left, unconditional)
67-
yield* extractClassNodes(node, nodeExpression.right, unconditional)
96+
yield* extractClassNodes(
97+
node,
98+
nodeExpression.left,
99+
unconditional,
100+
nodeExpression
101+
)
102+
yield* extractClassNodes(
103+
node,
104+
nodeExpression.right,
105+
false,
106+
nodeExpression
107+
)
68108
break
69109
}
70110
}
@@ -250,11 +290,15 @@ module.exports = {
250290
/** @type {Map<string, ASTNode>} */
251291
const seen = new Map()
252292

253-
/** @type {Map<string, {node: ASTNode, unconditional: boolean}>} */
293+
/** @type {Map<string, {node: ASTNode, unconditional: boolean, parentExpr?: Expression}>} */
254294
const collected = new Map()
255295

256296
const classNodes = extractClassNodes(node)
257-
for (const { node: reportNode, unconditional } of classNodes) {
297+
for (const {
298+
node: reportNode,
299+
unconditional,
300+
parentExpr
301+
} of classNodes) {
258302
// report fixable duplicates and collect reported class names
259303
const reportedClasses = reportDuplicateClasses(reportNode)
260304
if (reportedClasses) {
@@ -272,14 +316,21 @@ module.exports = {
272316
if (reported.has(className)) continue
273317
const existing = collected.get(className)
274318
if (existing) {
275-
// only add duplicate if at least one is unconditional
276-
if (existing.unconditional || unconditional) {
319+
// only add duplicate if at least one is unconditional, or share the same combining parent
320+
const isSameParent =
321+
parentExpr &&
322+
existing.parentExpr === parentExpr &&
323+
(parentExpr.type === 'BinaryExpression' ||
324+
parentExpr.type === 'TemplateLiteral')
325+
326+
if (existing.unconditional || unconditional || isSameParent) {
277327
duplicatesInExpression.add(className)
278328
}
279329
} else {
280330
collected.set(className, {
281331
node: reportNode.parent,
282-
unconditional
332+
unconditional,
333+
parentExpr
283334
})
284335
}
285336
// track unconditional duplicates separately for reporting

lib/rules/no-negated-v-if-condition.js

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,10 @@ module.exports = {
7676
categories: undefined,
7777
url: 'https://eslint.vuejs.org/rules/no-negated-v-if-condition.html'
7878
},
79-
fixable: null,
80-
hasSuggestions: true,
79+
fixable: 'code',
8180
schema: [],
8281
messages: {
83-
negatedCondition: 'Unexpected negated condition in v-if with v-else.',
84-
fixNegatedCondition:
85-
'Convert to positive condition and swap if/else blocks.'
82+
negatedCondition: 'Unexpected negated condition in v-if with v-else.'
8683
}
8784
},
8885
/** @param {RuleContext} context */
@@ -152,14 +149,7 @@ module.exports = {
152149
context.report({
153150
node: expression,
154151
messageId: 'negatedCondition',
155-
suggest: [
156-
{
157-
messageId: 'fixNegatedCondition',
158-
*fix(fixer) {
159-
yield* swapElements(fixer, element, elseElement, expression)
160-
}
161-
}
162-
]
152+
fix: (fixer) => swapElements(fixer, element, elseElement, expression)
163153
})
164154
}
165155

lib/rules/prefer-use-template-ref.js

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,40 +8,29 @@ const utils = require('../utils')
88

99
/**
1010
* @typedef ScriptRef
11-
* @type {{node: Expression, ref: string}}
11+
* @type {[string, CallExpression]}
1212
*/
1313

14-
/**
15-
* @param declarator {VariableDeclarator}
16-
* @returns {ScriptRef}
17-
* */
18-
function convertDeclaratorToScriptRef(declarator) {
19-
return {
20-
// @ts-ignore
21-
node: declarator.init,
22-
// @ts-ignore
23-
ref: declarator.id.name
24-
}
25-
}
26-
2714
/**
2815
* @param body {(Statement | ModuleDeclaration)[]}
2916
* @returns {ScriptRef[]}
3017
* */
3118
function getScriptRefsFromSetupFunction(body) {
32-
/** @type {VariableDeclaration[]} */
33-
const variableDeclarations = body.filter(
34-
(child) => child.type === 'VariableDeclaration'
35-
)
36-
const variableDeclarators = variableDeclarations.map(
37-
(declaration) => declaration.declarations[0]
38-
)
39-
const refDeclarators = variableDeclarators.filter((declarator) =>
40-
// @ts-ignore
41-
['ref', 'shallowRef'].includes(declarator.init?.callee?.name)
42-
)
19+
return body.flatMap((child) => {
20+
if (child.type === 'VariableDeclaration') {
21+
const declarator = child.declarations[0]
22+
23+
if (
24+
declarator.init?.type === 'CallExpression' &&
25+
declarator.init.callee?.type === 'Identifier' &&
26+
declarator.id.type === 'Identifier' &&
27+
['ref', 'shallowRef'].includes(declarator.init.callee.name)
28+
)
29+
return [[declarator.id.name, declarator.init]]
30+
}
4331

44-
return refDeclarators.map(convertDeclaratorToScriptRef)
32+
return []
33+
})
4534
}
4635

4736
/** @type {import("eslint").Rule.RuleModule} */
@@ -94,21 +83,20 @@ module.exports = {
9483
}),
9584
{
9685
'Program:exit'() {
86+
const scriptRefsMap = new Map(scriptRefs)
87+
9788
for (const templateRef of templateRefs) {
98-
const scriptRef = scriptRefs.find(
99-
(scriptRef) => scriptRef.ref === templateRef
100-
)
89+
const scriptRef = scriptRefsMap.get(templateRef)
10190

10291
if (!scriptRef) {
10392
continue
10493
}
10594

10695
context.report({
107-
node: scriptRef.node,
96+
node: scriptRef,
10897
messageId: 'preferUseTemplateRef',
10998
data: {
110-
// @ts-ignore
111-
name: scriptRef.node?.callee?.name
99+
name: /** @type {Identifier} */ (scriptRef.callee).name
112100
}
113101
})
114102
}

lib/utils/vue3-export-names.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,8 @@
266266
"DeprecationTypes",
267267
"createElementVNode",
268268
"WatchOptionsBase",
269+
"nodeOps",
270+
"patchProp",
269271
"TransitionProps",
270272
"Transition",
271273
"TransitionGroupProps",

0 commit comments

Comments
 (0)