Skip to content

Commit 3d32c1b

Browse files
authored
Add support for props destructure to vue/no-boolean-default rule (#2553)
1 parent 4704ab6 commit 3d32c1b

File tree

2 files changed

+92
-26
lines changed

2 files changed

+92
-26
lines changed

Diff for: lib/rules/no-boolean-default.js

+43-25
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,27 @@ const utils = require('../utils')
88

99
/**
1010
* @typedef {import('../utils').ComponentProp} ComponentProp
11+
* @typedef {import('../utils').ComponentObjectProp} ComponentObjectProp
1112
*/
1213

1314
/**
14-
* @param {Property | SpreadElement} prop
15+
* @param {Expression|undefined} node
16+
*/
17+
function isBooleanIdentifier(node) {
18+
return Boolean(node && node.type === 'Identifier' && node.name === 'Boolean')
19+
}
20+
21+
/**
22+
* Detects whether given prop node is a Boolean
23+
* @param {ComponentObjectProp} prop
24+
* @return {Boolean}
1525
*/
1626
function isBooleanProp(prop) {
27+
const value = utils.skipTSAsExpression(prop.value)
1728
return (
18-
prop.type === 'Property' &&
19-
prop.key.type === 'Identifier' &&
20-
prop.key.name === 'type' &&
21-
prop.value.type === 'Identifier' &&
22-
prop.value.name === 'Boolean'
29+
isBooleanIdentifier(value) ||
30+
(value.type === 'ObjectExpression' &&
31+
isBooleanIdentifier(utils.findProperty(value, 'type')?.value))
2332
)
2433
}
2534

@@ -55,40 +64,40 @@ module.exports = {
5564
const booleanType = context.options[0] || 'no-default'
5665
/**
5766
* @param {ComponentProp} prop
58-
* @param { { [key: string]: Expression | undefined } } [withDefaultsExpressions]
67+
* @param {(propName: string) => Expression[]} otherDefaultProvider
5968
*/
60-
function processProp(prop, withDefaultsExpressions) {
69+
function processProp(prop, otherDefaultProvider) {
6170
if (prop.type === 'object') {
62-
if (prop.value.type !== 'ObjectExpression') {
71+
if (!isBooleanProp(prop)) {
6372
return
6473
}
65-
if (!prop.value.properties.some(isBooleanProp)) {
66-
return
74+
if (prop.value.type === 'ObjectExpression') {
75+
const defaultNode = getDefaultNode(prop.value)
76+
if (defaultNode) {
77+
verifyDefaultExpression(defaultNode.value)
78+
}
6779
}
68-
const defaultNode = getDefaultNode(prop.value)
69-
if (!defaultNode) {
70-
return
80+
if (prop.propName != null) {
81+
for (const defaultNode of otherDefaultProvider(prop.propName)) {
82+
verifyDefaultExpression(defaultNode)
83+
}
7184
}
72-
verifyDefaultExpression(defaultNode.value)
7385
} else if (prop.type === 'type') {
7486
if (prop.types.length !== 1 || prop.types[0] !== 'Boolean') {
7587
return
7688
}
77-
const defaultNode =
78-
withDefaultsExpressions && withDefaultsExpressions[prop.propName]
79-
if (!defaultNode) {
80-
return
89+
for (const defaultNode of otherDefaultProvider(prop.propName)) {
90+
verifyDefaultExpression(defaultNode)
8191
}
82-
verifyDefaultExpression(defaultNode)
8392
}
8493
}
8594
/**
8695
* @param {ComponentProp[]} props
87-
* @param { { [key: string]: Expression | undefined } } [withDefaultsExpressions]
96+
* @param {(propName: string) => Expression[]} otherDefaultProvider
8897
*/
89-
function processProps(props, withDefaultsExpressions) {
98+
function processProps(props, otherDefaultProvider) {
9099
for (const prop of props) {
91-
processProp(prop, withDefaultsExpressions)
100+
processProp(prop, otherDefaultProvider)
92101
}
93102
}
94103

@@ -118,11 +127,20 @@ module.exports = {
118127
}
119128
return utils.compositingVisitors(
120129
utils.executeOnVueComponent(context, (obj) => {
121-
processProps(utils.getComponentPropsFromOptions(obj))
130+
processProps(utils.getComponentPropsFromOptions(obj), () => [])
122131
}),
123132
utils.defineScriptSetupVisitor(context, {
124133
onDefinePropsEnter(node, props) {
125-
processProps(props, utils.getWithDefaultsPropExpressions(node))
134+
const defaultsByWithDefaults =
135+
utils.getWithDefaultsPropExpressions(node)
136+
const defaultsByAssignmentPatterns =
137+
utils.getDefaultPropExpressionsForPropsDestructure(node)
138+
processProps(props, (propName) =>
139+
[
140+
defaultsByWithDefaults[propName],
141+
defaultsByAssignmentPatterns[propName]?.expression
142+
].filter(utils.isDef)
143+
)
126144
}
127145
})
128146
)

Diff for: tests/lib/rules/no-boolean-default.js

+49-1
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,18 @@ ruleTester.run('no-boolean-default', rule, {
326326
parser: require.resolve('@typescript-eslint/parser')
327327
}
328328
}
329+
},
330+
{
331+
filename: 'test.vue',
332+
code: `
333+
<script setup>
334+
const {foo = false} = defineProps({foo: Boolean})
335+
</script>
336+
`,
337+
options: ['default-false'],
338+
languageOptions: {
339+
parser: require('vue-eslint-parser')
340+
}
329341
}
330342
],
331343

@@ -512,6 +524,42 @@ ruleTester.run('no-boolean-default', rule, {
512524
}
513525
]
514526
}
515-
])
527+
]),
528+
{
529+
filename: 'test.vue',
530+
code: `
531+
<script setup>
532+
const {foo = false} = defineProps({foo: Boolean})
533+
</script>
534+
`,
535+
languageOptions: {
536+
parser: require('vue-eslint-parser')
537+
},
538+
errors: [
539+
{
540+
message:
541+
'Boolean prop should not set a default (Vue defaults it to false).',
542+
line: 3
543+
}
544+
]
545+
},
546+
{
547+
filename: 'test.vue',
548+
code: `
549+
<script setup>
550+
const {foo = true} = defineProps({foo: Boolean})
551+
</script>
552+
`,
553+
options: ['default-false'],
554+
languageOptions: {
555+
parser: require('vue-eslint-parser')
556+
},
557+
errors: [
558+
{
559+
message: 'Boolean prop should only be defaulted to false.',
560+
line: 3
561+
}
562+
]
563+
}
516564
]
517565
})

0 commit comments

Comments
 (0)