Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
77 changes: 77 additions & 0 deletions docs/rules/template-builtin-component-arguments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# ember/template-builtin-component-arguments

<!-- end auto-generated rule header -->

✅ 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
<Input type='text' size='10' />
```

```hbs
<Input @type='checkbox' checked />
```

```hbs
<Textarea value="Hello, Tom!" /></Textarea>
```

This rule **allows** the following:

```hbs
<input type='text' size='10' />
```

```hbs
<Input @type='text' size='10' />
```

```hbs
<Input @type='checkbox' @checked={{true}} />
```

```hbs
<Textarea @value="Hello, Tom!" /></Textarea>
```

## 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)
45 changes: 45 additions & 0 deletions lib/rules/template-builtin-component-arguments.js
Original file line number Diff line number Diff line change
@@ -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),
});
}
}
}
},
};
},
};
48 changes: 48 additions & 0 deletions tests/lib/rules/template-builtin-component-arguments.js
Original file line number Diff line number Diff line change
@@ -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: [
'<template><input type="text" size="10" /></template>',
'<template><Input @type="text" size="10" /></template>',
'<template><Input @type="checkbox" @checked={{true}} /></template>',
'<template><Textarea @value="Tomster" /></template>',
],
invalid: [
{
code: '<template><Input type="text" size="10" /></template>',
output: null,
errors: [
{
message:
'Setting the `type` attribute on the builtin <Input> component is not allowed. Did you mean `@type`?',
},
],
},
{
code: '<template><Input @type="checkbox" checked /></template>',
output: null,
errors: [
{
message:
'Setting the `checked` attribute on the builtin <Input> component is not allowed. Did you mean `@checked`?',
},
],
},
{
code: '<template><Textarea value="Tomster" /></template>',
output: null,
errors: [
{
message:
'Setting the `value` attribute on the builtin <Textarea> component is not allowed. Did you mean `@value`?',
},
],
},
],
});
Loading