Skip to content

Commit

Permalink
Add ignorePublicMembers option to vue/no-unused-properties rule (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
ota-meshi committed Mar 2, 2021
1 parent 4ae9178 commit 7099954
Show file tree
Hide file tree
Showing 4 changed files with 336 additions and 4 deletions.
36 changes: 33 additions & 3 deletions docs/rules/no-unused-properties.md
Expand Up @@ -55,18 +55,20 @@ This rule cannot be checked for use in other components (e.g. `mixins`, Property
{
"vue/no-unused-properties": ["error", {
"groups": ["props"],
"deepData": false
"deepData": false,
"ignorePublicMembers": false
}]
}
```

- `"groups"` (`string[]`) Array of groups to search for properties. Default is `["props"]`. The value of the array is some of the following strings:
- `groups` (`string[]`) Array of groups to search for properties. Default is `["props"]`. The value of the array is some of the following strings:
- `"props"`
- `"data"`
- `"computed"`
- `"methods"`
- `"setup"`
- `"deepData"` (`boolean`) If `true`, the object of the property defined in `data` will be searched deeply. Default is `false`. Include `"data"` in `groups` to use this option.
- `deepData` (`boolean`) If `true`, the object of the property defined in `data` will be searched deeply. Default is `false`. Include `"data"` in `groups` to use this option.
- `ignorePublicMembers` (`boolean`) If `true`, members marked with a [JSDoc `/** @public */` tag](https://jsdoc.app/tags-public.html) will be ignored. Default is `false`.

### `"groups": ["props", "data"]`

Expand Down Expand Up @@ -188,6 +190,34 @@ This rule cannot be checked for use in other components (e.g. `mixins`, Property

</eslint-code-block>

### `{ "groups": ["props", "methods"], "ignorePublicMembers": true }`

<eslint-code-block :rules="{'vue/no-unused-properties': ['error', {groups: ['props', 'methods'], ignorePublicMembers: true}]}">

```vue
<!-- ✓ GOOD -->
<template>
<button @click="usedInTemplate()" />
</template>
<script>
export default {
methods: {
/* ✓ GOOD */
usedInTemplate() {},
/* ✓ GOOD */
/** @public */
publicMethod() {},
/* ✗ BAD */
unusedMethod() {}
}
}
</script>
```

</eslint-code-block>

## :rocket: Version

This rule was introduced in eslint-plugin-vue v7.0.0
Expand Down
95 changes: 94 additions & 1 deletion lib/rules/no-unused-properties.js
Expand Up @@ -459,6 +459,91 @@ function getObjectPatternPropertyPatternTracker(pattern) {
return () => new UsedProperties({ unknown: true })
}

/**
* Check if the given component property is marked as `@public` in JSDoc comments.
* @param {ComponentPropertyData} property
* @param {SourceCode} sourceCode
*/
function isPublicMember(property, sourceCode) {
if (
property.type === 'object' &&
// Props do not support @public.
property.groupName !== 'props'
) {
return isPublicProperty(property.property, sourceCode)
}
return false
}

/**
* Check if the given property node is marked as `@public` in JSDoc comments.
* @param {Property} node
* @param {SourceCode} sourceCode
*/
function isPublicProperty(node, sourceCode) {
const jsdoc = getJSDocFromProperty(node, sourceCode)
if (jsdoc) {
return /(?:^|\s|\*)@public\b/u.test(jsdoc.value)
}
return false
}

/**
* Get the JSDoc comment for a given property node.
* @param {Property} node
* @param {SourceCode} sourceCode
*/
function getJSDocFromProperty(node, sourceCode) {
const jsdoc = findJSDocComment(node, sourceCode)
if (jsdoc) {
return jsdoc
}
if (
node.value.type === 'FunctionExpression' ||
node.value.type === 'ArrowFunctionExpression'
) {
return findJSDocComment(node.value, sourceCode)
}

return null
}

/**
* Finds a JSDoc comment for the given node.
* @param {ASTNode} node
* @param {SourceCode} sourceCode
* @returns {Comment | null}
*/
function findJSDocComment(node, sourceCode) {
/** @type {ASTNode | Token} */
let currentNode = node
let tokenBefore = null

while (currentNode) {
tokenBefore = sourceCode.getTokenBefore(currentNode, {
includeComments: true
})
if (!tokenBefore || !eslintUtils.isCommentToken(tokenBefore)) {
return null
}
if (tokenBefore.type === 'Line') {
currentNode = tokenBefore
continue
}
break
}

if (
tokenBefore &&
tokenBefore.type === 'Block' &&
tokenBefore.value.charAt(0) === '*'
) {
return tokenBefore
}

return null
}

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
Expand Down Expand Up @@ -490,7 +575,8 @@ module.exports = {
additionalItems: false,
uniqueItems: true
},
deepData: { type: 'boolean' }
deepData: { type: 'boolean' },
ignorePublicMembers: { type: 'boolean' }
},
additionalProperties: false
}
Expand All @@ -504,6 +590,7 @@ module.exports = {
const options = context.options[0] || {}
const groups = new Set(options.groups || [GROUP_PROPERTY])
const deepData = Boolean(options.deepData)
const ignorePublicMembers = Boolean(options.ignorePublicMembers)

/** @type {Map<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression, ParamsUsedProperties>} */
const paramsUsedPropertiesMap = new Map()
Expand Down Expand Up @@ -626,6 +713,12 @@ module.exports = {
// used template refs
continue
}
if (
ignorePublicMembers &&
isPublicMember(property, context.getSourceCode())
) {
continue
}
if (usedProperties.isUsed(property.name)) {
// used
if (
Expand Down

0 comments on commit 7099954

Please sign in to comment.