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

Issue:
Nested Brace Expanded Properties
I get errors from `ember/use-brace-expansion` with something like this:

```javascript
  sampleProperty: computed(
    'foo.bar.{name,place}',
    'foo.baz.{thing,@each.stuff}',
    'foo.qux.[]',
    function() {
```

It doesn't pass until I nest them all like this:

```javascript
  sampleProperty: computed(
    'foo.{bar.{name,place},baz.{thing,@each.stuff},qux.[]}',
    function() {
```

This does not work. Ember does not support nested brace expansion, per [this comment/code](https://github.com/emberjs/ember.js/blob/master/packages/ember-metal/lib/expand_properties.js#L45).

Pull request:
Don't suggest nested property brace expansion
Fixes #105

Patch:
diff --git a/README.md b/README.md
index 6179dadc..7bc779ab 100644
--- a/README.md
+++ b/README.md
@@ -37,8 +37,8 @@ module.exports = {
 ```
 
 Possible configurations:
-- [plugin:ember/base](https://github.com/ember-cli/eslint-plugin-ember/blob/master/config/base.js) - contains only recommended settings for custom rules defined in this plugin.
-- [plugin:ember/recommended](https://github.com/ember-cli/eslint-plugin-ember/blob/master/config/recommended.js) - extends base configuration with extra rules' settings provided by eslint
+- [plugin:ember/base](https://github.com/ember-cli/eslint-plugin-ember/blob/master/lib/config/base.js) - contains only recommended settings for custom rules defined in this plugin.
+- [plugin:ember/recommended](https://github.com/ember-cli/eslint-plugin-ember/blob/master/lib/config/recommended.js) - extends base configuration with extra rules' settings provided by eslint
 
 #### Use plain plugin:
 
diff --git a/lib/rules/use-brace-expansion.js b/lib/rules/use-brace-expansion.js
index d396193e..614b1e34 100644
--- a/lib/rules/use-brace-expansion.js
+++ b/lib/rules/use-brace-expansion.js
@@ -28,17 +28,33 @@ module.exports = {
       CallExpression(node) {
         if (!ember.isComputedProp(node)) return;
 
-        const properties = node.arguments
-          .filter(arg => utils.isLiteral(arg) && typeof arg.value === 'string')
+        const matchesBraces = x => !!x.match(/[{}]/g);
+        const hasBraces = arr => arr.some(matchesBraces);
+        const beforeBraces = arr => arr.slice(0, arr.indexOf(arr.find(matchesBraces)));
+        const arrayDeepEqual = (a, b) =>
+          a.length === b.length && a.reduce((acc, e, i) => acc && e === b[i], true);
+
+        const problem = node.arguments
+          .filter(arg => utils.isLiteral(arg) && typeof arg.value === 'string' && arg.value.indexOf('.') >= 0)
           .map(e => e.value.split('.'))
-          .filter(e => e.length > 1)
-          .map(e => e[0])
-          .sort((a, b) => a > b);
-
-        for (let i = 0; i < properties.length - 1; i++) {
-          if (properties[i] === properties[i + 1]) {
-            report(node);
-          }
+          .find((prop, i, props) => props.filter((e) => {
+            const propHasBraces = hasBraces(prop);
+            const eHasBraces = hasBraces(e);
+
+            if (propHasBraces && eHasBraces) {
+              return arrayDeepEqual(beforeBraces(e), beforeBraces(prop));
+            } else if (!propHasBraces && !eHasBraces) {
+              return prop[0] === e[0];
+            }
+
+            const withBraces = propHasBraces ? prop : e;
+            const withoutBraces = propHasBraces ? e : prop;
+            const shareable = beforeBraces(withBraces);
+            return arrayDeepEqual(shareable, withoutBraces.slice(0, shareable.length));
+          }).length > 1);
+
+        if (problem) {
+          report(node);
         }
       },
     };


Test patch:
diff --git a/tests/lib/rules/use-brace-expansion.js b/tests/lib/rules/use-brace-expansion.js
index 2d43267e..d5963be2 100644
--- a/tests/lib/rules/use-brace-expansion.js
+++ b/tests/lib/rules/use-brace-expansion.js
@@ -16,15 +16,42 @@ eslintTester.run('use-brace-expansion', rule, {
     { code: '{ test: computed(function() {}) }' },
     { code: '{ test: computed("a.test", "b.test", function() {}) }' },
     { code: '{ test: computed("a.{test,test2}", "b", function() {}) }' },
+    { code: '{ test: computed("a.{test,test2}", "c", "b", function() {}) }' },
+    { code: '{ test: computed("model.a.{test,test2}", "model.b.{test3,test4}", function() {}) }' },
+    { code: '{ test: computed("foo.bar.{name,place}", "foo.qux.[]", "foo.baz.{thing,@each.stuff}", function() {}) }' },
     { code: "{ test: Ember.computed.filterBy('a', 'b', false) }" },
   ],
   invalid: [
+    {
+      code: '{ test: computed("foo.{name,place}", "foo.[]", "foo.{thing,@each.stuff}", function() {}) }',
+      errors: [{
+        message: 'Use brace expansion',
+      }],
+    },
     {
       code: '{ test: computed("a.test", "a.test2", function() {}) }',
       errors: [{
         message: 'Use brace expansion',
       }],
     },
+    {
+      code: '{ test: computed("a.{same,level}", "a.{not,nested}", function() {}) }',
+      errors: [{
+        message: 'Use brace expansion',
+      }],
+    },
+    {
+      code: '{ test: computed("a.b.c.d", "a.b.deep.props", function() {}) }',
+      errors: [{
+        message: 'Use brace expansion',
+      }],
+    },
+    {
+      code: '{ test: computed("a.{test,test2}", "c", "a.test3.test4", function() {}) }',
+      errors: [{
+        message: 'Use brace expansion',
+      }],
+    },
     {
       code: '{ test: computed("a.{test,test2}", "a.test3", function() {}) }',
       errors: [{