Repository: ember-cli/eslint-plugin-ember
Pull request #413 (2019-04-21 20:19:59)

Ignore template literals in `no-get` and `no-get-properties` rules
Ignore template literals since we can't be sure if they contain nested property paths (which should always be ignored by these rules).

And without this fix, template literals will cause the rule to throw an error: `TypeError: Cannot read property 'includes' of undefined`


Patch:
diff --git a/lib/rules/no-get-properties.js b/lib/rules/no-get-properties.js
index 5863fc66..1926550e 100644
--- a/lib/rules/no-get-properties.js
+++ b/lib/rules/no-get-properties.js
@@ -60,5 +60,5 @@ function validateGetPropertiesArguments(args) {
     return validateGetPropertiesArguments(args[0].elements);
   }
   // We can only handle string arguments without nested property paths.
-  return args.every(argument => utils.isString(argument) && !argument.value.includes('.'));
+  return args.every(argument => utils.isStringLiteral(argument) && !argument.value.includes('.'));
 }
diff --git a/lib/rules/no-get.js b/lib/rules/no-get.js
index 474da180..3ab1f091 100644
--- a/lib/rules/no-get.js
+++ b/lib/rules/no-get.js
@@ -25,7 +25,7 @@ module.exports = {
           utils.isThisExpression(node.callee.object) &&
           node.callee.property.name === 'get' &&
           node.arguments.length === 1 &&
-          utils.isString(node.arguments[0]) &&
+          utils.isStringLiteral(node.arguments[0]) &&
           !node.arguments[0].value.includes('.')
         ) {
           // Example: this.get('foo');
@@ -37,7 +37,7 @@ module.exports = {
           node.callee.name === 'get' &&
           node.arguments.length === 2 &&
           utils.isThisExpression(node.arguments[0]) &&
-          utils.isString(node.arguments[1]) &&
+          utils.isStringLiteral(node.arguments[1]) &&
           !node.arguments[1].value.includes('.')
         ) {
           // Example: get(this, 'foo');
diff --git a/lib/utils/utils.js b/lib/utils/utils.js
index c77b37ca..8b3aa1ad 100644
--- a/lib/utils/utils.js
+++ b/lib/utils/utils.js
@@ -21,6 +21,7 @@ module.exports = {
   isTaggedTemplateExpression,
   isGlobalCallExpression,
   isString,
+  isStringLiteral,
   getSize,
   parseCallee,
   parseArgs,
@@ -272,6 +273,10 @@ function isString(node) {
   return isTemplateLiteral(node) || (isLiteral(node) && typeof node.value === 'string');
 }
 
+function isStringLiteral(node) {
+  return isLiteral(node) && typeof node.value === 'string';
+}
+
 /**
  * Get size of expression in lines
  *


Test patch:
diff --git a/tests/lib/rules/no-get-properties.js b/tests/lib/rules/no-get-properties.js
index 16821bba..383b5e9c 100644
--- a/tests/lib/rules/no-get-properties.js
+++ b/tests/lib/rules/no-get-properties.js
@@ -27,6 +27,10 @@ ruleTester.run('no-get-properties', rule, {
     "const { abc, def } = myObject.getProperties('abc', 'def');",
     "const { abc, def } = this.get('something').getProperties('abc', 'def');",
 
+    // Template literals.
+    'const { abc, def } = this.getProperties(`abc`, `def`);',
+    'const { abc, def } = getProperties(this, `abc`, `def`);',
+
     // Destructuring assignment but no `getProperties`.
     'const { abc, def } = this;',
 
diff --git a/tests/lib/rules/no-get.js b/tests/lib/rules/no-get.js
index 0f3c5d91..555288be 100644
--- a/tests/lib/rules/no-get.js
+++ b/tests/lib/rules/no-get.js
@@ -11,6 +11,16 @@ ruleTester.run('no-get', rule, {
     "this.get('foo.bar');",
     "get(this, 'foo.bar');",
 
+    // Template literals.
+    {
+      code: 'this.get(`foo`);',
+      parserOptions: { ecmaVersion: 6 }
+    },
+    {
+      code: 'get(this, `foo`);',
+      parserOptions: { ecmaVersion: 6 }
+    },
+
     // Not `this`.
     "foo.get('bar');",
     "get(foo, 'bar');",
diff --git a/tests/lib/utils/utils-test.js b/tests/lib/utils/utils-test.js
index d6486b04..bae13441 100644
--- a/tests/lib/utils/utils-test.js
+++ b/tests/lib/utils/utils-test.js
@@ -317,3 +317,52 @@ describe('isGlobalCallExpression', () => {
     expect(utils.isGlobalCallExpression(node, 'jQuery', ['$', 'jQuery'])).toBeTruthy();
   });
 });
+
+describe('isString', () => {
+  it('recognizes template literals', () => {
+    const node = parse('`template literal`');
+    expect(utils.isString(node)).toBeTruthy();
+  });
+
+  it('recognizes template literals with interpolation', () => {
+    const node = parse('`template ${123} literal`'); // eslint-disable-line no-template-curly-in-string
+    expect(utils.isString(node)).toBeTruthy();
+  });
+
+  it('recognizes string literals', () => {
+    const node = parse("'string literal'");
+    expect(utils.isString(node)).toBeTruthy();
+  });
+
+  it('ignores identifiers', () => {
+    const node = parse('MY_VARIABLE');
+    expect(utils.isString(node)).not.toBeTruthy();
+  });
+
+  it('ignores number literals', () => {
+    const node = parse('123');
+    expect(utils.isString(node)).not.toBeTruthy();
+  });
+});
+
+describe('isStringLiteral', () => {
+  it('recognizes string literals', () => {
+    const node = parse("'string literal'");
+    expect(utils.isStringLiteral(node)).toBeTruthy();
+  });
+
+  it('ignores template literals', () => {
+    const node = parse('`template literal`');
+    expect(utils.isStringLiteral(node)).not.toBeTruthy();
+  });
+
+  it('ignores identifiers', () => {
+    const node = parse('MY_VARIABLE');
+    expect(utils.isStringLiteral(node)).not.toBeTruthy();
+  });
+
+  it('ignores number literals', () => {
+    const node = parse('123');
+    expect(utils.isStringLiteral(node)).not.toBeTruthy();
+  });
+});