From 97011ec16c97bed64d7566038c803f53f4f3bc3a Mon Sep 17 00:00:00 2001 From: Bryan Mishkin <698306+bmish@users.noreply.github.com> Date: Fri, 7 Aug 2020 13:16:36 -0400 Subject: [PATCH] fix: check imported get/getProperties/getWithDefault functions for missing dependencies in require-computed-property-dependencies rule --- .../require-computed-property-dependencies.js | 52 ++++++++++++++----- .../require-computed-property-dependencies.js | 39 ++++++++++++++ 2 files changed, 79 insertions(+), 12 deletions(-) diff --git a/lib/rules/require-computed-property-dependencies.js b/lib/rules/require-computed-property-dependencies.js index ef3218a4c4..e557240a9c 100644 --- a/lib/rules/require-computed-property-dependencies.js +++ b/lib/rules/require-computed-property-dependencies.js @@ -117,16 +117,16 @@ function parseComputedDependencies(args) { * or `this.get(…)`. * * @param {ASTNode} node - * @param {string} importedEmberName + * @param {object} importedNames * @returns {Array} */ -function findEmberGetCalls(node, importedEmberName) { +function findEmberGetCalls(node, importedNames) { const results = []; new Traverser().traverse(node, { enter(child) { if (types.isCallExpression(child)) { - const dependency = extractEmberGetDependencies(child, importedEmberName); + const dependency = extractEmberGetDependencies(child, importedNames); if (dependency.length > 0) { results.push(child); @@ -211,10 +211,13 @@ function getArrayOrRest(args) { * Extracts all static property keys used in the various forms of `Ember.get`. * * @param {ASTNode} call - * @param {string} importedEmberName + * @param {object} importedNames * @returns {Array} */ -function extractEmberGetDependencies(call, importedEmberName) { +function extractEmberGetDependencies( + call, + { importedEmberName, importedGetName, importedGetPropertiesName, importedGetWithDefaultName } +) { if ( isMemberExpression(call.callee, 'this', 'get') || isMemberExpression(call.callee, 'this', 'getWithDefault') @@ -226,7 +229,9 @@ function extractEmberGetDependencies(call, importedEmberName) { } } else if ( isMemberExpression(call.callee, importedEmberName, 'get') || - isMemberExpression(call.callee, importedEmberName, 'getWithDefault') + isMemberExpression(call.callee, importedEmberName, 'getWithDefault') || + isIdentifier(call.callee, importedGetName) || + isIdentifier(call.callee, importedGetWithDefaultName) ) { const firstArg = call.arguments[0]; const secondArgument = call.arguments[1]; @@ -238,7 +243,10 @@ function extractEmberGetDependencies(call, importedEmberName) { return getArrayOrRest(call.arguments) .filter(types.isStringLiteral) .map((arg) => arg.value); - } else if (isMemberExpression(call.callee, importedEmberName, 'getProperties')) { + } else if ( + isMemberExpression(call.callee, importedEmberName, 'getProperties') || + isIdentifier(call.callee, importedGetPropertiesName) + ) { const firstArg = call.arguments[0]; const rest = call.arguments.slice(1); @@ -313,8 +321,11 @@ module.exports = { let importedEmberName; let importedComputedName; + let importedGetName; + let importedGetPropertiesName; + let importedGetWithDefaultName; - function checkComputedDependencies(node, nodeArguments, importedEmberName) { + function checkComputedDependencies(node, nodeArguments, importedNames) { const declaredDependencies = parseComputedDependencies(nodeArguments); if (!allowDynamicKeys) { @@ -331,8 +342,8 @@ module.exports = { ); const usedKeys1 = javascriptUtils.flatMap( - findEmberGetCalls(computedPropertyFunctionBody, importedEmberName), - (node) => extractEmberGetDependencies(node, importedEmberName) + findEmberGetCalls(computedPropertyFunctionBody, importedNames), + (node) => extractEmberGetDependencies(node, importedNames) ); const usedKeys2 = javascriptUtils.flatMap( findThisGetCalls(computedPropertyFunctionBody), @@ -459,18 +470,35 @@ module.exports = { if (node.source.value === '@ember/object') { importedComputedName = importedComputedName || getImportIdentifier(node, '@ember/object', 'computed'); + importedGetName = importedGetName || getImportIdentifier(node, '@ember/object', 'get'); + importedGetPropertiesName = + importedGetPropertiesName || + getImportIdentifier(node, '@ember/object', 'getProperties'); + importedGetWithDefaultName = + importedGetWithDefaultName || + getImportIdentifier(node, '@ember/object', 'getWithDefault'); } }, Identifier(node) { if (isEmberComputed(node, importedEmberName, importedComputedName)) { - checkComputedDependencies(node, [], importedEmberName); + checkComputedDependencies(node, [], { + importedEmberName, + importedGetName, + importedGetPropertiesName, + importedGetWithDefaultName, + }); } }, CallExpression(node) { if (isEmberComputed(node.callee, importedEmberName, importedComputedName)) { - checkComputedDependencies(node, node.arguments, importedEmberName); + checkComputedDependencies(node, node.arguments, { + importedEmberName, + importedGetName, + importedGetPropertiesName, + importedGetWithDefaultName, + }); } }, }; diff --git a/tests/lib/rules/require-computed-property-dependencies.js b/tests/lib/rules/require-computed-property-dependencies.js index 076ecd82e9..f5f6aa071d 100644 --- a/tests/lib/rules/require-computed-property-dependencies.js +++ b/tests/lib/rules/require-computed-property-dependencies.js @@ -1043,5 +1043,44 @@ ruleTester.run('require-computed-property-dependencies', rule, { }, ], }, + { + // Renamed get import: + code: + "import { computed, get as g } from '@ember/object'; computed(function() { return g(this, 'foo'); });", + output: + "import { computed, get as g } from '@ember/object'; computed('foo', function() { return g(this, 'foo'); });", + errors: [ + { + message: 'Use of undeclared dependencies in computed property: foo', + type: 'CallExpression', + }, + ], + }, + { + // Renamed getProperties import: + code: + "import { computed, getProperties as gp } from '@ember/object'; computed(function() { return gp(this, 'foo'); });", + output: + "import { computed, getProperties as gp } from '@ember/object'; computed('foo', function() { return gp(this, 'foo'); });", + errors: [ + { + message: 'Use of undeclared dependencies in computed property: foo', + type: 'CallExpression', + }, + ], + }, + { + // Renamed getWithDefault import: + code: + "import { computed, getWithDefault as gwd } from '@ember/object'; computed(function() { return gwd(this, 'foo', 'bar'); });", + output: + "import { computed, getWithDefault as gwd } from '@ember/object'; computed('foo', function() { return gwd(this, 'foo', 'bar'); });", + errors: [ + { + message: 'Use of undeclared dependencies in computed property: foo', + type: 'CallExpression', + }, + ], + }, ], });