Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ignorePublicMembers option to vue/no-unused-properties rule #1444

Merged
merged 1 commit into from Mar 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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