Skip to content

Commit dcd8c51

Browse files
Merge pull request #2688 from johanrd/day_fix/template-no-attrs-in-components
Post-merge-review: Fix template-no-attrs-in-components: align detection with upstream
2 parents 6f846ab + 179e8b1 commit dcd8c51

2 files changed

Lines changed: 127 additions & 29 deletions

File tree

lib/rules/template-no-attrs-in-components.js

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,29 @@
1+
// Matches traditional, pod, ui/components, and -components/ paths.
2+
// Also matches Octane co-located templates (app/components/foo.hbs) via
3+
// /components/ — cf. ember-template-lint#1445.
4+
const COMPONENT_TEMPLATE_REGEX = new RegExp(
5+
'templates/components|components/.*/template|ui/components|-components/|/components/'
6+
);
7+
18
/** @type {import('eslint').Rule.RuleModule} */
29
module.exports = {
310
meta: {
411
type: 'suggestion',
512
docs: {
613
description: 'disallow attrs in component templates',
714
category: 'Deprecations',
8-
915
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-attrs-in-components.md',
16+
templateMode: 'both',
1017
},
1118
schema: [],
1219
messages: {
13-
noThisAttrs:
14-
'Component templates should not contain `this.attrs`. Use `@arg` syntax instead.',
20+
noAttrs: 'Component templates should not contain `attrs`.',
21+
},
22+
originallyFrom: {
23+
name: 'ember-template-lint',
24+
rule: 'lib/rules/no-attrs-in-components.js',
25+
docs: 'docs/rule/no-attrs-in-components.md',
26+
tests: 'test/unit/rules/no-attrs-in-components-test.js',
1527
},
1628
originallyFrom: {
1729
name: 'ember-template-lint',
@@ -21,10 +33,16 @@ module.exports = {
2133
},
2234
},
2335
create(context) {
36+
if (!COMPONENT_TEMPLATE_REGEX.test(context.filename)) {
37+
return {};
38+
}
2439
return {
2540
GlimmerPathExpression(node) {
26-
if (node.original?.startsWith('this.attrs.') || node.original === 'this.attrs') {
27-
context.report({ node, messageId: 'noThisAttrs' });
41+
// Flag `attrs.*` and `this.attrs.*` — both are pre-Octane args-leakage
42+
// patterns from @ember/component. In the Glimmer AST, `this.attrs.foo`
43+
// has parts[0] === 'attrs' (this is the receiver, not a part).
44+
if (node.parts && node.parts[0] === 'attrs') {
45+
context.report({ node, messageId: 'noAttrs' });
2846
}
2947
},
3048
};

tests/lib/rules/template-no-attrs-in-components.js

Lines changed: 104 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,40 +8,120 @@ const ruleTester = new RuleTester({
88

99
ruleTester.run('template-no-attrs-in-components', rule, {
1010
valid: [
11-
'<template>{{@value}}</template>',
12-
'<template>{{this.value}}</template>',
13-
// Class component with normal this access
14-
`import Component from '@glimmer/component';
15-
class MyComponent extends Component {
16-
<template>{{this.args.name}}</template>
17-
}`,
18-
// Bare attrs is not accessible without this, so it's allowed
19-
'<template>{{attrs.value}}</template>',
20-
'<template>{{attrs}}</template>',
21-
`import Component from '@glimmer/component';
22-
class MyComponent extends Component {
23-
<template>{{attrs.name}}</template>
24-
}`,
11+
// Not a component template path: nothing is flagged, regardless of content.
12+
{
13+
filename: 'app/templates/application.hbs',
14+
code: '<template>{{@value}}</template>',
15+
},
16+
{
17+
filename: 'app/templates/application.hbs',
18+
code: '<template>{{this.value}}</template>',
19+
},
20+
// `this.attrs.*` outside a component template — not flagged (path gate).
21+
{
22+
filename: 'app/templates/application.hbs',
23+
code: '<template>{{this.attrs.foo}}</template>',
24+
},
25+
// Even `attrs.*` itself is only flagged inside component templates.
26+
{
27+
filename: 'app/templates/application.hbs',
28+
code: '<template>{{attrs.value}}</template>',
29+
},
30+
// Inside a component template, non-attrs paths are fine.
31+
{
32+
filename: 'app/templates/components/foo.hbs',
33+
code: '<template>{{@value}}</template>',
34+
},
35+
{
36+
filename: 'app/templates/components/foo.hbs',
37+
code: '<template>{{this.value}}</template>',
38+
},
39+
// Pod-style components path matches the gate, but no `attrs` usage.
40+
{
41+
filename: 'app/components/foo/template.hbs',
42+
code: '<template>{{@value}}</template>',
43+
},
44+
// `-components/` path gate, no `attrs` usage.
45+
{
46+
filename: 'app/ui-components/foo.hbs',
47+
code: '<template>{{@value}}</template>',
48+
},
49+
// Octane co-located template — non-attrs path is fine.
50+
{
51+
filename: 'app/components/foo.hbs',
52+
code: '<template>{{@value}}</template>',
53+
},
2554
],
2655
invalid: [
56+
// Bare `attrs.*` inside `templates/components/` — flagged.
57+
{
58+
filename: 'app/templates/components/foo.hbs',
59+
code: '<template>{{attrs.foo}}</template>',
60+
output: null,
61+
errors: [{ messageId: 'noAttrs' }],
62+
},
63+
// Bare `attrs` (no dotted tail) inside `templates/components/` — flagged.
64+
{
65+
filename: 'app/templates/components/foo.hbs',
66+
code: '<template>{{attrs}}</template>',
67+
output: null,
68+
errors: [{ messageId: 'noAttrs' }],
69+
},
70+
// Pod-style path `components/*/template` — flagged.
71+
{
72+
filename: 'app/components/foo/template.hbs',
73+
code: '<template>{{attrs.name}}</template>',
74+
output: null,
75+
errors: [{ messageId: 'noAttrs' }],
76+
},
77+
// `ui/components` path — flagged.
78+
{
79+
filename: 'app/ui/components/foo.hbs',
80+
code: '<template>{{attrs.name}}</template>',
81+
output: null,
82+
errors: [{ messageId: 'noAttrs' }],
83+
},
84+
// `-components/` path — flagged.
85+
{
86+
filename: 'app/ui-components/foo.hbs',
87+
code: '<template>{{attrs.name}}</template>',
88+
output: null,
89+
errors: [{ messageId: 'noAttrs' }],
90+
},
91+
// `this.attrs.*` inside a component template — flagged (real @ember/component API).
92+
{
93+
filename: 'app/templates/components/foo.hbs',
94+
code: '<template>{{this.attrs.foo}}</template>',
95+
output: null,
96+
errors: [{ messageId: 'noAttrs' }],
97+
},
98+
// attrs in attribute value position.
99+
{
100+
filename: 'app/templates/components/foo.hbs',
101+
code: '<template><div class={{attrs.foo}}></div></template>',
102+
output: null,
103+
errors: [{ messageId: 'noAttrs' }],
104+
},
105+
// attrs in block helper condition.
27106
{
28-
code: '<template>{{this.attrs.value}}</template>',
107+
filename: 'app/templates/components/foo.hbs',
108+
code: '<template>{{#if attrs.foo}}bar{{/if}}</template>',
29109
output: null,
30-
errors: [{ messageId: 'noThisAttrs' }],
110+
errors: [{ messageId: 'noAttrs' }],
31111
},
112+
// attrs as hash pair value.
32113
{
33-
code: '<template>{{this.attrs}}</template>',
114+
filename: 'app/templates/components/foo.hbs',
115+
code: '<template>{{bar foo=attrs.foo}}</template>',
34116
output: null,
35-
errors: [{ messageId: 'noThisAttrs' }],
117+
errors: [{ messageId: 'noAttrs' }],
36118
},
37-
// Class component using this.attrs
119+
// Octane co-located template (app/components/foo.hbs) — flagged.
38120
{
39-
code: `import Component from '@glimmer/component';
40-
class MyComponent extends Component {
41-
<template>{{this.attrs.name}}</template>
42-
}`,
121+
filename: 'app/components/foo.hbs',
122+
code: '<template>{{attrs.foo}}</template>',
43123
output: null,
44-
errors: [{ messageId: 'noThisAttrs' }],
124+
errors: [{ messageId: 'noAttrs' }],
45125
},
46126
],
47127
});

0 commit comments

Comments
 (0)