Skip to content

Commit

Permalink
Update vue/no-unused-properties rule to support expose (#1566)
Browse files Browse the repository at this point in the history
  • Loading branch information
ota-meshi committed Jul 17, 2021
1 parent 5bbc3fe commit 88d8d1b
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 42 deletions.
4 changes: 0 additions & 4 deletions lib/rules/no-invalid-model-keys.js
Expand Up @@ -6,10 +6,6 @@

const utils = require('../utils')

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

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
Expand Down
86 changes: 49 additions & 37 deletions lib/rules/no-unused-properties.js
Expand Up @@ -53,14 +53,20 @@ const GROUP_COMPUTED_PROPERTY = 'computed'
const GROUP_METHODS = 'methods'
const GROUP_SETUP = 'setup'
const GROUP_WATCHER = 'watch'
const GROUP_EXPOSE = 'expose'

const PROPERTY_LABEL = {
props: 'property',
data: 'data',
computed: 'computed property',
methods: 'method',
setup: 'property returned from `setup()`',
watch: 'watch'

// not use
watch: 'watch',
provide: 'provide',
inject: 'inject',
expose: 'expose'
}

// ------------------------------------------------------------------------------
Expand Down Expand Up @@ -900,51 +906,57 @@ module.exports = {
onVueObjectEnter(node) {
const container = getVueComponentPropertiesContainer(node)

for (const watcher of utils.iterateProperties(
for (const watcherOrExpose of utils.iterateProperties(
node,
new Set([GROUP_WATCHER])
new Set([GROUP_WATCHER, GROUP_EXPOSE])
)) {
// Process `watch: { foo /* <- this */ () {} }`
const segments = watcher.name.split('.')
container.usedProperties.addUsed(segments[0], (context) => {
return buildChainTracker(segments)(context)
/**
* @param {string[]} baseSegments
* @returns {UsedPropertiesTracker}
*/
function buildChainTracker(baseSegments) {
return () => {
const subSegments = baseSegments.slice(1)
const usedProps = new UsedProperties()
if (subSegments.length) {
usedProps.addUsed(
subSegments[0],
buildChainTracker(subSegments)
)
if (watcherOrExpose.groupName === GROUP_WATCHER) {
const watcher = watcherOrExpose
// Process `watch: { foo /* <- this */ () {} }`
const segments = watcher.name.split('.')
container.usedProperties.addUsed(segments[0], (context) => {
return buildChainTracker(segments)(context)
/**
* @param {string[]} baseSegments
* @returns {UsedPropertiesTracker}
*/
function buildChainTracker(baseSegments) {
return () => {
const subSegments = baseSegments.slice(1)
const usedProps = new UsedProperties()
if (subSegments.length) {
usedProps.addUsed(
subSegments[0],
buildChainTracker(subSegments)
)
}
return usedProps
}
return usedProps
}
}
})
})

// Process `watch: { x: 'foo' /* <- this */ }`
if (watcher.type === 'object') {
const property = watcher.property
if (property.kind === 'init') {
for (const handlerValueNode of utils.iterateWatchHandlerValues(
property
)) {
if (
handlerValueNode.type === 'Literal' ||
handlerValueNode.type === 'TemplateLiteral'
) {
const name = utils.getStringLiteralValue(handlerValueNode)
if (name != null) {
container.usedProperties.addUsed(name, null)
// Process `watch: { x: 'foo' /* <- this */ }`
if (watcher.type === 'object') {
const property = watcher.property
if (property.kind === 'init') {
for (const handlerValueNode of utils.iterateWatchHandlerValues(
property
)) {
if (
handlerValueNode.type === 'Literal' ||
handlerValueNode.type === 'TemplateLiteral'
) {
const name = utils.getStringLiteralValue(handlerValueNode)
if (name != null) {
container.usedProperties.addUsed(name, null)
}
}
}
}
}
} else if (watcherOrExpose.groupName === GROUP_EXPOSE) {
const expose = watcherOrExpose
container.usedProperties.addUsed(expose.name, null)
}
}
container.properties.push(...utils.iterateProperties(node, groups))
Expand Down
2 changes: 1 addition & 1 deletion lib/utils/index.js
Expand Up @@ -23,7 +23,7 @@
* @typedef { {key: string | null, value: BlockStatement | null} } ComponentComputedProperty
*/
/**
* @typedef { 'props' | 'data' | 'computed' | 'setup' | 'watch' | 'methods' } GroupName
* @typedef { 'props' | 'data' | 'computed' | 'setup' | 'watch' | 'methods' | 'provide' | 'inject' | 'expose' } GroupName
* @typedef { { type: 'array', name: string, groupName: GroupName, node: Literal | TemplateLiteral } } ComponentArrayPropertyData
* @typedef { { type: 'object', name: string, groupName: GroupName, node: Identifier | Literal | TemplateLiteral, property: Property } } ComponentObjectPropertyData
* @typedef { ComponentArrayPropertyData | ComponentObjectPropertyData } ComponentPropertyData
Expand Down
63 changes: 63 additions & 0 deletions tests/lib/rules/no-unused-properties.js
Expand Up @@ -1535,6 +1535,33 @@ tester.run('no-unused-properties', rule, {
ignorePublicMembers: true
}
]
},

// expose
{
filename: 'test.vue',
code: `
<template>
{{a}}
</template>
<script>
export default {
expose: ['a', 'b', 'c', 'd'],
props: ['a'],
data() {
return {
b: 42,
}
},
computed:{
c() {},
},
methods:{
d() {},
}
}
</script>`,
options: allOptions
}
],

Expand Down Expand Up @@ -2412,6 +2439,42 @@ tester.run('no-unused-properties', rule, {
props.b
</script>`,
errors: ["'c' of property found, but never used."]
},

// expose
{
filename: 'test.vue',
code: `
<template>
{{a}}
</template>
<script>
export default {
expose: ['a', 'c', 'e', 'g'],
props: ['a', 'b'],
data() {
return {
c: 42,
d: 42
}
},
computed:{
e() {},
f() {}
},
methods:{
g() {},
h() {}
}
}
</script>`,
options: allOptions,
errors: [
"'b' of property found, but never used.",
"'d' of data found, but never used.",
"'f' of computed property found, but never used.",
"'h' of method found, but never used."
]
}
]
})

0 comments on commit 88d8d1b

Please sign in to comment.