Skip to content

Commit

Permalink
Add support for props destructure to vue/no-boolean-default rule (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
ota-meshi committed Sep 18, 2024
1 parent 4704ab6 commit 3d32c1b
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 26 deletions.
68 changes: 43 additions & 25 deletions lib/rules/no-boolean-default.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,27 @@ const utils = require('../utils')

/**
* @typedef {import('../utils').ComponentProp} ComponentProp
* @typedef {import('../utils').ComponentObjectProp} ComponentObjectProp
*/

/**
* @param {Property | SpreadElement} prop
* @param {Expression|undefined} node
*/
function isBooleanIdentifier(node) {
return Boolean(node && node.type === 'Identifier' && node.name === 'Boolean')
}

/**
* Detects whether given prop node is a Boolean
* @param {ComponentObjectProp} prop
* @return {Boolean}
*/
function isBooleanProp(prop) {
const value = utils.skipTSAsExpression(prop.value)
return (
prop.type === 'Property' &&
prop.key.type === 'Identifier' &&
prop.key.name === 'type' &&
prop.value.type === 'Identifier' &&
prop.value.name === 'Boolean'
isBooleanIdentifier(value) ||
(value.type === 'ObjectExpression' &&
isBooleanIdentifier(utils.findProperty(value, 'type')?.value))
)
}

Expand Down Expand Up @@ -55,40 +64,40 @@ module.exports = {
const booleanType = context.options[0] || 'no-default'
/**
* @param {ComponentProp} prop
* @param { { [key: string]: Expression | undefined } } [withDefaultsExpressions]
* @param {(propName: string) => Expression[]} otherDefaultProvider
*/
function processProp(prop, withDefaultsExpressions) {
function processProp(prop, otherDefaultProvider) {
if (prop.type === 'object') {
if (prop.value.type !== 'ObjectExpression') {
if (!isBooleanProp(prop)) {
return
}
if (!prop.value.properties.some(isBooleanProp)) {
return
if (prop.value.type === 'ObjectExpression') {
const defaultNode = getDefaultNode(prop.value)
if (defaultNode) {
verifyDefaultExpression(defaultNode.value)
}
}
const defaultNode = getDefaultNode(prop.value)
if (!defaultNode) {
return
if (prop.propName != null) {
for (const defaultNode of otherDefaultProvider(prop.propName)) {
verifyDefaultExpression(defaultNode)
}
}
verifyDefaultExpression(defaultNode.value)
} else if (prop.type === 'type') {
if (prop.types.length !== 1 || prop.types[0] !== 'Boolean') {
return
}
const defaultNode =
withDefaultsExpressions && withDefaultsExpressions[prop.propName]
if (!defaultNode) {
return
for (const defaultNode of otherDefaultProvider(prop.propName)) {
verifyDefaultExpression(defaultNode)
}
verifyDefaultExpression(defaultNode)
}
}
/**
* @param {ComponentProp[]} props
* @param { { [key: string]: Expression | undefined } } [withDefaultsExpressions]
* @param {(propName: string) => Expression[]} otherDefaultProvider
*/
function processProps(props, withDefaultsExpressions) {
function processProps(props, otherDefaultProvider) {
for (const prop of props) {
processProp(prop, withDefaultsExpressions)
processProp(prop, otherDefaultProvider)
}
}

Expand Down Expand Up @@ -118,11 +127,20 @@ module.exports = {
}
return utils.compositingVisitors(
utils.executeOnVueComponent(context, (obj) => {
processProps(utils.getComponentPropsFromOptions(obj))
processProps(utils.getComponentPropsFromOptions(obj), () => [])
}),
utils.defineScriptSetupVisitor(context, {
onDefinePropsEnter(node, props) {
processProps(props, utils.getWithDefaultsPropExpressions(node))
const defaultsByWithDefaults =
utils.getWithDefaultsPropExpressions(node)
const defaultsByAssignmentPatterns =
utils.getDefaultPropExpressionsForPropsDestructure(node)
processProps(props, (propName) =>
[
defaultsByWithDefaults[propName],
defaultsByAssignmentPatterns[propName]?.expression
].filter(utils.isDef)
)
}
})
)
Expand Down
50 changes: 49 additions & 1 deletion tests/lib/rules/no-boolean-default.js
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,18 @@ ruleTester.run('no-boolean-default', rule, {
parser: require.resolve('@typescript-eslint/parser')
}
}
},
{
filename: 'test.vue',
code: `
<script setup>
const {foo = false} = defineProps({foo: Boolean})
</script>
`,
options: ['default-false'],
languageOptions: {
parser: require('vue-eslint-parser')
}
}
],

Expand Down Expand Up @@ -512,6 +524,42 @@ ruleTester.run('no-boolean-default', rule, {
}
]
}
])
]),
{
filename: 'test.vue',
code: `
<script setup>
const {foo = false} = defineProps({foo: Boolean})
</script>
`,
languageOptions: {
parser: require('vue-eslint-parser')
},
errors: [
{
message:
'Boolean prop should not set a default (Vue defaults it to false).',
line: 3
}
]
},
{
filename: 'test.vue',
code: `
<script setup>
const {foo = true} = defineProps({foo: Boolean})
</script>
`,
options: ['default-false'],
languageOptions: {
parser: require('vue-eslint-parser')
},
errors: [
{
message: 'Boolean prop should only be defaulted to false.',
line: 3
}
]
}
]
})

0 comments on commit 3d32c1b

Please sign in to comment.