Skip to content

Commit a1ab781

Browse files
Merge pull request #2674 from johanrd/night_fix/template-no-invalid-link-title
Post-merge-review: Fix `template-no-invalid-link-title`: track `@ember/routing` LinkTo import
2 parents ae2260b + 391add4 commit a1ab781

2 files changed

Lines changed: 55 additions & 9 deletions

File tree

lib/rules/template-no-invalid-link-title.js

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,21 +45,33 @@ module.exports = {
4545
},
4646

4747
create(context) {
48+
const filename = context.filename;
49+
const isStrictMode = filename.endsWith('.gjs') || filename.endsWith('.gts');
50+
51+
// In HBS, <LinkTo> always refers to Ember's router link component.
52+
// In GJS/GTS, LinkTo must be explicitly imported from '@ember/routing'.
53+
// local alias → true (any truthy value marks it as a tracked link component)
54+
const importedLinkComponents = new Map();
55+
56+
const linkTags = new Set(['a']);
57+
if (!isStrictMode) {
58+
linkTags.add('LinkTo');
59+
}
60+
4861
// eslint-disable-next-line complexity
4962
function checkElementNode(node) {
50-
if (node.tag !== 'a' && node.tag !== 'LinkTo') {
63+
if (!linkTags.has(node.tag)) {
5164
return;
5265
}
66+
// Determine if this tag should be treated as <LinkTo> for @title handling
67+
const isLinkTo = node.tag === 'LinkTo' || importedLinkComponents.has(node.tag);
5368

5469
const titleAttr = node.attributes.find(
5570
(attr) => attr.type === 'GlimmerAttrNode' && attr.name === 'title'
5671
);
57-
const titleArgAttr =
58-
node.tag === 'LinkTo'
59-
? node.attributes.find(
60-
(attr) => attr.type === 'GlimmerAttrNode' && attr.name === '@title'
61-
)
62-
: null;
72+
const titleArgAttr = isLinkTo
73+
? node.attributes.find((attr) => attr.type === 'GlimmerAttrNode' && attr.name === '@title')
74+
: null;
6375

6476
// Get title attribute text value
6577
let titleAttrValue;
@@ -74,12 +86,12 @@ module.exports = {
7486
}
7587

7688
// Collect all title values (lowercased, trimmed)
77-
const titleValues = [titleAttrValue, node.tag === 'LinkTo' ? titleArgValue : null]
89+
const titleValues = [titleAttrValue, isLinkTo ? titleArgValue : null]
7890
.filter((v) => typeof v === 'string')
7991
.map((v) => v.toLowerCase().trim());
8092

8193
// Error if both title and @title are specified on LinkTo
82-
if (node.tag === 'LinkTo' && titleAttrValue !== undefined && titleArgValue !== undefined) {
94+
if (isLinkTo && titleAttrValue !== undefined && titleArgValue !== undefined) {
8395
context.report({
8496
node: titleAttr || node,
8597
messageId: 'noInvalidLinkTitle',
@@ -150,6 +162,19 @@ module.exports = {
150162
}
151163

152164
return {
165+
ImportDeclaration(node) {
166+
if (!isStrictMode) {
167+
return;
168+
}
169+
if (node.source.value === '@ember/routing') {
170+
for (const specifier of node.specifiers) {
171+
if (specifier.type === 'ImportSpecifier' && specifier.imported.name === 'LinkTo') {
172+
importedLinkComponents.set(specifier.local.name, true);
173+
linkTags.add(specifier.local.name);
174+
}
175+
}
176+
}
177+
},
153178
GlimmerElementNode: checkElementNode,
154179
GlimmerBlockStatement: checkBlockStatement,
155180
};

tests/lib/rules/template-no-invalid-link-title.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,19 @@ const ruleTester = new RuleTester({
1212

1313
ruleTester.run('template-no-invalid-link-title', rule, {
1414
valid: [
15+
// In GJS/GTS, <LinkTo> is only a router link if explicitly imported.
16+
// Without an import, it's a user-authored component and the rule shouldn't fire.
17+
{
18+
filename: 'test.gjs',
19+
code: '<template><LinkTo title="x">x</LinkTo></template>',
20+
},
21+
// With the import, the rule correctly treats it as the router LinkTo.
22+
{
23+
filename: 'test.gjs',
24+
code: `import { LinkTo } from '@ember/routing';
25+
<template><LinkTo title="More about page">Page</LinkTo></template>`,
26+
},
27+
1528
'<template><a href="/page" title="More information about page">Page</a></template>',
1629
'<template><a href="/page">Page</a></template>',
1730
'<template><a href="/page" title={{dynamic}}>Page</a></template>',
@@ -33,6 +46,14 @@ ruleTester.run('template-no-invalid-link-title', rule, {
3346
</template>`,
3447
],
3548
invalid: [
49+
// Imported <LinkTo> in GJS/GTS: rule still applies
50+
{
51+
filename: 'test.gjs',
52+
code: `import { LinkTo } from '@ember/routing';
53+
<template><LinkTo title="quickstart">Quickstart</LinkTo></template>`,
54+
output: null,
55+
errors: [{ messageId: 'noInvalidLinkTitle' }],
56+
},
3657
{
3758
code: '<template><a href="/page" title="">Page</a></template>',
3859
output: null,

0 commit comments

Comments
 (0)