Repository: ember-cli/eslint-plugin-ember
Pull request #561 (2019-10-31 07:53:26)

Add `octane` configuration (experimental)
Fixes https://github.com/ember-cli/eslint-plugin-ember/issues/533

Creating an Octane config and including the following rules to start:

* `ember/no-actions-hash`
* `ember/no-classic-classes`
* `ember/no-classic-components`
* `ember/no-computed-properties-in-native-classes`
* `ember/require-tagless-components`

Questions I have:

* Are there any other Octane specific rules we should be adding?
* Should this only contain Octane rules or should this be similar to [ember-template-lint](https://github.com/ember-template-lint/ember-template-lint/tree/master/lib/config) where the `Octane` config also contains a selection of relevant `recommended` rules? So you use either `octane` or `recommended`, not both.
* I notice the `recommended` ruleset is auto-generated. Should I also be somehow doing the same here?



bmish: I would also add these rules:
* `no-get`
* `no-jquery`
* `classic-decorator-hooks`
* `classic-decorator-no-classic-methods`
bmish: I would also add these rules:
* `no-get`
* `no-jquery`
* `classic-decorator-hooks`
* `classic-decorator-no-classic-methods`

I'm also leaning towards having this new config extend `recommended` since Octane users would want all of the recommended rules too and it would be tedious/error-prone to remember to have to enable both configs.

And I would also recommend adding an `octane: true` property to such rules so we can autogenerate the list. 

And we'll want to update the `rule-setup.js` tests to test this new config.
patocallaghan: @bmish I made some updates based on your feedback

1. `octane: true` has now been added to the meta of the rules we want to add to the Octane config
2. I'm now autogenerating the octane-rules.js file
3. I've added a test to check the rules in the Octane config
4. I'm extending the Octane config from Recommended
bmish: I added an additional configuration test in [plugin-exports.js](https://github.com/ember-cli/eslint-plugin-ember/blob/master/tests/plugin-exports.js), can you rebase and update that test?
Turbo87: regarding the Readme change: we should mention that this ruleset is unstable/experimental for now so that we don't have to consider adding new rules as a breaking change. once we're happy with the contents we can remove that part and make it stable. 

Patch:
diff --git a/.eslintignore b/.eslintignore
index b10e4f8a..abcf9d5e 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,3 +1,4 @@
 coverage/
 node_modules
 lib/recommended-rules.js
+lib/octane-rules.js
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 00000000..075bb839
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,2 @@
+lib/octane-rules.js
+lib/recommended-rules.js
\ No newline at end of file
diff --git a/README.md b/README.md
index cdcc43e0..f2082d5d 100644
--- a/README.md
+++ b/README.md
@@ -46,6 +46,7 @@ module.exports = {
 Possible configurations:
 - [plugin:ember/base](https://github.com/ember-cli/eslint-plugin-ember/blob/master/lib/config/base.js) - contains no rules settings, but the basic eslint configuration suitable for any ember project. You can use it to configure rules as you wish.
 - [plugin:ember/recommended](https://github.com/ember-cli/eslint-plugin-ember/blob/master/lib/config/recommended.js) - extends base configuration with recommended rules' settings
+- :warning: [plugin:ember/octane](https://github.com/ember-cli/eslint-plugin-ember/blob/master/lib/config/octane.js) - extends recommended configuration with octane rules' settings. This ruleset is currently considered **unstable and experiemental** as rules may be added and removed until the final ruleset is settled upon.
 
 #### Use plain plugin:
 
@@ -74,6 +75,8 @@ Rules are grouped by category to help you understand their purpose.
 
 All rules below with a check mark :white_check_mark: are enabled by default while using `plugin:ember/recommended` config.
 
+The `plugin:ember/octane` config contains both Octane rules with a red car :car: in addition to the rules in the `plugin:ember/recommended` config.
+
 The `--fix` option on the command line automatically fixes problems reported by rules which have a wrench :wrench: below.
 
 <!--RULES_TABLE_START-->
@@ -88,9 +91,9 @@ 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 |
-|  | [no-get](./docs/rules/no-get.md) | Require ES5 getters instead of Ember's `get` / `getProperties` functions |
+| :car: | [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 |
-|  | [no-jquery](./docs/rules/no-jquery.md) | Disallow any usage of jQuery |
+| :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 |
 | :white_check_mark: | [no-observers](./docs/rules/no-observers.md) | Prevents usage of observers |
 | :white_check_mark::wrench: | [no-old-shims](./docs/rules/no-old-shims.md) | Prevents usage of old shims for modules |
@@ -110,8 +113,8 @@ The `--fix` option on the command line automatically fixes problems reported by
 |    | Rule ID | Description |
 |:---|:--------|:------------|
 | :white_check_mark: | [avoid-leaking-state-in-ember-objects](./docs/rules/avoid-leaking-state-in-ember-objects.md) | Avoids state leakage |
-|  | [classic-decorator-hooks](./docs/rules/classic-decorator-hooks.md) | Ensure correct hooks are used for both classic and non-classic classes |
-|  | [classic-decorator-no-classic-methods](./docs/rules/classic-decorator-no-classic-methods.md) | Prevent usage of classic APIs such as get/set in classes that aren't explicitly decorated with @classic |
+| :car: | [classic-decorator-hooks](./docs/rules/classic-decorator-hooks.md) | Ensure correct hooks are used for both classic and non-classic classes |
+| :car: | [classic-decorator-no-classic-methods](./docs/rules/classic-decorator-no-classic-methods.md) | Prevent usage of classic APIs such as get/set in classes that aren't explicitly decorated with @classic |
 |  | [computed-property-getters](./docs/rules/computed-property-getters.md) | Enforce the consistent use of getters in computed properties |
 |  | [no-proxies](./docs/rules/no-proxies.md) | Disallows using array or object proxies |
 
@@ -142,11 +145,11 @@ The `--fix` option on the command line automatically fixes problems reported by
 
 |    | Rule ID | Description |
 |:---|:--------|:------------|
-|  | [no-actions-hash](./docs/rules/no-actions-hash.md) | Disallows the actions hash in components, controllers and routes |
-|  | [no-classic-classes](./docs/rules/no-classic-classes.md) | Disallow "classic" classes in favor of native JS classes |
-|  | [no-classic-components](./docs/rules/no-classic-components.md) | Enforces Glimmer components |
-|  | [no-computed-properties-in-native-classes](./docs/rules/no-computed-properties-in-native-classes.md) | Disallows using computed properties in native classes |
-|  | [require-tagless-components](./docs/rules/require-tagless-components.md) | Disallows using the wrapper element of a Component |
+| :car: | [no-actions-hash](./docs/rules/no-actions-hash.md) | Disallows the actions hash in components, controllers and routes |
+| :car: | [no-classic-classes](./docs/rules/no-classic-classes.md) | Disallow "classic" classes in favor of native JS classes |
+| :car: | [no-classic-components](./docs/rules/no-classic-components.md) | Enforces Glimmer components |
+| :car: | [no-computed-properties-in-native-classes](./docs/rules/no-computed-properties-in-native-classes.md) | Disallows using computed properties in native classes |
+| :car: | [require-tagless-components](./docs/rules/require-tagless-components.md) | Disallows using the wrapper element of a Component |
 
 
 ### Ember Data
diff --git a/lib/config/octane.js b/lib/config/octane.js
new file mode 100644
index 00000000..4e4c0233
--- /dev/null
+++ b/lib/config/octane.js
@@ -0,0 +1,6 @@
+const rules = require('../octane-rules.js');
+
+module.exports = {
+  extends: require.resolve('./recommended.js'),
+  rules,
+};
diff --git a/lib/index.js b/lib/index.js
index 3cd6b868..e33f0208 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -64,6 +64,7 @@ module.exports = {
   configs: {
     base: require('./config/base'),
     recommended: require('./config/recommended'),
+    octane: require('./config/octane'),
   },
   utils: {
     ember: require('./utils/ember'),
diff --git a/lib/octane-rules.js b/lib/octane-rules.js
new file mode 100644
index 00000000..e6915a1b
--- /dev/null
+++ b/lib/octane-rules.js
@@ -0,0 +1,17 @@
+/*
+ * IMPORTANT!
+ * This file has been automatically generated.
+ * In order to update its content based on rules'
+ * definitions, execute "npm run update"
+ */
+module.exports = {
+  "ember/classic-decorator-hooks": "error",
+  "ember/classic-decorator-no-classic-methods": "error",
+  "ember/no-actions-hash": "error",
+  "ember/no-classic-classes": "error",
+  "ember/no-classic-components": "error",
+  "ember/no-computed-properties-in-native-classes": "error",
+  "ember/no-get": "error",
+  "ember/no-jquery": "error",
+  "ember/require-tagless-components": "error"
+}
\ No newline at end of file
diff --git a/lib/rules/classic-decorator-hooks.js b/lib/rules/classic-decorator-hooks.js
index ebe3f07f..eae3592a 100644
--- a/lib/rules/classic-decorator-hooks.js
+++ b/lib/rules/classic-decorator-hooks.js
@@ -15,6 +15,7 @@ module.exports = {
       description: 'Ensure correct hooks are used for both classic and non-classic classes',
       category: 'Ember Object',
       recommended: false,
+      octane: true,
       url:
         'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/classic-decorator-hooks.md',
     },
diff --git a/lib/rules/classic-decorator-no-classic-methods.js b/lib/rules/classic-decorator-no-classic-methods.js
index 4a947cdc..d548dcb1 100644
--- a/lib/rules/classic-decorator-no-classic-methods.js
+++ b/lib/rules/classic-decorator-no-classic-methods.js
@@ -27,6 +27,7 @@ module.exports = {
         "Prevent usage of classic APIs such as get/set in classes that aren't explicitly decorated with @classic",
       category: 'Ember Object',
       recommended: false,
+      octane: true,
       url:
         'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/classic-decorator-no-classic-methods.md',
     },
diff --git a/lib/rules/no-actions-hash.js b/lib/rules/no-actions-hash.js
index 5c44276a..78a63b29 100644
--- a/lib/rules/no-actions-hash.js
+++ b/lib/rules/no-actions-hash.js
@@ -10,6 +10,7 @@ module.exports = {
       description: 'Disallows the actions hash in components, controllers and routes',
       category: 'Ember Octane',
       recommended: false,
+      octane: true,
     },
     fixable: null, // or "code" or "whitespace"
     url:
diff --git a/lib/rules/no-classic-classes.js b/lib/rules/no-classic-classes.js
index d5031bf1..4327d808 100644
--- a/lib/rules/no-classic-classes.js
+++ b/lib/rules/no-classic-classes.js
@@ -26,6 +26,7 @@ module.exports = {
       description: 'Disallow "classic" classes in favor of native JS classes',
       category: 'Ember Octane',
       recommended: false,
+      octane: true,
     },
     fixable: null, // or "code" or "whitespace"
   },
diff --git a/lib/rules/no-classic-components.js b/lib/rules/no-classic-components.js
index f3db6fc5..26b17220 100644
--- a/lib/rules/no-classic-components.js
+++ b/lib/rules/no-classic-components.js
@@ -9,6 +9,7 @@ module.exports = {
       description: 'Enforces Glimmer components',
       category: 'Ember Octane',
       recommended: false,
+      octane: true,
       url:
         'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/no-classic-components.md',
     },
diff --git a/lib/rules/no-computed-properties-in-native-classes.js b/lib/rules/no-computed-properties-in-native-classes.js
index 3c6f57b3..e45e48b5 100644
--- a/lib/rules/no-computed-properties-in-native-classes.js
+++ b/lib/rules/no-computed-properties-in-native-classes.js
@@ -31,6 +31,7 @@ module.exports = {
       description: 'Disallows using computed properties in native classes',
       category: 'Ember Octane',
       recommended: false,
+      octane: true,
       url:
         'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/no-computed-properties-in-native-classes.md',
     },
diff --git a/lib/rules/no-get.js b/lib/rules/no-get.js
index 6468eba3..c180374e 100644
--- a/lib/rules/no-get.js
+++ b/lib/rules/no-get.js
@@ -19,6 +19,7 @@ module.exports = {
       description: "Require ES5 getters instead of Ember's `get` / `getProperties` functions",
       category: 'Best Practices',
       recommended: false,
+      octane: true,
     },
     schema: [
       {
diff --git a/lib/rules/no-jquery.js b/lib/rules/no-jquery.js
index d54fb9da..2e26dacf 100644
--- a/lib/rules/no-jquery.js
+++ b/lib/rules/no-jquery.js
@@ -17,6 +17,7 @@ module.exports = {
       description: 'Disallow any usage of jQuery',
       category: 'Best Practices',
       recommended: false,
+      octane: true,
       url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/no-jquery.md',
     },
     fixable: null, // or "code" or "whitespace"
diff --git a/lib/rules/require-tagless-components.js b/lib/rules/require-tagless-components.js
index d3f17e2a..463699da 100644
--- a/lib/rules/require-tagless-components.js
+++ b/lib/rules/require-tagless-components.js
@@ -92,6 +92,7 @@ module.exports = {
       description: 'Disallows using the wrapper element of a Component',
       category: 'Ember Octane',
       recommended: false,
+      octane: true,
       url:
         'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/require-tagless-components.md',
     },
diff --git a/scripts/update-rules.js b/scripts/update-rules.js
index 9ab9fc61..0da028ae 100644
--- a/scripts/update-rules.js
+++ b/scripts/update-rules.js
@@ -21,11 +21,13 @@ const path = require('path');
 const root = path.resolve(__dirname, '../lib/rules');
 const readmeFile = path.resolve(__dirname, '../README.md');
 const recommendedRulesFile = path.resolve(__dirname, '../lib/recommended-rules.js');
+const octaneRulesFile = path.resolve(__dirname, '../lib/octane-rules.js');
 const tablePlaceholder = /<!--RULES_TABLE_START-->[\s\S]*<!--RULES_TABLE_END-->/;
 const readmeContent = fs.readFileSync(readmeFile, 'utf8');
 
 const STAR = ':white_check_mark:';
 const PEN = ':wrench:';
+const OCTANE = ':car:';
 
 const rules = fs
   .readdirSync(root)
@@ -54,7 +56,9 @@ ${rules
   .map(entry => {
     const name = entry[0];
     const meta = entry[1].meta;
-    const mark = `${meta.docs.recommended ? STAR : ''}${meta.fixable ? PEN : ''}`;
+    const mark = `${meta.docs.recommended ? STAR : ''}${meta.docs.octane ? OCTANE : ''}${
+      meta.fixable ? PEN : ''
+    }`;
     const link = `[${name}](./docs/rules/${name}.md)`;
     const description = meta.docs.description || '(no description)';
     return `| ${mark} | ${link} | ${description} |`;
@@ -112,3 +116,22 @@ fs.writeFileSync(
 );
 
 fs.writeFileSync(recommendedRulesFile, recommendedRulesContent);
+
+const octaneRules = rules.reduce((obj, entry) => {
+  const name = `ember/${entry[0]}`;
+  const octane = entry[1].meta.docs.octane;
+  if (octane) {
+    obj[name] = 'error'; // eslint-disable-line no-param-reassign
+  }
+  return obj;
+}, {});
+
+const octaneRulesContent = `/*
+ * IMPORTANT!
+ * This file has been automatically generated.
+ * In order to update its content based on rules'
+ * definitions, execute "npm run update"
+ */
+module.exports = ${JSON.stringify(octaneRules, null, 2)}`;
+
+fs.writeFileSync(octaneRulesFile, octaneRulesContent);


Test patch:
diff --git a/tests/plugin-exports.js b/tests/plugin-exports.js
index 0eb2fa5a..da4accd1 100644
--- a/tests/plugin-exports.js
+++ b/tests/plugin-exports.js
@@ -6,6 +6,7 @@ const ember = require('../lib/utils/ember');
 const utils = require('../lib/utils/utils');
 const base = require('../lib/config/base.js');
 const recommended = require('../lib/config/recommended.js');
+const octane = require('../lib/config/octane.js');
 
 describe('plugin exports', () => {
   describe('utils', () => {
@@ -16,7 +17,7 @@ describe('plugin exports', () => {
 
   describe('configs', () => {
     it('has the right configurations', () => {
-      assert.deepStrictEqual(plugin.configs, { base, recommended });
+      assert.deepStrictEqual(plugin.configs, { base, recommended, octane });
     });
   });
 });
diff --git a/tests/rule-setup.js b/tests/rule-setup.js
index 677eed5e..dfcc8028 100644
--- a/tests/rule-setup.js
+++ b/tests/rule-setup.js
@@ -5,8 +5,10 @@ const { join } = require('path');
 const assert = require('assert');
 const rules = require('../lib/index.js').rules;
 const recommendedRules = require('../lib/recommended-rules.js');
+const octaneRules = require('../lib/octane-rules.js');
 
 const RULE_NAMES = Object.keys(rules);
+const OCTANE_RULE_NAMES = Object.keys(octaneRules);
 
 describe('rules setup is correct', function() {
   it('should have a list of exported rules and rules directory that match', function() {
@@ -27,6 +29,14 @@ describe('rules setup is correct', function() {
     );
   });
 
+  it('should list all rules in the octane rules file', function() {
+    const octaneRuleNames = Object.keys(rules).filter(key => rules[key].meta.docs.octane);
+    assert.deepStrictEqual(
+      OCTANE_RULE_NAMES.map(file => file.replace('ember/', '')),
+      octaneRuleNames
+    );
+  });
+
   it('should have tests for all rules', function() {
     const path = join(__dirname, '../tests/lib/rules');
     const files = readdirSync(path);