From 32ba2a6967d6955445c52ec78a9c190e9497f01a Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Thu, 15 Jul 2021 13:41:38 +0900 Subject: [PATCH] Update `vue/no-unused-properties` rule to support `expose` --- lib/rules/no-invalid-model-keys.js | 4 -- lib/rules/no-unused-properties.js | 86 ++++++++++++++----------- lib/utils/index.js | 2 +- tests/lib/rules/no-unused-properties.js | 63 ++++++++++++++++++ 4 files changed, 113 insertions(+), 42 deletions(-) diff --git a/lib/rules/no-invalid-model-keys.js b/lib/rules/no-invalid-model-keys.js index 7985fd25d..f86587ff1 100644 --- a/lib/rules/no-invalid-model-keys.js +++ b/lib/rules/no-invalid-model-keys.js @@ -6,10 +6,6 @@ const utils = require('../utils') -/** - * @typedef {import('../utils').GroupName} GroupName - */ - // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ diff --git a/lib/rules/no-unused-properties.js b/lib/rules/no-unused-properties.js index 9a38c2a75..0e5b0b3e7 100644 --- a/lib/rules/no-unused-properties.js +++ b/lib/rules/no-unused-properties.js @@ -53,6 +53,7 @@ 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', @@ -60,7 +61,12 @@ const PROPERTY_LABEL = { computed: 'computed property', methods: 'method', setup: 'property returned from `setup()`', - watch: 'watch' + + // not use + watch: 'watch', + provide: 'provide', + inject: 'inject', + expose: 'expose' } // ------------------------------------------------------------------------------ @@ -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)) diff --git a/lib/utils/index.js b/lib/utils/index.js index b697a0d6b..7ad21ec4c 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -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 diff --git a/tests/lib/rules/no-unused-properties.js b/tests/lib/rules/no-unused-properties.js index f9d61e4b0..4af8091af 100644 --- a/tests/lib/rules/no-unused-properties.js +++ b/tests/lib/rules/no-unused-properties.js @@ -1535,6 +1535,33 @@ tester.run('no-unused-properties', rule, { ignorePublicMembers: true } ] + }, + + // expose + { + filename: 'test.vue', + code: ` + + `, + options: allOptions } ], @@ -2412,6 +2439,42 @@ tester.run('no-unused-properties', rule, { props.b `, errors: ["'c' of property found, but never used."] + }, + + // expose + { + filename: 'test.vue', + code: ` + + `, + 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." + ] } ] })