Skip to content

Commit 2ab29ca

Browse files
committed
Address PR Feedback
1 parent 9830b2e commit 2ab29ca

3 files changed

Lines changed: 101 additions & 22 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ rules in templates can be disabled with eslint directives with mustache or html
204204
| [template-no-action-on-submit-button](docs/rules/template-no-action-on-submit-button.md) | disallow action attribute on submit buttons | | | |
205205
| [template-no-arguments-for-html-elements](docs/rules/template-no-arguments-for-html-elements.md) | disallow @arguments on HTML elements | | | |
206206
| [template-no-array-prototype-extensions](docs/rules/template-no-array-prototype-extensions.md) | disallow usage of Ember Array prototype extensions | | | |
207-
| [template-no-bare-yield](docs/rules/template-no-bare-yield.md) | disallow {{yield}} without parameters outside of contextual components | | | |
207+
| [template-no-bare-yield](docs/rules/template-no-bare-yield.md) | disallow templates whose only meaningful content is a bare {{yield}} | | | |
208208
| [template-no-block-params-for-html-elements](docs/rules/template-no-block-params-for-html-elements.md) | disallow block params on HTML elements | | | |
209209
| [template-no-capital-arguments](docs/rules/template-no-capital-arguments.md) | disallow capital arguments (use lowercase @arg instead of @Arg) | | | |
210210
| [template-no-chained-this](docs/rules/template-no-chained-this.md) | disallow redundant `this.this` in templates | | 🔧 | |

lib/rules/template-no-bare-yield.js

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,62 @@
1+
function isEmptyNode(node) {
2+
return (
3+
node.type === 'GlimmerMustacheCommentStatement' ||
4+
node.type === 'GlimmerCommentStatement' ||
5+
(node.type === 'GlimmerTextNode' && !node.chars.trim())
6+
);
7+
}
8+
9+
function isBareYield(node) {
10+
return (
11+
node.type === 'GlimmerMustacheStatement' &&
12+
node.path.original === 'yield' &&
13+
(!node.params || node.params.length === 0)
14+
);
15+
}
16+
117
/** @type {import('eslint').Rule.RuleModule} */
218
module.exports = {
319
meta: {
420
type: 'problem',
521
docs: {
6-
description: 'disallow {{yield}} without parameters outside of contextual components',
22+
description: 'disallow templates whose only meaningful content is a bare {{yield}}',
723
category: 'Best Practices',
824
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-bare-yield.md',
925
templateMode: 'both',
1026
},
1127
schema: [],
1228
messages: {
13-
noBareYield: 'yield should have parameters or be used in contextual components only.',
29+
noBareYield: '{{yieldCall}}-only templates are not allowed.',
30+
},
31+
originallyFrom: {
32+
name: 'ember-template-lint',
33+
rule: 'lib/rules/no-yield-only.js',
34+
docs: 'docs/rule/no-yield-only.md',
35+
tests: 'test/unit/rules/no-yield-only-test.js',
1436
},
1537
},
1638

1739
create(context) {
1840
return {
19-
GlimmerMustacheStatement(node) {
20-
if (node.path.type === 'GlimmerPathExpression' && node.path.original === 'yield') {
21-
if (!node.params || node.params.length === 0) {
22-
context.report({
23-
node,
24-
messageId: 'noBareYield',
25-
});
26-
}
41+
GlimmerTemplate(node) {
42+
// In GJS/GTS mode the content lives inside a GlimmerElementNode wrapper
43+
// with tag="template"; in HBS mode the body is the content directly.
44+
let body = node.body;
45+
if (
46+
body.length === 1 &&
47+
body[0].type === 'GlimmerElementNode' &&
48+
body[0].tag === 'template'
49+
) {
50+
body = body[0].children;
51+
}
52+
53+
const nonEmptyNodes = body.filter((n) => !isEmptyNode(n));
54+
if (nonEmptyNodes.length === 1 && isBareYield(nonEmptyNodes[0])) {
55+
context.report({
56+
node: nonEmptyNodes[0],
57+
messageId: 'noBareYield',
58+
data: { yieldCall: '{{yield}}' },
59+
});
2760
}
2861
},
2962
};

tests/lib/rules/template-no-bare-yield.js

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,71 @@ const ruleTester = new RuleTester({
1212

1313
ruleTester.run('template-no-bare-yield', rule, {
1414
valid: [
15-
// yield with params inside a class
16-
`import Component from '@glimmer/component';
17-
class MyComponent extends Component {
18-
<template>{{yield this}}</template>
19-
}`,
20-
// yield with params inside a function
21-
`function myComponent() {
22-
return <template>{{yield this}}</template>;
23-
}`,
15+
// yield with params is fine
16+
'<template>{{yield this}}</template>',
2417
'<template>{{yield @model}}</template>',
18+
'<template>{{yield (hash someProp=someValue)}}</template>',
19+
// yield is not the only content
20+
'<template>{{yield}}<div>Content</div></template>',
21+
'<template><div>Content</div>{{yield}}</template>',
22+
// no yield at all
2523
'<template><div>Content</div></template>',
26-
// yield this at module level is allowed by this rule (template-no-unavailable-this handles the `this` part)
27-
'<template>{{yield this}}</template>',
2824
],
2925
invalid: [
3026
{
3127
code: '<template>{{yield}}</template>',
3228
output: null,
3329
errors: [{ messageId: 'noBareYield' }],
3430
},
31+
{
32+
// whitespace around yield doesn't count as other content
33+
code: '<template> {{yield}} </template>',
34+
output: null,
35+
errors: [{ messageId: 'noBareYield' }],
36+
},
37+
{
38+
// comments don't count as other content
39+
code: '<template>{{! a comment }}{{yield}}</template>',
40+
output: null,
41+
errors: [{ messageId: 'noBareYield' }],
42+
},
43+
],
44+
});
45+
46+
const hbsRuleTester = new RuleTester({
47+
parser: require.resolve('ember-eslint-parser/hbs'),
48+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
49+
});
50+
51+
hbsRuleTester.run('template-no-bare-yield', rule, {
52+
valid: [
53+
'{{yield (hash someProp=someValue)}}',
54+
'{{yield this}}',
55+
// yield with other content
56+
'{{yield}}<div>Content</div>',
57+
],
58+
invalid: [
59+
{
60+
code: '{{yield}}',
61+
output: null,
62+
errors: [{ messageId: 'noBareYield' }],
63+
},
64+
{
65+
// whitespace around yield doesn't count as other content
66+
code: ' {{yield}}',
67+
output: null,
68+
errors: [{ messageId: 'noBareYield' }],
69+
},
70+
{
71+
code: '\n {{yield}}\n ',
72+
output: null,
73+
errors: [{ messageId: 'noBareYield' }],
74+
},
75+
{
76+
// comments don't count as other content
77+
code: '\n{{! some comment }} {{yield}}\n ',
78+
output: null,
79+
errors: [{ messageId: 'noBareYield' }],
80+
},
3581
],
3682
});

0 commit comments

Comments
 (0)