Repository: ember-cli/eslint-plugin-ember
Pull request #764 (2020-04-10 16:55:42)

Catch unnecessary braces in `no-invalid-dependent-keys` rule
Before: `computed("foo.{bar}", function() {})`
After: `computed("foo.bar", function() {})`

Includes autofixer.


Patch:
diff --git a/docs/rules/no-invalid-dependent-keys.md b/docs/rules/no-invalid-dependent-keys.md
index b4a1fd25..b3711821 100644
--- a/docs/rules/no-invalid-dependent-keys.md
+++ b/docs/rules/no-invalid-dependent-keys.md
@@ -12,8 +12,8 @@ This rule aims to avoid invalid dependent keys in computed properties.
 
 Currently implemented:
 
-- Unbalanced open and closed braces. These can be hard to track for complex computed properties
-and are usually unchecked since the expressions are passed as Strings.
+- Unbalanced open and closed braces. These can be hard to track for complex computed properties and are usually unchecked since the expressions are passed as Strings.
+- Unnecessary braces
 - Invalid position of `@each` or `[]`
 
 Not yet implemented:
@@ -33,6 +33,15 @@ export default Component.extend({
 });
 ```
 
+```js
+export default Component.extend({
+  // Unnecessary braces:
+  userId: computed('user.{id}', {
+    // Code
+  })
+});
+```
+
 ```js
 export default Component.extend({
   // Terminal `@each`:
@@ -61,6 +70,14 @@ export default Component.extend({
 });
 ```
 
+```js
+export default Component.extend({
+  userId: computed('user.id', {
+    // Code
+  })
+});
+```
+
 ```js
 export default Component.extend({
   items: computed('arr.[]', {
diff --git a/lib/rules/no-invalid-dependent-keys.js b/lib/rules/no-invalid-dependent-keys.js
index 3275c7ee..0fac2a26 100644
--- a/lib/rules/no-invalid-dependent-keys.js
+++ b/lib/rules/no-invalid-dependent-keys.js
@@ -8,6 +8,7 @@ const ember = require('../utils/ember');
 //------------------------------------------------------------------------------
 
 const ERROR_MESSAGE_UNBALANCED_BRACES = 'Found unbalanced braces in dependent key';
+const ERROR_MESSAGE_UNNECESSARY_BRACES = 'Found unnecessary braces in dependent key';
 const ERROR_MESSAGE_TERMINAL_AT_EACH = 'Found terminal `@each`, use `[]` instead';
 const ERROR_MESSAGE_MIDDLE_BRACKETS = '`[]` should only be used at the end of the dependent key';
 
@@ -18,6 +19,11 @@ function hasUnbalancedBraces(str) {
   return openBraces !== closeBraces;
 }
 
+const REGEXP_UNNECESSARY_BRACES = /{([^,.]+)}/g;
+function hasUnnecessaryBraces(str) {
+  return str.match(REGEXP_UNNECESSARY_BRACES);
+}
+
 function hasTerminalAtEach(str) {
   return str.endsWith('.@each');
 }
@@ -41,6 +47,7 @@ module.exports = {
   },
 
   ERROR_MESSAGE_UNBALANCED_BRACES,
+  ERROR_MESSAGE_UNNECESSARY_BRACES,
   ERROR_MESSAGE_TERMINAL_AT_EACH,
   ERROR_MESSAGE_MIDDLE_BRACKETS,
 
@@ -76,6 +83,17 @@ module.exports = {
             });
           }
 
+          if (hasUnnecessaryBraces(node.value)) {
+            context.report({
+              node,
+              message: ERROR_MESSAGE_UNNECESSARY_BRACES,
+              fix(fixer) {
+                const nodeText = sourceCode.getText(node);
+                return fixer.replaceText(node, nodeText.replace(REGEXP_UNNECESSARY_BRACES, '$1'));
+              },
+            });
+          }
+
           if (hasMiddleBrackets(node.value)) {
             context.report({
               node,


Test patch:
diff --git a/tests/lib/rules/no-invalid-dependent-keys.js b/tests/lib/rules/no-invalid-dependent-keys.js
index 9a68e6df..2671d0b9 100644
--- a/tests/lib/rules/no-invalid-dependent-keys.js
+++ b/tests/lib/rules/no-invalid-dependent-keys.js
@@ -3,6 +3,7 @@ const RuleTester = require('eslint').RuleTester;
 
 const {
   ERROR_MESSAGE_UNBALANCED_BRACES,
+  ERROR_MESSAGE_UNNECESSARY_BRACES,
   ERROR_MESSAGE_TERMINAL_AT_EACH,
   ERROR_MESSAGE_MIDDLE_BRACKETS,
 } = rule;
@@ -120,6 +121,28 @@ eslintTester.run('no-invalid-dependent-keys', rule, {
       ],
     },
 
+    // Unnecessary braces
+    {
+      code: '{ test: computed("foo.{bar}", function() {}) }',
+      output: '{ test: computed("foo.bar", function() {}) }',
+      errors: [
+        {
+          message: ERROR_MESSAGE_UNNECESSARY_BRACES,
+          type: 'Literal',
+        },
+      ],
+    },
+    {
+      code: '{ test: computed("foo.{bar}.{abc}", function() {}) }',
+      output: '{ test: computed("foo.bar.abc", function() {}) }',
+      errors: [
+        {
+          message: ERROR_MESSAGE_UNNECESSARY_BRACES,
+          type: 'Literal',
+        },
+      ],
+    },
+
     // @each
     {
       code: 'computed("foo.@each", function() {})',