diff --git a/README.md b/README.md index 97bcbf41b6..a3ded26aca 100644 --- a/README.md +++ b/README.md @@ -182,11 +182,12 @@ rules in templates can be disabled with eslint directives with mustache or html ### Best Practices -| Name | Description | 💼 | 🔧 | 💡 | -| :----------------------------------------------------------------------------------------- | :-------------------------------------------------------- | :- | :- | :- | -| [template-builtin-component-arguments](docs/rules/template-builtin-component-arguments.md) | disallow setting certain attributes on builtin components | | | | -| [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 | | | | +| Name | Description | 💼 | 🔧 | 💡 | +| :------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------- | :- | :- | :- | +| [template-builtin-component-arguments](docs/rules/template-builtin-component-arguments.md) | disallow setting certain attributes on builtin components | | | | +| [template-no-attribute-splat-on-html-element](docs/rules/template-no-attribute-splat-on-html-element.md) | disallow ...attributes on HTML elements | | | | +| [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 | | | | ### Components diff --git a/docs/rules/template-no-attribute-splat-on-html-element.md b/docs/rules/template-no-attribute-splat-on-html-element.md new file mode 100644 index 0000000000..5b79d135f3 --- /dev/null +++ b/docs/rules/template-no-attribute-splat-on-html-element.md @@ -0,0 +1,43 @@ +# ember/template-no-attribute-splat-on-html-element + + + +Disallows using `...attributes` on HTML elements. + +## Rule Details + +The `...attributes` syntax should only be used on component elements, not on HTML elements, to avoid confusion and maintain clear component boundaries. + +## Examples + +Examples of **incorrect** code for this rule: + +```gjs + +``` + +```gjs + +``` + +Examples of **correct** code for this rule: + +```gjs + +``` + +```gjs + +``` + +## References + +- [eslint-plugin-ember no-attrs-in-components](https://github.com/eslint-plugin-ember/eslint-plugin-ember/blob/master/docs/rule/no-attrs-in-components.md) diff --git a/lib/rules/template-no-attribute-splat-on-html-element.js b/lib/rules/template-no-attribute-splat-on-html-element.js new file mode 100644 index 0000000000..11c8c9a580 --- /dev/null +++ b/lib/rules/template-no-attribute-splat-on-html-element.js @@ -0,0 +1,39 @@ +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: 'suggestion', + docs: { + description: 'disallow ...attributes on HTML elements', + category: 'Best Practices', + strictGjs: true, + strictGts: true, + url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-attribute-splat-on-html-element.md', + }, + fixable: null, + schema: [], + messages: { + noAttributeSplat: + 'Do not use ...attributes on HTML elements. Use it only on component elements.', + }, + }, + + create(context) { + return { + GlimmerElementNode(node) { + // Check if it's an HTML element (starts with lowercase letter) + if (node.tag && node.tag.length > 0 && node.tag[0] === node.tag[0].toLowerCase()) { + if (node.attributes) { + for (const attr of node.attributes) { + if (attr.name === '...attributes') { + context.report({ + node: attr, + messageId: 'noAttributeSplat', + }); + } + } + } + } + }, + }; + }, +}; diff --git a/tests/lib/rules/template-no-attribute-splat-on-html-element.js b/tests/lib/rules/template-no-attribute-splat-on-html-element.js new file mode 100644 index 0000000000..9eba81d8ff --- /dev/null +++ b/tests/lib/rules/template-no-attribute-splat-on-html-element.js @@ -0,0 +1,68 @@ +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require('../../../lib/rules/template-no-attribute-splat-on-html-element'); +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-attribute-splat-on-html-element', rule, { + valid: [ + ``, + ``, + ``, + ], + + invalid: [ + { + code: ``, + output: null, + errors: [ + { + message: 'Do not use ...attributes on HTML elements. Use it only on component elements.', + type: 'GlimmerAttrNode', + }, + ], + }, + { + code: ``, + output: null, + errors: [ + { + message: 'Do not use ...attributes on HTML elements. Use it only on component elements.', + type: 'GlimmerAttrNode', + }, + ], + }, + { + code: ``, + output: null, + errors: [ + { + message: 'Do not use ...attributes on HTML elements. Use it only on component elements.', + type: 'GlimmerAttrNode', + }, + ], + }, + ], +});