Skip to content

Commit 7099954

Browse files
authored
Add ignorePublicMembers option to vue/no-unused-properties rule (#1444)
1 parent 4ae9178 commit 7099954

File tree

4 files changed

+336
-4
lines changed

4 files changed

+336
-4
lines changed

docs/rules/no-unused-properties.md

+33-3
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,20 @@ This rule cannot be checked for use in other components (e.g. `mixins`, Property
5555
{
5656
"vue/no-unused-properties": ["error", {
5757
"groups": ["props"],
58-
"deepData": false
58+
"deepData": false,
59+
"ignorePublicMembers": false
5960
}]
6061
}
6162
```
6263

63-
- `"groups"` (`string[]`) Array of groups to search for properties. Default is `["props"]`. The value of the array is some of the following strings:
64+
- `groups` (`string[]`) Array of groups to search for properties. Default is `["props"]`. The value of the array is some of the following strings:
6465
- `"props"`
6566
- `"data"`
6667
- `"computed"`
6768
- `"methods"`
6869
- `"setup"`
69-
- `"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.
70+
- `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.
71+
- `ignorePublicMembers` (`boolean`) If `true`, members marked with a [JSDoc `/** @public */` tag](https://jsdoc.app/tags-public.html) will be ignored. Default is `false`.
7072

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

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

189191
</eslint-code-block>
190192

193+
### `{ "groups": ["props", "methods"], "ignorePublicMembers": true }`
194+
195+
<eslint-code-block :rules="{'vue/no-unused-properties': ['error', {groups: ['props', 'methods'], ignorePublicMembers: true}]}">
196+
197+
```vue
198+
<!-- ✓ GOOD -->
199+
<template>
200+
<button @click="usedInTemplate()" />
201+
</template>
202+
<script>
203+
export default {
204+
methods: {
205+
/* ✓ GOOD */
206+
usedInTemplate() {},
207+
208+
/* ✓ GOOD */
209+
/** @public */
210+
publicMethod() {},
211+
212+
/* ✗ BAD */
213+
unusedMethod() {}
214+
}
215+
}
216+
</script>
217+
```
218+
219+
</eslint-code-block>
220+
191221
## :rocket: Version
192222

193223
This rule was introduced in eslint-plugin-vue v7.0.0

lib/rules/no-unused-properties.js

+94-1
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,91 @@ function getObjectPatternPropertyPatternTracker(pattern) {
459459
return () => new UsedProperties({ unknown: true })
460460
}
461461

462+
/**
463+
* Check if the given component property is marked as `@public` in JSDoc comments.
464+
* @param {ComponentPropertyData} property
465+
* @param {SourceCode} sourceCode
466+
*/
467+
function isPublicMember(property, sourceCode) {
468+
if (
469+
property.type === 'object' &&
470+
// Props do not support @public.
471+
property.groupName !== 'props'
472+
) {
473+
return isPublicProperty(property.property, sourceCode)
474+
}
475+
return false
476+
}
477+
478+
/**
479+
* Check if the given property node is marked as `@public` in JSDoc comments.
480+
* @param {Property} node
481+
* @param {SourceCode} sourceCode
482+
*/
483+
function isPublicProperty(node, sourceCode) {
484+
const jsdoc = getJSDocFromProperty(node, sourceCode)
485+
if (jsdoc) {
486+
return /(?:^|\s|\*)@public\b/u.test(jsdoc.value)
487+
}
488+
return false
489+
}
490+
491+
/**
492+
* Get the JSDoc comment for a given property node.
493+
* @param {Property} node
494+
* @param {SourceCode} sourceCode
495+
*/
496+
function getJSDocFromProperty(node, sourceCode) {
497+
const jsdoc = findJSDocComment(node, sourceCode)
498+
if (jsdoc) {
499+
return jsdoc
500+
}
501+
if (
502+
node.value.type === 'FunctionExpression' ||
503+
node.value.type === 'ArrowFunctionExpression'
504+
) {
505+
return findJSDocComment(node.value, sourceCode)
506+
}
507+
508+
return null
509+
}
510+
511+
/**
512+
* Finds a JSDoc comment for the given node.
513+
* @param {ASTNode} node
514+
* @param {SourceCode} sourceCode
515+
* @returns {Comment | null}
516+
*/
517+
function findJSDocComment(node, sourceCode) {
518+
/** @type {ASTNode | Token} */
519+
let currentNode = node
520+
let tokenBefore = null
521+
522+
while (currentNode) {
523+
tokenBefore = sourceCode.getTokenBefore(currentNode, {
524+
includeComments: true
525+
})
526+
if (!tokenBefore || !eslintUtils.isCommentToken(tokenBefore)) {
527+
return null
528+
}
529+
if (tokenBefore.type === 'Line') {
530+
currentNode = tokenBefore
531+
continue
532+
}
533+
break
534+
}
535+
536+
if (
537+
tokenBefore &&
538+
tokenBefore.type === 'Block' &&
539+
tokenBefore.value.charAt(0) === '*'
540+
) {
541+
return tokenBefore
542+
}
543+
544+
return null
545+
}
546+
462547
// ------------------------------------------------------------------------------
463548
// Rule Definition
464549
// ------------------------------------------------------------------------------
@@ -490,7 +575,8 @@ module.exports = {
490575
additionalItems: false,
491576
uniqueItems: true
492577
},
493-
deepData: { type: 'boolean' }
578+
deepData: { type: 'boolean' },
579+
ignorePublicMembers: { type: 'boolean' }
494580
},
495581
additionalProperties: false
496582
}
@@ -504,6 +590,7 @@ module.exports = {
504590
const options = context.options[0] || {}
505591
const groups = new Set(options.groups || [GROUP_PROPERTY])
506592
const deepData = Boolean(options.deepData)
593+
const ignorePublicMembers = Boolean(options.ignorePublicMembers)
507594

508595
/** @type {Map<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression, ParamsUsedProperties>} */
509596
const paramsUsedPropertiesMap = new Map()
@@ -626,6 +713,12 @@ module.exports = {
626713
// used template refs
627714
continue
628715
}
716+
if (
717+
ignorePublicMembers &&
718+
isPublicMember(property, context.getSourceCode())
719+
) {
720+
continue
721+
}
629722
if (usedProperties.isUsed(property.name)) {
630723
// used
631724
if (

0 commit comments

Comments
 (0)