From 461918d188c4742d6b0a3920bea3a4abeb8efd45 Mon Sep 17 00:00:00 2001 From: Marc Farias Jones Date: Sun, 21 Jul 2024 16:00:44 -0700 Subject: [PATCH 1/5] Vuex and Pinia support for no-undef-properties --- lib/rules/no-undef-properties.js | 57 +++++- tests/lib/rules/no-undef-properties.js | 251 +++++++++++++++++++++++++ 2 files changed, 307 insertions(+), 1 deletion(-) diff --git a/lib/rules/no-undef-properties.js b/lib/rules/no-undef-properties.js index 29fbb714b..d9183fee2 100644 --- a/lib/rules/no-undef-properties.js +++ b/lib/rules/no-undef-properties.js @@ -111,6 +111,11 @@ module.exports = { ).map(toRegExp) const propertyReferenceExtractor = definePropertyReferenceExtractor(context) const programNode = context.getSourceCode().ast + /** + * Property names identified as defined via a Vuex or Pinia helpers + * @type {string[]} + */ + const propertiesDefinedByVuexOrPiniaHelpers = [] /** * @param {ASTNode} node @@ -185,7 +190,8 @@ module.exports = { report(node, name, messageId = 'undef') { if ( reserved.includes(name) || - ignores.some((ignore) => ignore.test(name)) + ignores.some((ignore) => ignore.test(name)) || + propertiesDefinedByVuexOrPiniaHelpers.includes(name) ) { return } @@ -331,6 +337,55 @@ module.exports = { } }), utils.defineVueVisitor(context, { + /** + * @param {CallExpression} node + */ + CallExpression(node) { + if (node.callee.type !== 'Identifier') return + /** @type {'methods'|'computed'|null} */ + let groupName = null + if (/^mapMutations|mapActions$/u.test(node.callee.name)) { + groupName = GROUP_METHODS + } else if ( + /^mapState|mapGetters|mapWritableState$/u.test(node.callee.name) + ) { + groupName = GROUP_COMPUTED_PROPERTY + } + + if (!groupName || node.arguments.length === 0) return + // On Pinia the store is always the first argument + const arg = + node.arguments.length === 2 ? node.arguments[1] : node.arguments[0] + if (arg.type === 'ObjectExpression') { + // e.g. + // `mapMutations({ add: 'increment' })` + // `mapState({ count: state => state.todosCount })` + for (const prop of arg.properties) { + const name = + prop.type === 'SpreadElement' + ? null + : utils.getStaticPropertyName(prop) + if (name) { + propertiesDefinedByVuexOrPiniaHelpers.push(name) + } + } + } else if (arg.type === 'ArrayExpression') { + // e.g. `mapMutations(['add'])` + for (const element of arg.elements) { + if ( + !element || + (element.type !== 'Literal' && + element.type !== 'TemplateLiteral') + ) { + continue + } + const name = utils.getStringLiteralValue(element) + if (name) { + propertiesDefinedByVuexOrPiniaHelpers.push(name) + } + } + } + }, onVueObjectEnter(node) { const ctx = getVueComponentContext(node) diff --git a/tests/lib/rules/no-undef-properties.js b/tests/lib/rules/no-undef-properties.js index 83812a248..d234bb448 100644 --- a/tests/lib/rules/no-undef-properties.js +++ b/tests/lib/rules/no-undef-properties.js @@ -561,7 +561,258 @@ tester.run('no-undef-properties', rule, { } } }, + { + // Vuex + filename: 'test.vue', + code: ` + + + ` + }, + { + filename: 'test.vue', + code: ` + + + ` + }, + { + filename: 'test.vue', + code: ` + + + ` + }, + { + filename: 'test.vue', + code: ` + + + ` + }, + { + filename: 'test.vue', + code: ` + + + ` + }, + { + filename: 'test.vue', + code: ` + + + ` + }, + { + // Pinia + filename: 'test.vue', + code: ` + + + ` + }, + { + filename: 'test.vue', + code: ` + + + ` + }, + { + filename: 'test.vue', + code: ` + + + ` + }, + { + filename: 'test.vue', + code: ` + + + ` + }, + { + filename: 'test.vue', + code: ` + + + ` + }, ` + + `, + errors: [ + { + message: "'g' is not defined.", + line: 13 + } + ] + }, + { + filename: 'test.vue', + code: ` + + + `, + errors: [ + { + message: "'f' is not defined.", + line: 12 + } + ] + }, + { + filename: 'test.vue', + code: ` + + + `, + errors: [ + { + message: "'x' is not defined.", + line: 13 + } + ] + }, + { + filename: 'test.vue', + code: ` + + + `, + errors: [ + { + message: "'f' is not defined.", + line: 10 + } + ] + }, + { + filename: 'test.vue', + code: ` + + + `, + errors: [ + { + message: "'q' is not defined.", + line: 14 + } + ] + }, + { + filename: 'test.vue', + code: ` + + + `, + errors: [ + { + message: "'z' is not defined.", + line: 12 + } + ] + }, + { + // Pinia + filename: 'test.vue', + code: ` + + + `, + errors: [ + { + message: "'z' is not defined.", + line: 13 + } + ] + }, + { + filename: 'test.vue', + code: ` + + + `, + errors: [ + { + message: "'q' is not defined.", + line: 20 + } + ] + }, + { + filename: 'test.vue', + code: ` + + + `, + errors: [ + { + message: "'z' is not defined.", + line: 16 + } + ] + }, + { + filename: 'test.vue', + code: ` + + + `, + errors: [ + { + message: "'z' is not defined.", + line: 13 + } + ] + }, + { + filename: 'test.vue', + code: ` + + + `, + errors: [ + { + message: "'x' is not defined.", + line: 11 + } + ] } ] }) From 8aef8aa6fc59e5505e837b92ac7f9ecbc5bbaaed Mon Sep 17 00:00:00 2001 From: Marc Farias Jones Date: Thu, 5 Sep 2024 07:26:06 -0700 Subject: [PATCH 5/5] fix capitalization of vuex imports --- tests/lib/rules/no-undef-properties.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/lib/rules/no-undef-properties.js b/tests/lib/rules/no-undef-properties.js index 108ed0554..63313b4b0 100644 --- a/tests/lib/rules/no-undef-properties.js +++ b/tests/lib/rules/no-undef-properties.js @@ -566,7 +566,7 @@ tester.run('no-undef-properties', rule, { filename: 'test.vue', code: `