Repository: ember-cli/eslint-plugin-ember (js)

Issue:
false positive in `ember/no-attrs-in-components`
Using the `attrs` property of the [JSONSerializer](https://api.emberjs.com/ember-data/3.19/classes/JSONSerializer/properties/attrs?anchor=attrs) results in a false positive in the `ember/no-attrs-in-components` rule.

Is there a way to scope this rule to just components?

Pull request:
Ensure `no-attrs-in-components` rule only runs inside components
Fixes #890.

CC: @mongoose700 @miguelcobain

Patch:
diff --git a/lib/rules/no-attrs-in-components.js b/lib/rules/no-attrs-in-components.js
index 0f3c2974..f6be9a56 100644
--- a/lib/rules/no-attrs-in-components.js
+++ b/lib/rules/no-attrs-in-components.js
@@ -1,6 +1,7 @@
 'use strict';
 
 const types = require('../utils/types');
+const { isEmberComponent, isGlimmerComponent } = require('../utils/ember');
 
 const ERROR_MESSAGE = 'Do not use `this.attrs`';
 
@@ -25,9 +26,35 @@ module.exports = {
   ERROR_MESSAGE,
 
   create(context) {
+    let currentEmberComponent = null;
+
     return {
+      ClassDeclaration(node) {
+        if (isEmberComponent(context, node) || isGlimmerComponent(context, node)) {
+          currentEmberComponent = node;
+        }
+      },
+
+      CallExpression(node) {
+        if (isEmberComponent(context, node)) {
+          currentEmberComponent = node;
+        }
+      },
+
+      'ClassDeclaration:exit'(node) {
+        if (currentEmberComponent === node) {
+          currentEmberComponent = null;
+        }
+      },
+
+      'CallExpression:exit'(node) {
+        if (currentEmberComponent === node) {
+          currentEmberComponent = null;
+        }
+      },
+
       MemberExpression(node) {
-        if (isThisAttrsExpression(node)) {
+        if (currentEmberComponent && isThisAttrsExpression(node)) {
           context.report(node.property, ERROR_MESSAGE);
         }
       },


Test patch:
diff --git a/tests/lib/rules/no-attrs-in-components.js b/tests/lib/rules/no-attrs-in-components.js
index aedabda2..78726346 100644
--- a/tests/lib/rules/no-attrs-in-components.js
+++ b/tests/lib/rules/no-attrs-in-components.js
@@ -19,26 +19,37 @@ const ruleTester = new RuleTester({
 
 ruleTester.run('no-attrs-in-components', rule, {
   valid: [
-    `Component.extend({
-        init() {
-          const newName = get(this, '_name');
-        }
-      });`,
+    "import Component from '@ember/component'; Component.extend({ init() { this.foo.bar; }  });",
+    "import Component from '@ember/component'; class MyComponent extends Component { init() { this.foo.bar; } }",
+    "import Component from '@glimmer/component'; class MyComponent extends Component { constructor() { this.foo.bar; } }",
+
+    // After a component:
+    "import Component from '@ember/component'; Component.extend({}); this.attrs.foo;",
+    "import Component from '@ember/component'; class MyComponent extends Component {} this.attrs.foo;",
+
+    // Not a component:
+    'Random.extend({ init() { this.attrs.foo; }  });',
+    "import Component from 'not-a-component'; class MyComponent extends Component { init() { this.attrs.foo; } }",
   ],
 
   invalid: [
     {
-      code: `Component.extend({
-        init() {
-          const newName = this.attrs.name;
-        }
-      });`,
+      code:
+        "import Component from '@ember/component'; Component.extend({ init() { this.attrs.foo; } });",
+      output: null,
+      errors: [{ message: ERROR_MESSAGE, type: 'Identifier' }],
+    },
+    {
+      code:
+        "import Component from '@ember/component'; class MyComponent extends Component { init() { this.attrs.foo; } }",
+      output: null,
+      errors: [{ message: ERROR_MESSAGE, type: 'Identifier' }],
+    },
+    {
+      code:
+        "import Component from '@glimmer/component'; class MyComponent extends Component { constructor() { this.attrs.foo; } }",
       output: null,
-      errors: [
-        {
-          message: ERROR_MESSAGE,
-        },
-      ],
+      errors: [{ message: ERROR_MESSAGE, type: 'Identifier' }],
     },
   ],
 });