Skip to content

Commit 32c6a86

Browse files
Merge pull request #2412 from NullVoxPopuli/nvp/template-lint-extract-rule-template-no-block-params-for-html-elements
Extract rule: template-no-block-params-for-html-elements
2 parents 3e664f5 + e0150b8 commit 32c6a86

File tree

4 files changed

+185
-8
lines changed

4 files changed

+185
-8
lines changed

README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -189,14 +189,15 @@ rules in templates can be disabled with eslint directives with mustache or html
189189

190190
### Best Practices
191191

192-
| Name | Description | 💼 | 🔧 | 💡 |
193-
| :----------------------------------------------------------------------------------------------- | :-------------------------------------------------------- | :- | :- | :- |
194-
| [template-builtin-component-arguments](docs/rules/template-builtin-component-arguments.md) | disallow setting certain attributes on builtin components | | | |
195-
| [template-no-action-modifiers](docs/rules/template-no-action-modifiers.md) | disallow usage of {{action}} modifiers | | | |
196-
| [template-no-arguments-for-html-elements](docs/rules/template-no-arguments-for-html-elements.md) | disallow @arguments on HTML elements | | | |
197-
| [template-no-array-prototype-extensions](docs/rules/template-no-array-prototype-extensions.md) | disallow usage of Ember Array prototype extensions | | | |
198-
| [template-no-debugger](docs/rules/template-no-debugger.md) | disallow {{debugger}} in templates | | | |
199-
| [template-no-log](docs/rules/template-no-log.md) | disallow {{log}} in templates | | | |
192+
| Name | Description | 💼 | 🔧 | 💡 |
193+
| :----------------------------------------------------------------------------------------------------- | :-------------------------------------------------------- | :- | :- | :- |
194+
| [template-builtin-component-arguments](docs/rules/template-builtin-component-arguments.md) | disallow setting certain attributes on builtin components | | | |
195+
| [template-no-action-modifiers](docs/rules/template-no-action-modifiers.md) | disallow usage of {{action}} modifiers | | | |
196+
| [template-no-arguments-for-html-elements](docs/rules/template-no-arguments-for-html-elements.md) | disallow @arguments on HTML elements | | | |
197+
| [template-no-array-prototype-extensions](docs/rules/template-no-array-prototype-extensions.md) | disallow usage of Ember Array prototype extensions | | | |
198+
| [template-no-block-params-for-html-elements](docs/rules/template-no-block-params-for-html-elements.md) | disallow block params on HTML elements | | | |
199+
| [template-no-debugger](docs/rules/template-no-debugger.md) | disallow {{debugger}} in templates | | | |
200+
| [template-no-log](docs/rules/template-no-log.md) | disallow {{log}} in templates | | | |
200201

201202
### Components
202203

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# ember/template-no-block-params-for-html-elements
2+
3+
<!-- end auto-generated rule header -->
4+
5+
Disallow block params on HTML elements.
6+
7+
Block params (using the `as |param|` syntax) are a feature specific to Ember components and block helpers. They should not be used on regular HTML elements.
8+
9+
## Rule Details
10+
11+
This rule disallows using block params on HTML elements. Use components if you need to pass block params.
12+
13+
## Examples
14+
15+
### Incorrect ❌
16+
17+
```gjs
18+
<template>
19+
<div as |content|>
20+
{{content}}
21+
</div>
22+
</template>
23+
```
24+
25+
```gjs
26+
<template>
27+
<section as |data|>
28+
<p>{{data}}</p>
29+
</section>
30+
</template>
31+
```
32+
33+
```gjs
34+
<template>
35+
<ul as |items|>
36+
<li>{{items}}</li>
37+
</ul>
38+
</template>
39+
```
40+
41+
### Correct ✅
42+
43+
```gjs
44+
<template>
45+
<div>Content</div>
46+
</template>
47+
```
48+
49+
```gjs
50+
<template>
51+
<MyComponent as |item|>
52+
{{item.name}}
53+
</MyComponent>
54+
</template>
55+
```
56+
57+
```gjs
58+
<template>
59+
{{#each this.items as |item|}}
60+
<li>{{item}}</li>
61+
{{/each}}
62+
</template>
63+
```
64+
65+
## Related Rules
66+
67+
- [template-no-arguments-for-html-elements](./template-no-arguments-for-html-elements.md)
68+
69+
## References
70+
71+
- [Ember Guides - Block Content](https://guides.emberjs.com/release/components/block-content/)
72+
- [eslint-plugin-ember template-no-yield-only](https://github.com/ember-cli/eslint-plugin-ember/blob/master/docs/rules/template-no-yield-only.md)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/** @type {import('eslint').Rule.RuleModule} */
2+
const htmlTags = require('html-tags');
3+
4+
module.exports = {
5+
meta: {
6+
type: 'problem',
7+
docs: {
8+
description: 'disallow block params on HTML elements',
9+
category: 'Best Practices',
10+
strictGjs: true,
11+
strictGts: true,
12+
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-block-params-for-html-elements.md',
13+
},
14+
fixable: null,
15+
schema: [],
16+
messages: {
17+
noBlockParamsForHtmlElements:
18+
'Block params can only be used with components, not HTML elements.',
19+
},
20+
},
21+
22+
create(context) {
23+
const sourceCode = context.sourceCode;
24+
const HTML_ELEMENTS = new Set(htmlTags);
25+
26+
return {
27+
GlimmerElementNode(node) {
28+
// Check if this is an HTML element (lowercase)
29+
if (!HTML_ELEMENTS.has(node.tag)) {
30+
return;
31+
}
32+
33+
// If the tag name is a variable in scope, it's being used as a component, not an HTML element
34+
const scope = sourceCode.getScope(node.parent);
35+
const isVariable = scope.references.some((ref) => ref.identifier === node.parts[0]);
36+
if (isVariable) {
37+
return;
38+
}
39+
40+
// Check for block params
41+
if (node.blockParams && node.blockParams.length > 0) {
42+
context.report({
43+
node,
44+
messageId: 'noBlockParamsForHtmlElements',
45+
});
46+
}
47+
},
48+
};
49+
},
50+
};
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
const rule = require('../../../lib/rules/template-no-block-params-for-html-elements');
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+
9+
ruleTester.run('template-no-block-params-for-html-elements', rule, {
10+
valid: [
11+
`let div = <template>{{yield "hello"}}</template>;
12+
<template>
13+
<div as |greeting|>{{greeting}}</div>
14+
</template>
15+
`,
16+
'<template><div>Content</div></template>',
17+
'<template><MyComponent as |item|>{{item.name}}</MyComponent></template>',
18+
'<template>{{#each this.items as |item|}}<li>{{item}}</li>{{/each}}</template>',
19+
'<template><button>Click</button></template>',
20+
],
21+
22+
invalid: [
23+
{
24+
code: '<template><div as |content|>{{content}}</div></template>',
25+
output: null,
26+
errors: [
27+
{
28+
message: 'Block params can only be used with components, not HTML elements.',
29+
type: 'GlimmerElementNode',
30+
},
31+
],
32+
},
33+
{
34+
code: '<template><section as |data|><p>{{data}}</p></section></template>',
35+
output: null,
36+
errors: [
37+
{
38+
message: 'Block params can only be used with components, not HTML elements.',
39+
type: 'GlimmerElementNode',
40+
},
41+
],
42+
},
43+
{
44+
code: '<template><ul as |items|><li>{{items}}</li></ul></template>',
45+
output: null,
46+
errors: [
47+
{
48+
message: 'Block params can only be used with components, not HTML elements.',
49+
type: 'GlimmerElementNode',
50+
},
51+
],
52+
},
53+
],
54+
});

0 commit comments

Comments
 (0)