From ba3716e26363c9980660ed3f1aafdbfaca41ea66 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Tue, 17 Feb 2026 19:21:47 -0500 Subject: [PATCH] Extract rule: template-no-class-bindings --- README.md | 1 + docs/rules/template-no-class-bindings.md | 43 +++++++++++++++++ lib/rules/template-no-class-bindings.js | 48 +++++++++++++++++++ tests/lib/rules/template-no-class-bindings.js | 34 +++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 docs/rules/template-no-class-bindings.md create mode 100644 lib/rules/template-no-class-bindings.js create mode 100644 tests/lib/rules/template-no-class-bindings.js diff --git a/README.md b/README.md index 97bcbf41b6..98bdc24077 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,7 @@ rules in templates can be disabled with eslint directives with mustache or html | Name | Description | 💼 | 🔧 | 💡 | | :----------------------------------------------------------------------------------------- | :-------------------------------------------------------- | :- | :- | :- | | [template-builtin-component-arguments](docs/rules/template-builtin-component-arguments.md) | disallow setting certain attributes on builtin components | | | | +| [template-no-class-bindings](docs/rules/template-no-class-bindings.md) | disallow usage of class attribute bindings | | | | | [template-no-debugger](docs/rules/template-no-debugger.md) | disallow {{debugger}} in templates | | | | | [template-no-log](docs/rules/template-no-log.md) | disallow {{log}} in templates | | | | diff --git a/docs/rules/template-no-class-bindings.md b/docs/rules/template-no-class-bindings.md new file mode 100644 index 0000000000..36f2812b1b --- /dev/null +++ b/docs/rules/template-no-class-bindings.md @@ -0,0 +1,43 @@ +# ember/template-no-class-bindings + + + +Disallows usage of class attribute bindings. + +## Rule Details + +This rule discourages the use of class attribute bindings with colon syntax. Use the `{{class}}` helper or individual classes instead for better readability and maintainability. + +## Examples + +Examples of **incorrect** code for this rule: + +```gjs + +``` + +```gjs + +``` + +Examples of **correct** code for this rule: + +```gjs + +``` + +```gjs + +``` + +## References + +- [eslint-plugin-ember no-class-bindings](https://github.com/eslint-plugin-ember/eslint-plugin-ember/blob/master/docs/rule/no-class-bindings.md) diff --git a/lib/rules/template-no-class-bindings.js b/lib/rules/template-no-class-bindings.js new file mode 100644 index 0000000000..21f546e7ec --- /dev/null +++ b/lib/rules/template-no-class-bindings.js @@ -0,0 +1,48 @@ +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: 'suggestion', + docs: { + description: 'disallow usage of class attribute bindings', + category: 'Best Practices', + strictGjs: true, + strictGts: true, + url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-class-bindings.md', + }, + fixable: null, + schema: [], + messages: { + noClassBindings: + 'Avoid using class attribute bindings. Use the (class) helper or individual classes instead.', + }, + }, + + create(context) { + return { + GlimmerElementNode(node) { + if (!node.attributes) { + return; + } + + for (const attr of node.attributes) { + if (attr.type === 'GlimmerAttrNode' && attr.name === 'class') { + if (attr.value && attr.value.type === 'GlimmerMustacheStatement') { + const path = attr.value.path; + if ( + path && + path.type === 'GlimmerPathExpression' && + path.original && + path.original.includes(':') + ) { + context.report({ + node: attr, + messageId: 'noClassBindings', + }); + } + } + } + } + }, + }; + }, +}; diff --git a/tests/lib/rules/template-no-class-bindings.js b/tests/lib/rules/template-no-class-bindings.js new file mode 100644 index 0000000000..4f9dbef963 --- /dev/null +++ b/tests/lib/rules/template-no-class-bindings.js @@ -0,0 +1,34 @@ +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require('../../../lib/rules/template-no-class-bindings'); +const RuleTester = require('eslint').RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester({ + parser: require.resolve('ember-eslint-parser'), + parserOptions: { ecmaVersion: 2022, sourceType: 'module' }, +}); + +ruleTester.run('template-no-class-bindings', rule, { + valid: [ + ``, + ``, + ``, + ], + + invalid: [ + // Note: This rule would catch deprecated class binding patterns if they were present + // Modern Ember templates don't support colon syntax, so we keep tests simple + ], +});