Skip to content

Commit 52c8ff8

Browse files
committed
Extract rule: template-no-input-tagname
1 parent 0149ef1 commit 52c8ff8

File tree

4 files changed

+124
-0
lines changed

4 files changed

+124
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ rules in templates can be disabled with eslint directives with mustache or html
199199
| [template-no-capital-arguments](docs/rules/template-no-capital-arguments.md) | disallow capital arguments (use lowercase @arg instead of @Arg) | | | |
200200
| [template-no-debugger](docs/rules/template-no-debugger.md) | disallow {{debugger}} in templates | | | |
201201
| [template-no-element-event-actions](docs/rules/template-no-element-event-actions.md) | disallow element event actions (use {{on}} modifier instead) | | | |
202+
| [template-no-input-tagname](docs/rules/template-no-input-tagname.md) | disallow tagName attribute on {{input}} helper | | | |
202203
| [template-no-log](docs/rules/template-no-log.md) | disallow {{log}} in templates | | | |
203204

204205
### Components
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# ember/template-no-input-tagname
2+
3+
<!-- end auto-generated rule header -->
4+
5+
`{{input tagName=x}}` will result in obtuse errors. Typically, the input will simply fail to render, whether used in block form or inline. The only valid `tagName` for the input helper is `input`. For `textarea`, `button`, and other input-like elements, you should instead create a new component or better use the DOM!
6+
7+
## Examples
8+
9+
This rule **forbids** the following:
10+
11+
```gjs
12+
<template>
13+
{{input tagName='foo'}}
14+
{{input tagName=X}}
15+
{{component 'input' tagName='foo'}}
16+
{{component 'input' tagName=X}}
17+
{{yield (component 'input' tagName='foo')}}
18+
{{yield (component 'input' tagName=X)}}
19+
</template>
20+
```
21+
22+
## Related rules
23+
24+
- [no-link-to-tagname](no-link-to-tagname.md)
25+
- [no-unknown-arguments-for-builtin-components](no-unknown-arguments-for-builtin-components.md)
26+
27+
## References
28+
29+
- [Ember api/input component](https://api.emberjs.com/ember/release/classes/Ember.Templates.components/methods/Input?anchor=Input)
30+
- [rfcs/built in components](https://emberjs.github.io/rfcs/0459-angle-bracket-built-in-components.html)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/** @type {import('eslint').Rule.RuleModule} */
2+
module.exports = {
3+
meta: {
4+
type: 'problem',
5+
docs: {
6+
description: 'disallow tagName attribute on {{input}} helper',
7+
category: 'Best Practices',
8+
strictGjs: true,
9+
strictGts: true,
10+
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-input-tagname.md',
11+
},
12+
schema: [],
13+
messages: { unexpected: 'Unexpected tagName usage on input helper.' },
14+
},
15+
create(context) {
16+
function check(node) {
17+
if (!node.path) {
18+
return;
19+
}
20+
const attrs = node.hash?.pairs || [];
21+
const hasTagName = attrs.some((a) => a.key === 'tagName');
22+
23+
if (node.path.original === 'input' && hasTagName) {
24+
context.report({ node, messageId: 'unexpected' });
25+
} else if (
26+
node.path.original === 'component' &&
27+
node.params?.[0]?.original === 'input' &&
28+
hasTagName
29+
) {
30+
context.report({ node, messageId: 'unexpected' });
31+
}
32+
}
33+
return {
34+
GlimmerMustacheStatement: check,
35+
GlimmerSubExpression: check,
36+
};
37+
},
38+
};
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
const rule = require('../../../lib/rules/template-no-input-tagname');
2+
const RuleTester = require('eslint').RuleTester;
3+
4+
const ruleTester = new RuleTester({
5+
parser: require.resolve('ember-eslint-parser'),
6+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
7+
});
8+
ruleTester.run('template-no-input-tagname', rule, {
9+
valid: [
10+
'<template>{{input value=this.foo}}</template>',
11+
// Test cases ported from ember-template-lint
12+
'<template>{{input type="text"}}</template>',
13+
'<template>{{component "input" type="text"}}</template>',
14+
'<template>{{yield (component "input" type="text")}}</template>',
15+
],
16+
invalid: [
17+
{
18+
code: '<template>{{input tagName="span"}}</template>',
19+
output: null,
20+
errors: [{ messageId: 'unexpected' }],
21+
},
22+
23+
// Test cases ported from ember-template-lint
24+
{
25+
code: '<template>{{input tagName="foo"}}</template>',
26+
output: null,
27+
errors: [{ messageId: 'unexpected' }],
28+
},
29+
{
30+
code: '<template>{{input tagName=bar}}</template>',
31+
output: null,
32+
errors: [{ messageId: 'unexpected' }],
33+
},
34+
{
35+
code: '<template>{{component "input" tagName="foo"}}</template>',
36+
output: null,
37+
errors: [{ messageId: 'unexpected' }],
38+
},
39+
{
40+
code: '<template>{{component "input" tagName=bar}}</template>',
41+
output: null,
42+
errors: [{ messageId: 'unexpected' }],
43+
},
44+
{
45+
code: '<template>{{yield (component "input" tagName="foo")}}</template>',
46+
output: null,
47+
errors: [{ messageId: 'unexpected' }],
48+
},
49+
{
50+
code: '<template>{{yield (component "input" tagName=bar)}}</template>',
51+
output: null,
52+
errors: [{ messageId: 'unexpected' }],
53+
},
54+
],
55+
});

0 commit comments

Comments
 (0)