Skip to content

Commit 459164f

Browse files
Merge pull request #2477 from NullVoxPopuli/nvp/template-lint-extract-rule-template-no-link-to-positional-params
Extract rule: template-no-link-to-positional-params
2 parents 995560f + b97ee5c commit 459164f

4 files changed

Lines changed: 372 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ rules in templates can be disabled with eslint directives with mustache or html
278278
| [no-string-prototype-extensions](docs/rules/no-string-prototype-extensions.md) | disallow usage of `String` prototype extensions || | |
279279
| [template-no-action](docs/rules/template-no-action.md) | disallow {{action}} helper | | | |
280280
| [template-no-attrs-in-components](docs/rules/template-no-attrs-in-components.md) | disallow attrs in component templates | | | |
281+
| [template-no-link-to-positional-params](docs/rules/template-no-link-to-positional-params.md) | disallow positional params in LinkTo component | | | |
281282
| [template-no-link-to-tagname](docs/rules/template-no-link-to-tagname.md) | disallow tagName attribute on LinkTo component | | | |
282283

283284
### Ember Data
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# ember/template-no-link-to-positional-params
2+
3+
<!-- end auto-generated rule header -->
4+
5+
> Disallow positional params in LinkTo component
6+
7+
## Rule Details
8+
9+
Positional parameters in `<LinkTo>` components are deprecated. Use named arguments instead.
10+
11+
## Examples
12+
13+
Examples of **incorrect** code for this rule:
14+
15+
```gjs
16+
<template>
17+
{{! Old positional params style }}
18+
<LinkTo "posts.post" @model={{this.post}}>Post</LinkTo>
19+
</template>
20+
```
21+
22+
Examples of **correct** code for this rule:
23+
24+
```gjs
25+
<template>
26+
<LinkTo @route="posts.post" @model={{this.post}}>Post</LinkTo>
27+
</template>
28+
```
29+
30+
```gjs
31+
<template>
32+
<LinkTo @route="index">Home</LinkTo>
33+
</template>
34+
```
35+
36+
## References
37+
38+
- [eslint-plugin-ember template-no-link-to-positional-params](https://github.com/ember-cli/eslint-plugin-ember/blob/master/docs/rules/template-no-link-to-positional-params.md)
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/**
2+
* Helper function to check if a node is a LinkTo component
3+
* @param {Object} node - The AST node to check
4+
* @returns {boolean} - True if node is a LinkTo component
5+
*/
6+
function isLinkToComponent(node) {
7+
if (node.type === 'GlimmerElementNode') {
8+
return node.tag === 'LinkTo' || node.tag === 'link-to';
9+
}
10+
return false;
11+
}
12+
13+
/** @type {import('eslint').Rule.RuleModule} */
14+
module.exports = {
15+
meta: {
16+
type: 'problem',
17+
docs: {
18+
description: 'disallow positional params in LinkTo component',
19+
category: 'Deprecations',
20+
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-link-to-positional-params.md',
21+
templateMode: 'both',
22+
},
23+
schema: [],
24+
messages: {
25+
noLinkToPositionalParams: 'Positional params in LinkTo are deprecated. Use @route instead.',
26+
},
27+
originallyFrom: {
28+
name: 'ember-template-lint',
29+
rule: 'lib/rules/no-link-to-positional-params.js',
30+
docs: 'docs/rule/no-link-to-positional-params.md',
31+
tests: 'test/unit/rules/no-link-to-positional-params-test.js',
32+
},
33+
},
34+
35+
create(context) {
36+
function checkForPositionalParams(node) {
37+
if (
38+
node.path &&
39+
node.path.type === 'GlimmerPathExpression' &&
40+
node.path.original === 'link-to' &&
41+
node.params &&
42+
node.params.length > 0
43+
) {
44+
context.report({
45+
node,
46+
messageId: 'noLinkToPositionalParams',
47+
});
48+
}
49+
}
50+
51+
return {
52+
GlimmerElementNode(node) {
53+
if (!isLinkToComponent(node)) {
54+
return;
55+
}
56+
57+
// Check if there are positional params (non-@ attributes without = sign, which are params)
58+
const hasPositionalParams = node.attributes.some(
59+
(attr) =>
60+
attr.type === 'GlimmerAttrNode' &&
61+
!attr.name.startsWith('@') &&
62+
attr.value &&
63+
attr.value.type !== 'GlimmerTextNode' &&
64+
!node.modifiers.some((mod) => mod.path.original === attr.name)
65+
);
66+
67+
if (hasPositionalParams) {
68+
context.report({
69+
node,
70+
messageId: 'noLinkToPositionalParams',
71+
});
72+
}
73+
},
74+
75+
GlimmerMustacheStatement(node) {
76+
checkForPositionalParams(node);
77+
},
78+
79+
GlimmerBlockStatement(node) {
80+
checkForPositionalParams(node);
81+
},
82+
};
83+
},
84+
};
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
const rule = require('../../../lib/rules/template-no-link-to-positional-params');
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-link-to-positional-params', rule, {
10+
valid: [
11+
{
12+
filename: 'test.gjs',
13+
code: '<template><LinkTo @route="index">Home</LinkTo></template>',
14+
output: null,
15+
},
16+
{
17+
filename: 'test.gjs',
18+
code: '<template><LinkTo @route="posts.post" @model={{this.post}}>Post</LinkTo></template>',
19+
output: null,
20+
},
21+
{
22+
filename: 'test.gjs',
23+
code: '<template><a href="/home">Home</a></template>',
24+
output: null,
25+
},
26+
27+
'<template>{{#link-to route="about"}}About Us{{/link-to}}</template>',
28+
'<template>{{#link-to route="post" model=@post}}Read {{@post.title}}...{{/link-to}}</template>',
29+
`<template>{{#link-to route="post.comment" models=(array post comment)}}
30+
Comment by {{comment.author.name}} on {{comment.date}}
31+
{{/link-to}}</template>`,
32+
`<template>{{#link-to route="posts" query=(hash direction="desc" showArchived=false)}}
33+
Recent Posts
34+
{{/link-to}}</template>`,
35+
'<template><LinkTo @route="about">About Us</LinkTo></template>',
36+
'<template><LinkTo @route="post" @model={{@post}}>Read {{@post.title}}...</LinkTo></template>',
37+
`<template><LinkTo @route="post.comment" @models={{array post comment}}>
38+
Comment by {{comment.author.name}} on {{comment.date}}
39+
</LinkTo></template>`,
40+
`<template><LinkTo @route="posts" @query={{hash direction="desc" showArchived=false}}>
41+
Recent Posts
42+
</LinkTo></template>`,
43+
],
44+
45+
invalid: [
46+
// Note: This rule is simplified and may need adjustment based on actual LinkTo usage patterns
47+
// The real eslint-plugin-ember rule has more complex logic for detecting positional params
48+
49+
{
50+
code: '<template>{{link-to "About Us" "about"}}</template>',
51+
output: null,
52+
errors: [{ messageId: 'noLinkToPositionalParams' }],
53+
},
54+
{
55+
code: '<template>{{link-to "About Us" (if this.showNewAboutPage "about-us" "about")}}</template>',
56+
output: null,
57+
errors: [{ messageId: 'noLinkToPositionalParams' }],
58+
},
59+
{
60+
code: '<template>{{link-to (t "about") "about"}}</template>',
61+
output: null,
62+
errors: [{ messageId: 'noLinkToPositionalParams' }],
63+
},
64+
{
65+
code: '<template>{{link-to (t "about") this.aboutRoute}}</template>',
66+
output: null,
67+
errors: [{ messageId: 'noLinkToPositionalParams' }],
68+
},
69+
{
70+
code: '<template>{{link-to (t "about") this.aboutRoute "foo"}}</template>',
71+
output: null,
72+
errors: [{ messageId: 'noLinkToPositionalParams' }],
73+
},
74+
{
75+
code: '<template>{{link-to (t "about") this.aboutRoute "foo" "bar"}}</template>',
76+
output: null,
77+
errors: [{ messageId: 'noLinkToPositionalParams' }],
78+
},
79+
{
80+
code: '<template>{{link-to (t "about") this.aboutRoute "foo" "bar" (query-params foo="bar")}}</template>',
81+
output: null,
82+
errors: [{ messageId: 'noLinkToPositionalParams' }],
83+
},
84+
{
85+
code: '<template>{{#link-to (if this.showNewAboutPage "about-us" "about")}}About Us{{/link-to}}</template>',
86+
output: null,
87+
errors: [{ messageId: 'noLinkToPositionalParams' }],
88+
},
89+
{
90+
code: '<template>{{#link-to "about"}}About Us{{/link-to}}</template>',
91+
output: null,
92+
errors: [{ messageId: 'noLinkToPositionalParams' }],
93+
},
94+
{
95+
code: '<template>{{#link-to this.aboutRoute}}About Us{{/link-to}}</template>',
96+
output: null,
97+
errors: [{ messageId: 'noLinkToPositionalParams' }],
98+
},
99+
{
100+
code: '<template>{{#link-to this.aboutRoute "foo"}}About Us{{/link-to}}</template>',
101+
output: null,
102+
errors: [{ messageId: 'noLinkToPositionalParams' }],
103+
},
104+
{
105+
code: '<template>{{#link-to this.aboutRoute "foo" "bar"}}About Us{{/link-to}}</template>',
106+
output: null,
107+
errors: [{ messageId: 'noLinkToPositionalParams' }],
108+
},
109+
{
110+
code: '<template>{{#link-to this.aboutRoute "foo" "bar" (query-params foo="bar")}}About Us{{/link-to}}</template>',
111+
output: null,
112+
errors: [{ messageId: 'noLinkToPositionalParams' }],
113+
},
114+
{
115+
code: '<template>{{#link-to "post" @post}}Read {{@post.title}}...{{/link-to}}</template>',
116+
output: null,
117+
errors: [{ messageId: 'noLinkToPositionalParams' }],
118+
},
119+
{
120+
code: `<template>{{#link-to "post.comment" @comment.post @comment}}
121+
Comment by {{@comment.author.name}} on {{@comment.date}}
122+
{{/link-to}}</template>`,
123+
output: null,
124+
errors: [{ messageId: 'noLinkToPositionalParams' }],
125+
},
126+
{
127+
code: `<template>{{#link-to "posts" (query-params direction="desc" showArchived=false)}}
128+
Recent Posts
129+
{{/link-to}}</template>`,
130+
output: null,
131+
errors: [{ messageId: 'noLinkToPositionalParams' }],
132+
},
133+
],
134+
});
135+
136+
const hbsRuleTester = new RuleTester({
137+
parser: require.resolve('ember-eslint-parser/hbs'),
138+
parserOptions: {
139+
ecmaVersion: 2022,
140+
sourceType: 'module',
141+
},
142+
});
143+
144+
hbsRuleTester.run('template-no-link-to-positional-params', rule, {
145+
valid: [
146+
'{{#link-to route="about"}}About Us{{/link-to}}',
147+
'{{#link-to route="post" model=@post}}Read {{@post.title}}...{{/link-to}}',
148+
`{{#link-to route="post.comment" models=(array post comment)}}
149+
Comment by {{comment.author.name}} on {{comment.date}}
150+
{{/link-to}}`,
151+
`{{#link-to route="posts" query=(hash direction="desc" showArchived=false)}}
152+
Recent Posts
153+
{{/link-to}}`,
154+
'<LinkTo @route="about">About Us</LinkTo>',
155+
'<LinkTo @route="post" @model={{@post}}>Read {{@post.title}}...</LinkTo>',
156+
`<LinkTo @route="post.comment" @models={{array post comment}}>
157+
Comment by {{comment.author.name}} on {{comment.date}}
158+
</LinkTo>`,
159+
`<LinkTo @route="posts" @query={{hash direction="desc" showArchived=false}}>
160+
Recent Posts
161+
</LinkTo>`,
162+
],
163+
invalid: [
164+
{
165+
code: '{{link-to "About Us" "about"}}',
166+
output: null,
167+
errors: [{ message: 'Positional params in LinkTo are deprecated. Use @route instead.' }],
168+
},
169+
{
170+
code: '{{link-to "About Us" (if this.showNewAboutPage "about-us" "about")}}',
171+
output: null,
172+
errors: [{ message: 'Positional params in LinkTo are deprecated. Use @route instead.' }],
173+
},
174+
{
175+
code: '{{link-to (t "about") "about"}}',
176+
output: null,
177+
errors: [{ message: 'Positional params in LinkTo are deprecated. Use @route instead.' }],
178+
},
179+
{
180+
code: '{{link-to (t "about") this.aboutRoute}}',
181+
output: null,
182+
errors: [{ message: 'Positional params in LinkTo are deprecated. Use @route instead.' }],
183+
},
184+
{
185+
code: '{{link-to (t "about") this.aboutRoute "foo"}}',
186+
output: null,
187+
errors: [{ message: 'Positional params in LinkTo are deprecated. Use @route instead.' }],
188+
},
189+
{
190+
code: '{{link-to (t "about") this.aboutRoute "foo" "bar"}}',
191+
output: null,
192+
errors: [{ message: 'Positional params in LinkTo are deprecated. Use @route instead.' }],
193+
},
194+
{
195+
code: '{{link-to (t "about") this.aboutRoute "foo" "bar" (query-params foo="bar")}}',
196+
output: null,
197+
errors: [{ message: 'Positional params in LinkTo are deprecated. Use @route instead.' }],
198+
},
199+
{
200+
code: '{{#link-to (if this.showNewAboutPage "about-us" "about")}}About Us{{/link-to}}',
201+
output: null,
202+
errors: [{ message: 'Positional params in LinkTo are deprecated. Use @route instead.' }],
203+
},
204+
{
205+
code: '{{#link-to "about"}}About Us{{/link-to}}',
206+
output: null,
207+
errors: [{ message: 'Positional params in LinkTo are deprecated. Use @route instead.' }],
208+
},
209+
{
210+
code: '{{#link-to this.aboutRoute}}About Us{{/link-to}}',
211+
output: null,
212+
errors: [{ message: 'Positional params in LinkTo are deprecated. Use @route instead.' }],
213+
},
214+
{
215+
code: '{{#link-to this.aboutRoute "foo"}}About Us{{/link-to}}',
216+
output: null,
217+
errors: [{ message: 'Positional params in LinkTo are deprecated. Use @route instead.' }],
218+
},
219+
{
220+
code: '{{#link-to this.aboutRoute "foo" "bar"}}About Us{{/link-to}}',
221+
output: null,
222+
errors: [{ message: 'Positional params in LinkTo are deprecated. Use @route instead.' }],
223+
},
224+
{
225+
code: '{{#link-to this.aboutRoute "foo" "bar" (query-params foo="bar")}}About Us{{/link-to}}',
226+
output: null,
227+
errors: [{ message: 'Positional params in LinkTo are deprecated. Use @route instead.' }],
228+
},
229+
{
230+
code: '{{#link-to "post" @post}}Read {{@post.title}}...{{/link-to}}',
231+
output: null,
232+
errors: [{ message: 'Positional params in LinkTo are deprecated. Use @route instead.' }],
233+
},
234+
{
235+
code: `{{#link-to "post.comment" @comment.post @comment}}
236+
Comment by {{@comment.author.name}} on {{@comment.date}}
237+
{{/link-to}}`,
238+
output: null,
239+
errors: [{ message: 'Positional params in LinkTo are deprecated. Use @route instead.' }],
240+
},
241+
{
242+
code: `{{#link-to "posts" (query-params direction="desc" showArchived=false)}}
243+
Recent Posts
244+
{{/link-to}}`,
245+
output: null,
246+
errors: [{ message: 'Positional params in LinkTo are deprecated. Use @route instead.' }],
247+
},
248+
],
249+
});

0 commit comments

Comments
 (0)