Repository: ember-cli/eslint-plugin-ember
Pull request #577 (2019-11-08 00:00:49)

Add autofixer to `no-get` rule
Adds an autofixer to help with the adoption of this rule. There is already a [codemod](https://github.com/rondale-sc/es5-getter-ember-codemod) for this but the autofixer is more accessible.

I recently updated the rule to eliminate some false positives (#580 #581) and thus safely enable adding this autofixer.

bmish: Looking for feedback on whether there's interest in this autofixer despite the unsafe situations mentioned above.
rwjblue: Can you detect the simple version of the two caveats (like if `ArrayProxy` or `ObjectProxy` are imported, or if we detect an `unknownProperty`), and bail out on the fixer?

Patch:
diff --git a/README.md b/README.md
index f2082d5d..6ae93d0f 100644
--- a/README.md
+++ b/README.md
@@ -91,7 +91,7 @@ The `--fix` option on the command line automatically fixes problems reported by
 |  | [named-functions-in-promises](./docs/rules/named-functions-in-promises.md) | Enforces usage of named functions in promises |
 | :white_check_mark: | [new-module-imports](./docs/rules/new-module-imports.md) |  Use "New Module Imports" from Ember RFC #176 |
 | :white_check_mark: | [no-function-prototype-extensions](./docs/rules/no-function-prototype-extensions.md) | Prevents usage of Ember's `function` prototype extensions |
-| :car: | [no-get](./docs/rules/no-get.md) | Require ES5 getters instead of Ember's `get` / `getProperties` functions |
+| :car::wrench: | [no-get](./docs/rules/no-get.md) | Require ES5 getters instead of Ember's `get` / `getProperties` functions |
 | :white_check_mark: | [no-global-jquery](./docs/rules/no-global-jquery.md) | Prevents usage of global jQuery object |
 | :car: | [no-jquery](./docs/rules/no-jquery.md) | Disallow any usage of jQuery |
 | :white_check_mark: | [no-new-mixins](./docs/rules/no-new-mixins.md) | Prevents creation of new mixins |
diff --git a/lib/rules/no-get.js b/lib/rules/no-get.js
index b4594932..442e4335 100644
--- a/lib/rules/no-get.js
+++ b/lib/rules/no-get.js
@@ -22,6 +22,7 @@ module.exports = {
       recommended: false,
       octane: true,
     },
+    fixable: 'code',
     schema: [
       {
         type: 'object',
@@ -107,7 +108,18 @@ module.exports = {
           (!node.arguments[0].value.includes('.') || !ignoreNestedPaths)
         ) {
           // Example: this.get('foo');
-          context.report(node, makeErrorMessageForGet(node.arguments[0].value), false);
+          const path = node.arguments[0].value;
+          context.report({
+            node,
+            message: makeErrorMessageForGet(path, false),
+            fix(fixer) {
+              if (path.includes('.')) {
+                // Not safe to autofix nested properties because some properties in the path might be null.
+                return null;
+              }
+              return fixer.replaceText(node, `this.${path}`);
+            },
+          });
         }
 
         if (
@@ -119,7 +131,18 @@ module.exports = {
           (!node.arguments[1].value.includes('.') || !ignoreNestedPaths)
         ) {
           // Example: get(this, 'foo');
-          context.report(node, makeErrorMessageForGet(node.arguments[1].value, true));
+          const path = node.arguments[1].value;
+          context.report({
+            node,
+            message: makeErrorMessageForGet(path, true),
+            fix(fixer) {
+              if (path.includes('.')) {
+                // Not safe to autofix nested properties because some properties in the path might be null.
+                return null;
+              }
+              return fixer.replaceText(node, `this.${path}`);
+            },
+          });
         }
 
         // **************************


Test patch:
diff --git a/tests/lib/rules/no-get.js b/tests/lib/rules/no-get.js
index 14a9711c..6f1bc95b 100644
--- a/tests/lib/rules/no-get.js
+++ b/tests/lib/rules/no-get.js
@@ -157,18 +157,18 @@ ruleTester.run('no-get', rule, {
 
     {
       code: "this.get('foo');",
-      output: null,
-      errors: [{ message: makeErrorMessageForGet('foo', false) }],
+      output: 'this.foo;',
+      errors: [{ message: makeErrorMessageForGet('foo', false), type: 'CallExpression' }],
     },
     {
       code: "get(this, 'foo');",
-      output: null,
-      errors: [{ message: makeErrorMessageForGet('foo', true) }],
+      output: 'this.foo;',
+      errors: [{ message: makeErrorMessageForGet('foo', true), type: 'CallExpression' }],
     },
     {
       code: "this.get('foo').someFunction();",
-      output: null,
-      errors: [{ message: makeErrorMessageForGet('foo', false) }],
+      output: 'this.foo.someFunction();',
+      errors: [{ message: makeErrorMessageForGet('foo', false), type: 'CallExpression' }],
     },
 
     // **************************
@@ -234,7 +234,16 @@ ruleTester.run('no-get', rule, {
       });
       this.get('propertyOutsideClass');
       `,
-      output: null,
+      output: `
+      import ArrayProxy from '@ember/array/proxy';
+      export default ArrayProxy.extend({
+        someFunction() {
+          test();
+          console.log(this.get('propertyInsideProxyObject'));
+        }
+      });
+      this.propertyOutsideClass;
+      `,
       errors: [{ message: makeErrorMessageForGet('propertyOutsideClass'), type: 'CallExpression' }],
     },
     {
@@ -249,7 +258,16 @@ ruleTester.run('no-get', rule, {
       }
       this.get('propertyOutsideClass');
       `,
-      output: null,
+      output: `
+      import ArrayProxy from '@ember/array/proxy';
+      class MyProxy extends ArrayProxy {
+        someFunction() {
+          test();
+          console.log(this.get('propertyInsideProxyObject'));
+        }
+      }
+      this.propertyOutsideClass;
+      `,
       errors: [{ message: makeErrorMessageForGet('propertyOutsideClass'), type: 'CallExpression' }],
     },
 
@@ -265,7 +283,16 @@ ruleTester.run('no-get', rule, {
       });
       this.get('propertyOutsideClass');
       `,
-      output: null,
+      output: `
+      import EmberObject from '@ember/object';
+      export default EmberObject.extend({
+        unknownProperty() {},
+        someFunction() {
+          console.log(this.get('propertyInsideClassWithUnknownProperty'));
+        }
+      });
+      this.propertyOutsideClass;
+      `,
       errors: [{ message: makeErrorMessageForGet('propertyOutsideClass'), type: 'CallExpression' }],
     },
     {
@@ -280,7 +307,16 @@ ruleTester.run('no-get', rule, {
       }
       this.get('propertyOutsideClass');
       `,
-      output: null,
+      output: `
+      import EmberObject from '@ember/object';
+      class MyClass extends EmberObject {
+        unknownProperty() {}
+        someFunction() {
+          console.log(this.get('propertyInsideClassWithUnknownProperty'));
+        }
+      }
+      this.propertyOutsideClass;
+      `,
       errors: [{ message: makeErrorMessageForGet('propertyOutsideClass'), type: 'CallExpression' }],
     },
   ],