From fc1532a7786b632d98b443b4c176c28ceb79c388 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Tue, 17 Feb 2026 15:40:20 -0500 Subject: [PATCH] Extract rule: template-builtin-component-arguments --- README.md | 9 ++- .../template-builtin-component-arguments.md | 77 +++++++++++++++++++ .../template-builtin-component-arguments.js | 45 +++++++++++ .../template-builtin-component-arguments.js | 48 ++++++++++++ 4 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 docs/rules/template-builtin-component-arguments.md create mode 100644 lib/rules/template-builtin-component-arguments.js create mode 100644 tests/lib/rules/template-builtin-component-arguments.js diff --git a/README.md b/README.md index 1997fcd7ea..9ce688b8a4 100644 --- a/README.md +++ b/README.md @@ -176,10 +176,11 @@ rules in templates can be disabled with eslint directives with mustache or html ### Best Practices -| Name | Description | 💼 | 🔧 | 💡 | -| :--------------------------------------------------------- | :--------------------------------- | :- | :- | :- | -| [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-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-builtin-component-arguments.md b/docs/rules/template-builtin-component-arguments.md new file mode 100644 index 0000000000..72fba89b9e --- /dev/null +++ b/docs/rules/template-builtin-component-arguments.md @@ -0,0 +1,77 @@ +# ember/template-builtin-component-arguments + + + +✅ The `extends: 'recommended'` property in a configuration file enables this rule. + +The builtin `Input` component has several arguments that match attributes +of the lower-case `input` HTML element. These arguments should be set via e.g. +`@type`, instead of `type`, but it is easy to forget and can cause subtle +issues. + +This rule warns about `Input` component invocations that use the following attributes instead of arguments: + +- `checked` +- `type` +- `value` + +The builtin `Textarea` component has several arguments that match properties +of the lower-case `textarea` HTML element. These arguments should be set via e.g. +`@value`, instead of `value`, but it is easy to forget and can cause subtle +issues. + +This rule warns about `Textarea` component invocations that use the following attributes instead of arguments: + +- `value` + +Please note that this rule currently only warns about these three attributes on +the `Input`, and one property on the `Textarea` components, but might be extended in the future to also warn about +other attributes or builtin components. + +## Examples + +This rule **forbids** the following: + +```hbs + +``` + +```hbs + +``` + +```hbs + +``` + +This rule **allows** the following: + +```hbs + +``` + +```hbs + +``` + +```hbs + +``` + +```hbs + +``` + +## Migration + +- Add the `@` character in front of the relevant attributes to convert them + into component argument + +## Related Rules + +- [template-no-unknown-arguments-for-builtin-components](template-no-unknown-arguments-for-builtin-components.md) + +## References + +- [`Input` component API documentation](https://api.emberjs.com/ember/release/classes/Ember.Templates.components/methods/Input?anchor=Input) +- [`Textarea` component API documentation](https://api.emberjs.com/ember/release/classes/Ember.Templates.components/methods/Textarea?anchor=Textarea) diff --git a/lib/rules/template-builtin-component-arguments.js b/lib/rules/template-builtin-component-arguments.js new file mode 100644 index 0000000000..8e91f5f24c --- /dev/null +++ b/lib/rules/template-builtin-component-arguments.js @@ -0,0 +1,45 @@ +const FORBIDDEN_ATTRIBUTES = { + Input: new Set(['checked', 'type', 'value']), + Textarea: new Set(['value']), +}; + +function generateErrorMessage(component, attribute) { + return `Setting the \`${attribute}\` attribute on the builtin <${component}> component is not allowed. Did you mean \`@${attribute}\`?`; +} + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: 'suggestion', + docs: { + description: 'disallow setting certain attributes on builtin components', + category: 'Best Practices', + strictGjs: true, + strictGts: true, + url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-builtin-component-arguments.md', + }, + fixable: null, + schema: [], + messages: {}, + }, + + create(context) { + return { + GlimmerElementNode(node) { + const { tag, attributes } = node; + const forbiddenAttributes = FORBIDDEN_ATTRIBUTES[tag]; + + if (forbiddenAttributes && attributes) { + for (const attribute of attributes) { + if (attribute.name && forbiddenAttributes.has(attribute.name)) { + context.report({ + node: attribute, + message: generateErrorMessage(tag, attribute.name), + }); + } + } + } + }, + }; + }, +}; diff --git a/tests/lib/rules/template-builtin-component-arguments.js b/tests/lib/rules/template-builtin-component-arguments.js new file mode 100644 index 0000000000..09cfb15cf1 --- /dev/null +++ b/tests/lib/rules/template-builtin-component-arguments.js @@ -0,0 +1,48 @@ +const rule = require('../../../lib/rules/template-builtin-component-arguments'); +const RuleTester = require('eslint').RuleTester; + +const ruleTester = new RuleTester({ + parser: require.resolve('ember-eslint-parser'), + parserOptions: { ecmaVersion: 2022, sourceType: 'module' }, +}); + +ruleTester.run('template-builtin-component-arguments', rule, { + valid: [ + '', + '', + '', + '