diff --git a/docs/rules/no-array-prototype-extensions.md b/docs/rules/no-array-prototype-extensions.md index ad681e3d01..7793652cdf 100644 --- a/docs/rules/no-array-prototype-extensions.md +++ b/docs/rules/no-array-prototype-extensions.md @@ -27,6 +27,7 @@ To reduce false positives, the rule ignores some common known-non-array classes/ - `localStorage.clear()` / `sessionStorage.clear()` - `Promise.any()` / `Promise.reject()` - Lodash / jQuery +- Ember Data `this.store` service - etc If you run into additional false positives, please file a bug or submit a PR to add it to the rule's hardcoded ignore list. diff --git a/lib/rules/no-array-prototype-extensions.js b/lib/rules/no-array-prototype-extensions.js index 1b2cf6cd73..b1178e20c6 100644 --- a/lib/rules/no-array-prototype-extensions.js +++ b/lib/rules/no-array-prototype-extensions.js @@ -598,6 +598,22 @@ function applyFix(callExpressionNode, fixer, context, options = {}) { } } +/** + * Check for a call on `this.store` which we can assume is the Ember Data store service. + * We don't check for an initialization as the service could be implicitly injected: https://github.com/ember-cli/eslint-plugin-ember/blob/master/docs/rules/no-implicit-injections.md + */ +function isThisStoreCall(node) { + return ( + node.type === 'CallExpression' && + node.callee.type === 'MemberExpression' && + node.callee.object.type === 'MemberExpression' && + node.callee.object.object.type === 'ThisExpression' && + node.callee.object.property.type === 'Identifier' && + node.callee.object.property.name === 'store' && + node.callee.property.type === 'Identifier' // Any function call on the store service. + ); +} + //---------------------------------------------------------------------------------------------- // General rule - Don't use Ember's array prototype extensions like .any(), .pushObject() or .firstObject //---------------------------------------------------------------------------------------------- @@ -698,6 +714,15 @@ module.exports = { return; } + if ( + (nodeInitializedTo.type === 'AwaitExpression' && + isThisStoreCall(nodeInitializedTo.argument)) || + isThisStoreCall(nodeInitializedTo) + ) { + // Found call on the Ember Data this.store class. + return; + } + if ( node.callee.type === 'MemberExpression' && node.callee.object.type === 'MemberExpression' && diff --git a/tests/lib/rules/no-array-prototype-extensions.js b/tests/lib/rules/no-array-prototype-extensions.js index 4b748bf6c1..1d3a560dd5 100644 --- a/tests/lib/rules/no-array-prototype-extensions.js +++ b/tests/lib/rules/no-array-prototype-extensions.js @@ -226,6 +226,25 @@ ruleTester.run('no-array-prototype-extensions', rule, { array.without(2) `, + // Ember Data call with await. + ` + class MyClass { + async _fetch(query) { + const response = await this.store.query('foo-bar', query); + return response.toArray(); + } + } + `, + // Ember Data call without await. + ` + class MyClass { + _fetch(query) { + const response = this.store.peekAll('foo-bar', query); + return response.toArray(); + } + } + `, + // TODO: handle non-Identifier property names: 'foo["clear"]();', ],