Skip to content

Commit 80f86cf

Browse files
committed
feat: add optional-props-using-with-defaults
1 parent f358817 commit 80f86cf

File tree

3 files changed

+810
-0
lines changed

3 files changed

+810
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/optional-props-using-with-defaults
5+
description: 'enforce props with default values ​​to be optional',
6+
---
7+
# vue/optional-props-using-with-defaults
8+
9+
> enforce props with default values ​​to be optional
10+
11+
## :book: Rule Details
12+
13+
This rule enforce props with default values ​​to be optional.
14+
Because when a required prop declared with a default value, but it doesn't be passed value when using it, it will be assigned the default value. So a required prop with default value is same as a optional prop.
15+
16+
<eslint-code-block :rules="{'vue/optional-props-using-with-defaults': ['error']}">
17+
18+
```vue
19+
<script setup lang="ts">
20+
/* ✗ GOOD */
21+
const props = withDefaults(
22+
defineProps<{
23+
name?: string | number
24+
age?: number
25+
}>(),
26+
{
27+
name: "Foo",
28+
}
29+
);
30+
31+
/* ✗ BAD */
32+
const props = withDefaults(
33+
defineProps<{
34+
name: string | number
35+
age?: number
36+
}>(),
37+
{
38+
name: "Foo",
39+
}
40+
);
41+
</script>
42+
```
43+
44+
</eslint-code-block>
45+
46+
## :wrench: Options
47+
48+
Nothing.
49+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**
2+
* @author @neferqiqi
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
// ------------------------------------------------------------------------------
7+
// Requirements
8+
// ------------------------------------------------------------------------------
9+
10+
const utils = require('../utils')
11+
/**
12+
* @typedef {import('../utils').ComponentTypeProp} ComponentTypeProp
13+
*/
14+
15+
// ------------------------------------------------------------------------------
16+
// Helpers
17+
// ------------------------------------------------------------------------------
18+
19+
// ...
20+
21+
// ------------------------------------------------------------------------------
22+
// Rule Definition
23+
// ------------------------------------------------------------------------------
24+
25+
module.exports = {
26+
meta: {
27+
type: 'suggestion',
28+
docs: {
29+
description: 'enforce props with default values ​​to be optional',
30+
categories: undefined,
31+
url: 'https://eslint.vuejs.org/rules/optional-props-using-with-defaults.html'
32+
},
33+
fixable: 'code',
34+
schema: [],
35+
messages: {
36+
// ...
37+
}
38+
},
39+
/** @param {RuleContext} context */
40+
create(context) {
41+
/**
42+
* @param {ComponentTypeProp} prop
43+
* @param {Token[]} tokens
44+
* */
45+
const findKeyToken = (prop, tokens) =>
46+
tokens.find((token) => {
47+
const isKeyIdentifierEqual =
48+
prop.key.type === 'Identifier' && token.value === prop.key.name
49+
const isKeyLiteralEqual =
50+
prop.key.type === 'Literal' && token.value === prop.key.raw
51+
return isKeyIdentifierEqual || isKeyLiteralEqual
52+
})
53+
54+
return utils.defineScriptSetupVisitor(context, {
55+
onDefinePropsEnter(node, props) {
56+
if (!utils.hasWithDefaults(node)) {
57+
return
58+
}
59+
const withDefaultsProps = Object.keys(
60+
utils.getWithDefaultsPropExpressions(node)
61+
)
62+
const requiredProps = props.flatMap((item) =>
63+
item.type === 'type' && item.required ? [item] : []
64+
)
65+
66+
for (const prop of requiredProps) {
67+
if (withDefaultsProps.includes(prop.propName)) {
68+
const firstToken = context.getSourceCode().getFirstToken(prop.node)
69+
// skip setter & getter case
70+
if (firstToken.value === 'get' || firstToken.value === 'set') {
71+
return
72+
}
73+
// skip computed
74+
if (prop.node.computed) {
75+
return
76+
}
77+
const keyToken = findKeyToken(
78+
prop,
79+
context.getSourceCode().getTokens(prop.node)
80+
)
81+
if (!keyToken) return
82+
context.report({
83+
node: prop.node,
84+
loc: prop.node.loc,
85+
data: {
86+
key: prop.propName
87+
},
88+
message: `Prop "{{ key }}" should be optional.`,
89+
fix: (fixer) => fixer.insertTextAfter(keyToken, '?')
90+
})
91+
}
92+
}
93+
}
94+
})
95+
}
96+
}

0 commit comments

Comments
 (0)