Skip to content

Commit d2b64e0

Browse files
committed
Sync with template-lint
1 parent 66a70c1 commit d2b64e0

3 files changed

Lines changed: 105 additions & 144 deletions

File tree

docs/rules/template-no-unused-block-params.md

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,49 +2,47 @@
22

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

5-
Disallow unused block parameters in templates.
6-
7-
## Rule Details
8-
9-
This rule reports block parameters that are declared but never used within the block.
5+
This rule forbids unused block parameters except when they are needed to access a later parameter.
106

117
## Examples
128

13-
Examples of **incorrect** code for this rule:
9+
This rule **forbids** the following (unused parameters):
1410

1511
```gjs
1612
<template>
17-
{{#each items as |item|}}
18-
Hello
19-
{{/each}}
20-
</template>
21-
22-
<template>
23-
{{#each items as |item index|}}
24-
{{item.name}}
13+
{{#each users as |user index|}}
14+
{{user.name}}
2515
{{/each}}
2616
</template>
2717
```
2818

29-
Examples of **correct** code for this rule:
19+
This rule **allows** the following:
20+
21+
Allowed (used parameters):
3022

3123
```gjs
3224
<template>
33-
{{#each items as |item|}}
34-
{{item.name}}
25+
{{#each users as |user|}}
26+
{{user.name}}
3527
{{/each}}
3628
</template>
29+
```
3730

31+
```gjs
3832
<template>
39-
{{#each items as |item index|}}
40-
{{index}}: {{item.name}}
33+
{{#each users as |user index|}}
34+
{{index}} {{user.name}}
4135
{{/each}}
4236
</template>
37+
```
4338

39+
Allowed (later parameter used):
40+
41+
```gjs
4442
<template>
45-
{{#let user as |u|}}
46-
{{u.name}}
47-
{{/let}}
43+
{{#each users as |user index|}}
44+
{{index}}
45+
{{/each}}
4846
</template>
4947
```
5048

@@ -54,4 +52,6 @@ Examples of **correct** code for this rule:
5452

5553
## References
5654

57-
- [Ember Guides - Block Parameters](https://guides.emberjs.com/release/components/block-content/)
55+
- [Ember guides/block content](https://guides.emberjs.com/release/components/block-content/)
56+
- [rfcs/angle bracket invocation](https://emberjs.github.io/rfcs/0311-angle-bracket-invocation.html)
57+
- [rfcs/named blocks](https://emberjs.github.io/rfcs/0226-named-blocks.html)

lib/rules/template-no-unused-block-params.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ module.exports = {
6363
},
6464
schema: [],
6565
messages: {
66-
unusedBlockParam: 'Block param "{{param}}" is unused',
66+
unusedBlockParam: "'{{param}}' is defined but never used",
6767
},
6868
originallyFrom: {
6969
name: 'ember-template-lint',
@@ -134,12 +134,13 @@ module.exports = {
134134

135135
// Only report trailing unused params (after the last used one)
136136
const unusedTrailing = blockParams.slice(lastUsedIndex + 1);
137+
const firstUnusedTrailing = unusedTrailing[0];
137138

138-
if (unusedTrailing.length > 0) {
139+
if (firstUnusedTrailing) {
139140
context.report({
140141
node,
141142
messageId: 'unusedBlockParam',
142-
data: { param: unusedTrailing.join(', ') },
143+
data: { param: firstUnusedTrailing },
143144
});
144145
}
145146
},

tests/lib/rules/template-no-unused-block-params.js

Lines changed: 78 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -3,137 +3,64 @@ const rule = require('../../../lib/rules/template-no-unused-block-params');
33

44
const { RuleTester } = eslint;
55

6-
const ruleTester = new RuleTester({
7-
parser: require.resolve('ember-eslint-parser'),
8-
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
9-
});
10-
11-
ruleTester.run('template-no-unused-block-params', rule, {
12-
valid: [
13-
// All params used
14-
'<template>{{#each items as |item|}}{{item.name}}{{/each}}</template>',
15-
'<template>{{#each items as |item index|}}{{index}}: {{item}}{{/each}}</template>',
16-
17-
// No block params
18-
'<template>{{#each items}}{{this}}{{/each}}</template>',
19-
20-
// Param used in nested path
21-
'<template>{{#let user as |u|}}{{u.name}}{{/let}}</template>',
22-
23-
'<template>{{cat}}</template>',
24-
'<template>{{#each cats as |cat|}}{{cat}}{{/each}}</template>',
25-
'<template>{{#each cats as |cat|}}{{partial "cat"}}{{/each}}</template>',
26-
'<template>{{#each cats as |cat|}}{{cat.name}}{{/each}}</template>',
27-
'<template>{{#each cats as |cat|}}{{meow cat}}{{/each}}</template>',
28-
'<template>{{#each cats as |cat index|}}{{index}}{{/each}}</template>',
29-
'<template>{{#each cats as |cat index|}}{{#each cat.lives as |life|}}{{index}}: {{life}}{{/each}}{{/each}}</template>',
30-
'<template>{{#each cats as |cat|}}{{#meow-meow cat as |cat|}}{{cat}}{{/meow-meow}}{{/each}}</template>',
31-
'<template>{{#with (component "foo-bar") as |FooBar|}}<FooBar />{{/with}}</template>',
32-
'<template><BurgerMenu as |menu|><header>Something</header><menu.item>Text</menu.item></BurgerMenu></template>',
33-
'<template>{{#burger-menu as |menu|}}<header>Something</header>{{#menu.item}}Text{{/menu.item}}{{/burger-menu}}</template>',
34-
],
35-
invalid: [
36-
{
37-
code: '<template>{{#each items as |item|}}Hello{{/each}}</template>',
38-
output: null,
39-
errors: [{ messageId: 'unusedBlockParam', data: { param: 'item' } }],
40-
},
41-
{
42-
code: '<template>{{#each items as |item index|}}{{item}}{{/each}}</template>',
43-
output: null,
44-
errors: [{ messageId: 'unusedBlockParam', data: { param: 'index' } }],
45-
},
46-
{
47-
code: '<template>{{#let value as |v|}}Something{{/let}}</template>',
48-
output: null,
49-
errors: [{ messageId: 'unusedBlockParam', data: { param: 'v' } }],
50-
},
51-
52-
{
53-
code: '<template>{{#each cats as |cat|}}Dogs{{/each}}</template>',
54-
output: null,
55-
errors: [{ messageId: 'unusedBlockParam' }],
56-
},
57-
{
58-
code: '<template>{{#each cats as |cat index|}}{{cat}}{{/each}}</template>',
59-
output: null,
60-
errors: [{ messageId: 'unusedBlockParam' }],
61-
},
62-
{
63-
code: '<template>{{#each cats as |cat index|}}{{/each}}</template>',
64-
output: null,
65-
errors: [{ messageId: 'unusedBlockParam' }],
66-
},
67-
],
68-
});
69-
70-
const hbsRuleTester = new RuleTester({
71-
parser: require.resolve('ember-eslint-parser/hbs'),
72-
parserOptions: {
73-
ecmaVersion: 2022,
74-
sourceType: 'module',
75-
},
76-
});
77-
78-
hbsRuleTester.run('template-no-unused-block-params', rule, {
79-
valid: [
80-
'{{cat}}',
81-
'{{#each cats as |cat|}}{{cat}}{{/each}}',
82-
'{{#each cats as |cat|}}{{partial "cat"}}{{/each}}',
83-
'{{#each cats as |cat|}}{{cat.name}}{{/each}}',
84-
'{{#each cats as |cat|}}{{meow cat}}{{/each}}',
85-
'{{#each cats as |cat index|}}{{index}}{{/each}}',
86-
'{{#each cats as |cat index|}}{{#each cat.lives as |life|}}{{index}}: {{life}}{{/each}}{{/each}}',
87-
`
6+
const validHbs = [
7+
'{{cat}}',
8+
'{{#each cats as |cat|}}{{cat}}{{/each}}',
9+
'{{#each cats as |cat|}}{{partial "cat"}}{{/each}}',
10+
'{{#each cats as |cat|}}{{cat.name}}{{/each}}',
11+
'{{#each cats as |cat|}}{{meow cat}}{{/each}}',
12+
'{{#each cats as |cat index|}}{{index}}{{/each}}',
13+
'{{#each cats as |cat index|}}{{#each cat.lives as |life|}}{{index}}: {{life}}{{/each}}{{/each}}',
14+
`
8815
<MyComponent @model={{this.model}} as |param|>
8916
{{! template-lint-disable }}
9017
<MyOtherComponent .... @param={{param}} />
9118
{{! template-lint-enable }}
9219
</MyComponent>
9320
`,
94-
`
21+
`
9522
<MyComponent @model={{this.model}} as |param|>
9623
{{! template-lint-disable }}
9724
{{foo-bar param}}
9825
{{! template-lint-enable }}
9926
</MyComponent>
10027
`,
101-
`
28+
`
10229
<MyComponent @model={{this.model}} as |param|>
10330
{{! template-lint-disable }}
10431
{{param}}
10532
{{! template-lint-enable }}
10633
</MyComponent>
10734
`,
108-
`
35+
`
10936
<MyComponent @model={{this.model}} as |param|>
11037
{{! template-lint-disable }}
11138
{{foo-bar prop=param}}
11239
{{! template-lint-enable }}
11340
</MyComponent>
11441
`,
115-
`
42+
`
11643
{{#my-component as |param|}}
11744
{{! template-lint-disable }}
11845
<MyOtherComponent .... @param={{param}} />
11946
{{! template-lint-enable }}
12047
{{/my-component}}
12148
`,
122-
`
49+
`
12350
{{#my-component as |param|}}
12451
{{! template-lint-disable }}
12552
{{foo-bar param}}
12653
{{! template-lint-enable }}
12754
{{/my-component}}
12855
`,
129-
`
56+
`
13057
{{#my-component as |param|}}
13158
{{! template-lint-disable }}
13259
{{param}}
13360
{{! template-lint-enable }}
13461
{{/my-component}}
13562
`,
136-
`
63+
`
13764
{{#my-component as |param bar baz|}}
13865
{{! template-lint-disable }}
13966
{{foo-bar prop=param}}
@@ -144,33 +71,66 @@ hbsRuleTester.run('template-no-unused-block-params', rule, {
14471
{{! template-lint-enable }}
14572
{{/my-component}}
14673
`,
147-
'{{#each cats as |cat|}}{{#meow-meow cat as |cat|}}{{cat}}{{/meow-meow}}{{/each}}',
148-
'{{#with (component "foo-bar") as |FooBar|}}<FooBar />{{/with}}',
149-
'<BurgerMenu as |menu|><header>Something</header><menu.item>Text</menu.item></BurgerMenu>',
150-
'{{#burger-menu as |menu|}}<header>Something</header>{{#menu.item}}Text{{/menu.item}}{{/burger-menu}}',
151-
],
152-
invalid: [
153-
{
154-
code: '{{#each cats as |cat|}}Dogs{{/each}}',
155-
output: null,
156-
errors: [{ message: 'Block param "cat" is unused' }],
157-
},
158-
{
159-
code: '{{#each cats as |cat index|}}{{cat}}{{/each}}',
160-
output: null,
161-
errors: [{ message: 'Block param "index" is unused' }],
162-
},
163-
{
164-
// Outer `index` is shadowed by inner `index`, so outer `index` is unused
165-
code: '{{#each cats as |cat index|}}{{#each cat.lives as |life index|}}{{index}}: {{life}}{{/each}}{{/each}}',
166-
output: null,
167-
errors: [{ message: 'Block param "index" is unused' }],
168-
},
169-
{
170-
// `partial` marks outer params as used, but inner `life` is unused
171-
code: '{{#each cats as |cat index|}}{{partial "cat"}}{{#each cat.lives as |life|}}Life{{/each}}{{/each}}',
172-
output: null,
173-
errors: [{ message: 'Block param "life" is unused' }],
174-
},
175-
],
74+
'{{#each cats as |cat|}}{{#meow-meow cat as |cat|}}{{cat}}{{/meow-meow}}{{/each}}',
75+
'{{#with (component "foo-bar") as |FooBar|}}<FooBar />{{/with}}',
76+
'<BurgerMenu as |menu|><header>Something</header><menu.item>Text</menu.item></BurgerMenu>',
77+
'{{#burger-menu as |menu|}}<header>Something</header>{{#menu.item}}Text{{/menu.item}}{{/burger-menu}}',
78+
];
79+
80+
const invalidHbs = [
81+
{
82+
code: '{{#each cats as |cat|}}Dogs{{/each}}',
83+
output: null,
84+
errors: [{ messageId: 'unusedBlockParam', data: { param: 'cat' } }],
85+
},
86+
{
87+
code: '{{#each cats as |cat index|}}{{cat}}{{/each}}',
88+
output: null,
89+
errors: [{ messageId: 'unusedBlockParam', data: { param: 'index' } }],
90+
},
91+
{
92+
code: '{{#each cats as |cat index|}}{{#each cat.lives as |life index|}}{{index}}: {{life}}{{/each}}{{/each}}',
93+
output: null,
94+
errors: [{ messageId: 'unusedBlockParam', data: { param: 'index' } }],
95+
},
96+
{
97+
code: '{{#each cats as |cat index|}}{{partial "cat"}}{{#each cat.lives as |life|}}Life{{/each}}{{/each}}',
98+
output: null,
99+
errors: [{ messageId: 'unusedBlockParam', data: { param: 'life' } }],
100+
},
101+
];
102+
103+
function wrapTemplate(entry) {
104+
if (typeof entry === 'string') {
105+
return `<template>${entry}</template>`;
106+
}
107+
108+
return {
109+
...entry,
110+
code: `<template>${entry.code}</template>`,
111+
output: entry.output ? `<template>${entry.output}</template>` : entry.output,
112+
};
113+
}
114+
115+
const gjsRuleTester = new RuleTester({
116+
parser: require.resolve('ember-eslint-parser'),
117+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
118+
});
119+
120+
gjsRuleTester.run('template-no-unused-block-params', rule, {
121+
valid: validHbs.map(wrapTemplate),
122+
invalid: invalidHbs.map(wrapTemplate),
123+
});
124+
125+
const hbsRuleTester = new RuleTester({
126+
parser: require.resolve('ember-eslint-parser/hbs'),
127+
parserOptions: {
128+
ecmaVersion: 2022,
129+
sourceType: 'module',
130+
},
131+
});
132+
133+
hbsRuleTester.run('template-no-unused-block-params', rule, {
134+
valid: validHbs,
135+
invalid: invalidHbs,
176136
});

0 commit comments

Comments
 (0)