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

Issue:
New Rule: `no-empty-glimmer-component-classes`
Add new rule to lint against empty backing classes for Glimmer components. Template-only Glimmer components without backing classes are much faster and lighter-weight than those with backing classes. 

Pull request description:
Add new rule `no-empty-glimmer-component-classes`
This fixes #992 

Patch:
diff --git a/README.md b/README.md
index 01386cec..08e0fe81 100644
--- a/README.md
+++ b/README.md
@@ -138,6 +138,7 @@ Rules are grouped by category to help you understand their purpose. Each rule ha
 | :car: | [no-actions-hash](./docs/rules/no-actions-hash.md) | disallow 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 |
 | :white_check_mark::wrench: | [no-ember-super-in-es-classes](./docs/rules/no-ember-super-in-es-classes.md) | disallow use of `this._super` in ES class methods |
+|  | [no-empty-glimmer-component-classes](./docs/rules/no-empty-glimmer-component-classes.md) | disallow empty backing classes for Glimmer components |
 
 ### jQuery
 
diff --git a/docs/rules/no-empty-glimmer-component-classes.md b/docs/rules/no-empty-glimmer-component-classes.md
new file mode 100644
index 00000000..e5fa0d6d
--- /dev/null
+++ b/docs/rules/no-empty-glimmer-component-classes.md
@@ -0,0 +1,42 @@
+# no-empty-glimmer-component-classes
+
+This rule will catch and prevent the use of empty backing classes for Glimmer components.
+
+## Rule Details
+
+This rule aims to disallow the use of empty backing classes for Glimmer components when possible. Template-only Glimmer components where there is no backing class are much faster and lighter-weight than Glimmer components with backing classes, which are much lighter-weight than Ember components. Therefore, you should only have a backing class for a Glimmer component when absolutely necessary.
+
+In addons, the empty class should be replaced with a `templateOnly` export. This is because addons can't assume `template-only-glimmer-components` is enabled. In apps, remove the backing class entirely until it is actually needed.
+
+## Examples
+
+Examples of **incorrect** code for this rule:
+
+```js
+import Component from '@glimmer/component';
+
+class MyComponent extends Component {}
+```
+
+Examples of **correct** code for this rule:
+
+```js
+import Component from '@glimmer/component';
+
+class MyComponent extends Component {
+  foo() {
+    return this.args.bar + this.args.baz;
+  }
+}
+```
+
+```js
+import templateOnly from '@ember/component/template-only';
+
+export default templateOnly();
+```
+
+## References
+
+* [Glimmer Components - Octane Upgrade Guide](https://guides.emberjs.com/release/upgrading/current-edition/glimmer-components/)
+* [Glimmer Components RFC](https://emberjs.github.io/rfcs/0416-glimmer-components.html)
diff --git a/lib/index.js b/lib/index.js
index 8072837f..3b479940 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -29,6 +29,7 @@ module.exports = {
     'no-ember-super-in-es-classes': require('./rules/no-ember-super-in-es-classes'),
     'no-ember-testing-in-module-scope': require('./rules/no-ember-testing-in-module-scope'),
     'no-empty-attrs': require('./rules/no-empty-attrs'),
+    'no-empty-glimmer-component-classes': require('./rules/no-empty-glimmer-component-classes'),
     'no-function-prototype-extensions': require('./rules/no-function-prototype-extensions'),
     'no-get-with-default': require('./rules/no-get-with-default'),
     'no-get': require('./rules/no-get'),
diff --git a/lib/rules/no-empty-glimmer-component-classes.js b/lib/rules/no-empty-glimmer-component-classes.js
new file mode 100644
index 00000000..128e4198
--- /dev/null
+++ b/lib/rules/no-empty-glimmer-component-classes.js
@@ -0,0 +1,36 @@
+'use strict';
+
+const { isGlimmerComponent } = require('../utils/ember');
+
+const ERROR_MESSAGE = 'Do not create empty backing classes for Glimmer components.';
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    type: 'suggestion',
+    docs: {
+      description: 'disallow empty backing classes for Glimmer components',
+      category: 'Ember Octane',
+      recommended: false,
+      url:
+        'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/no-empty-glimmer-component-classes.md',
+    },
+    fixable: null,
+    schema: [],
+  },
+
+  ERROR_MESSAGE,
+
+  create(context) {
+    return {
+      ClassDeclaration(node) {
+        if (isGlimmerComponent(context, node) && node.body.body.length === 0) {
+          context.report(node, ERROR_MESSAGE);
+        }
+      },
+    };
+  },
+};


Test patch:
diff --git a/tests/lib/rules/no-empty-glimmer-component-classes.js b/tests/lib/rules/no-empty-glimmer-component-classes.js
new file mode 100644
index 00000000..d78a843b
--- /dev/null
+++ b/tests/lib/rules/no-empty-glimmer-component-classes.js
@@ -0,0 +1,48 @@
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/no-empty-glimmer-component-classes');
+const RuleTester = require('eslint').RuleTester;
+
+const { ERROR_MESSAGE } = rule;
+
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+  parserOptions: {
+    ecmaVersion: 6,
+    sourceType: 'module',
+  },
+});
+
+ruleTester.run('no-empty-glimmer-component-classes', rule, {
+  valid: [
+    `import Component from '@glimmer/component';
+
+    class MyComponent extends Component {
+      foo() {
+        return this.args.bar + this.args.baz;
+      }
+    }`,
+    'class MyComponent extends NotAGlimmerComponent {}',
+  ],
+  invalid: [
+    {
+      code: `import Component from '@glimmer/component';
+
+      class MyComponent extends Component {}`,
+      output: null,
+      errors: [{ message: ERROR_MESSAGE, type: 'ClassDeclaration' }],
+    },
+    {
+      code: `import Component from '@glimmer/component';
+
+      class MyComponent extends Component { /* foo */ }`,
+      output: null,
+      errors: [{ message: ERROR_MESSAGE, type: 'ClassDeclaration' }],
+    },
+  ],
+});