Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitb34b1a9

Browse files
committed
Add new vue/require-mayberef-unwrap rule
1 parent3d9e15e commitb34b1a9

File tree

5 files changed

+640
-0
lines changed

5 files changed

+640
-0
lines changed

‎docs/rules/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ Rules in this category are enabled for all presets provided by eslint-plugin-vue
9797
|[vue/no-watch-after-await]| disallow asynchronously registered`watch`||:three::hammer:|
9898
|[vue/prefer-import-from-vue]| enforce import from 'vue' instead of import from '@vue/*'|:wrench:|:three::hammer:|
9999
|[vue/require-component-is]| require`v-bind:is` of`<component>` elements||:three::two::warning:|
100+
|[vue/require-mayberef-unwrap]| require`MaybeRef` values to be unwrapped with`unref()` before using in conditions|:bulb:|:three::warning:|
100101
|[vue/require-prop-type-constructor]| require prop type to be a constructor|:wrench:|:three::two::hammer:|
101102
|[vue/require-render-return]| enforce render function to always return value||:three::two::warning:|
102103
|[vue/require-slots-as-functions]| enforce properties of`$slots` to be used as a function||:three::warning:|
@@ -564,6 +565,7 @@ The following rules extend the rules provided by ESLint itself and apply them to
564565
[vue/require-explicit-slots]:./require-explicit-slots.md
565566
[vue/require-expose]:./require-expose.md
566567
[vue/require-macro-variable-name]:./require-macro-variable-name.md
568+
[vue/require-mayberef-unwrap]:./require-mayberef-unwrap.md
567569
[vue/require-name-property]:./require-name-property.md
568570
[vue/require-prop-comment]:./require-prop-comment.md
569571
[vue/require-prop-type-constructor]:./require-prop-type-constructor.md

‎docs/rules/require-mayberef-unwrap.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
---
2+
pageClass:rule-details
3+
sidebarDepth:0
4+
title:vue/require-mayberef-unwrap
5+
description:require `MaybeRef` values to be unwrapped with `unref()` before using in conditions
6+
---
7+
8+
#vue/require-mayberef-unwrap
9+
10+
>require`MaybeRef` values to be unwrapped with`unref()` before using in conditions
11+
12+
-:gear: This rule is included in all of`"plugin:vue/essential"`,`*.configs["flat/essential"]`,`"plugin:vue/strongly-recommended"`,`*.configs["flat/strongly-recommended"]`,`"plugin:vue/recommended"` and`*.configs["flat/recommended"]`.
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).
14+
15+
16+
##:book: Rule Details
17+
18+
`MaybeRef<T>` and`MaybeRefOrGetter<T>` are TypeScript utility types provided by Vue.
19+
They allow a value to be either a plain value**or** a`Ref<T>`. When such a variable is used in a boolean context you must first unwrap it with`unref()` so that the actual inner value is evaluated.
20+
This rule reports (and can auto-fix) places where a`MaybeRef*` value is used directly in a conditional expression, logical expression, unary operator, etc.
21+
22+
<eslint-code-blockfix:rules="{'vue/require-mayberef-unwrap': ['error']}">
23+
24+
```vue
25+
<script setup lang="ts">
26+
import { ref, unref, type MaybeRef } from 'vue'
27+
28+
const maybeRef: MaybeRef<boolean> = ref(false)
29+
30+
/* ✓ GOOD */
31+
if (unref(maybeRef)) {
32+
console.log('good')
33+
}
34+
const result = unref(maybeRef) ? 'true' : 'false'
35+
36+
/* ✗ BAD */
37+
if (maybeRef) {
38+
console.log('bad')
39+
}
40+
const alt = maybeRef ? 'true' : 'false'
41+
</script>
42+
```
43+
44+
</eslint-code-block>
45+
46+
###What is considered**incorrect** ?
47+
48+
The following patterns are**incorrect**:
49+
50+
```ts
51+
// Condition without unref
52+
if (maybeRef) {}
53+
54+
// Ternary operator
55+
const result=maybeRef?'a':'b'
56+
57+
// Logical expressions
58+
const value=maybeRef||'fallback'
59+
60+
// Unary operators
61+
const negated=!maybeRef
62+
63+
// Type queries & wrappers
64+
const t=typeofmaybeRef
65+
const b=Boolean(maybeRef)
66+
```
67+
68+
###What is considered**correct** ?
69+
70+
```ts
71+
if (unref(maybeRef)) {}
72+
const result=unref(maybeRef)?'a':'b'
73+
```
74+
75+
##:wrench: Options
76+
77+
Nothing.
78+
79+
##:books: Further Reading
80+
81+
-[Guide – Reactivity –`unref`](https://vuejs.org/guide/essentials/reactivity-fundamentals.html#unref)
82+
-[API –`MaybeRef`](https://vuejs.org/api/utility-types.html#mayberef)
83+
-[API –`MaybeRefOrGetter`](https://vuejs.org/api/utility-types.html#maybereforgetter)
84+
85+
##:rocket: Version
86+
87+
This rule will be introduced in a future release of eslint-plugin-vue.
88+
89+
##:mag: Implementation
90+
91+
-[Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/require-mayberef-unwrap.js)
92+
-[Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/require-mayberef-unwrap.js)

‎lib/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ const plugin = {
223223
'require-explicit-slots':require('./rules/require-explicit-slots'),
224224
'require-expose':require('./rules/require-expose'),
225225
'require-macro-variable-name':require('./rules/require-macro-variable-name'),
226+
'require-mayberef-unwrap':require('./rules/require-mayberef-unwrap'),
226227
'require-name-property':require('./rules/require-name-property'),
227228
'require-prop-comment':require('./rules/require-prop-comment'),
228229
'require-prop-type-constructor':require('./rules/require-prop-type-constructor'),

‎lib/rules/require-mayberef-unwrap.js

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/**
2+
*@author 2nofa11
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
const{ findVariable}=require('@eslint-community/eslint-utils')
8+
constutils=require('../utils')
9+
10+
module.exports={
11+
meta:{
12+
type:'problem',
13+
docs:{
14+
description:'require `MaybeRef` values to be unwrapped with `unref()` before using in conditions',
15+
categories:undefined,
16+
url:'https://eslint.vuejs.org/rules/require-mayberef-unwrap.html'
17+
},
18+
fixable:null,
19+
hasSuggestions:true,
20+
schema:[],
21+
messages:{
22+
requireUnref:'MaybeRef should be unwrapped with `unref()` before using in conditions. Use `unref({{name}})` instead.',
23+
wrapWithUnref:'Wrap with unref().'
24+
}
25+
},
26+
/**@param {RuleContext} context */
27+
create(context){
28+
/**
29+
* Determine if identifier should be considered MaybeRef
30+
*@param {Identifier} node
31+
*/
32+
functionisMaybeRef(node){
33+
constvariable=findVariable(utils.getScope(context,node),node)
34+
constid=variable?.defs[0]?.node?.id
35+
if(id?.type==='Identifier'&&id.typeAnnotation){
36+
returnisMaybeRefTypeNode(id.typeAnnotation.typeAnnotation)
37+
}
38+
returnfalse
39+
}
40+
41+
/**
42+
* Check TypeScript type node for MaybeRef/MaybeRefOrGetter
43+
*@param {import('@typescript-eslint/types').TSESTree.TypeNode | undefined} typeNode
44+
*@returns {boolean}
45+
*/
46+
functionisMaybeRefTypeNode(typeNode){
47+
if(!typeNode)returnfalse
48+
if(typeNode.type==='TSTypeReference'){
49+
if(typeNode.typeName&&typeNode.typeName.type==='Identifier'){
50+
return(
51+
typeNode.typeName.name==='MaybeRef'||
52+
typeNode.typeName.name==='MaybeRefOrGetter'
53+
)
54+
}
55+
}
56+
if(typeNode.type==='TSUnionType'){
57+
returntypeNode.types.some((t)=>isMaybeRefTypeNode(t))
58+
}
59+
returnfalse
60+
}
61+
62+
/**
63+
* Reports if the identifier is a MaybeRef type
64+
*@param {Identifier} node
65+
*/
66+
functionreportIfMaybeRef(node){
67+
if(!isMaybeRef(node))return
68+
69+
constsourceCode=context.getSourceCode()
70+
context.report({
71+
node,
72+
messageId:'requireUnref',
73+
data:{name:node.name},
74+
suggest:[
75+
{
76+
messageId:'wrapWithUnref',
77+
/**@param {*} fixer */
78+
fix(fixer){
79+
returnfixer.replaceText(node,`unref(${sourceCode.getText(node)})`)
80+
}
81+
}
82+
]
83+
})
84+
}
85+
86+
return{
87+
// if (maybeRef)
88+
/**@param {Identifier} node */
89+
'IfStatement>Identifier'(node){
90+
reportIfMaybeRef(node)
91+
},
92+
// maybeRef ? x : y
93+
/**@param {Identifier & {parent: ConditionalExpression}} node */
94+
'ConditionalExpression>Identifier'(node){
95+
if(node.parent.test===node){
96+
reportIfMaybeRef(node)
97+
}
98+
},
99+
// !maybeRef, +maybeRef, -maybeRef, ~maybeRef, typeof maybeRef
100+
/**@param {Identifier} node */
101+
'UnaryExpression>Identifier'(node){
102+
reportIfMaybeRef(node)
103+
},
104+
// maybeRef || other, maybeRef && other, maybeRef ?? other
105+
/**@param {Identifier & {parent: LogicalExpression}} node */
106+
'LogicalExpression>Identifier'(node){
107+
reportIfMaybeRef(node)
108+
},
109+
// maybeRef == x, maybeRef != x, maybeRef === x, maybeRef !== x
110+
/**@param {Identifier} node */
111+
'BinaryExpression>Identifier'(node){
112+
reportIfMaybeRef(node)
113+
},
114+
// Boolean(maybeRef), String(maybeRef)
115+
/**@param {Identifier} node */
116+
'CallExpression>Identifier'(node){
117+
if(node.parent&&
118+
node.parent.type==='CallExpression'&&
119+
node.parent.callee&&
120+
node.parent.callee.type==='Identifier'&&
121+
['Boolean','String'].includes(node.parent.callee.name)&&
122+
node.parent.arguments[0]===node){
123+
reportIfMaybeRef(node)
124+
}
125+
}
126+
}
127+
}
128+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp