Skip to content

Post-merge-review: Fix template-no-link-to-tagname: only flag @tagName, not bare tagName, on angle-bracket <LinkTo>#2652

Merged
NullVoxPopuli merged 4 commits intoember-cli:masterfrom
johanrd:night_fix/template-no-link-to-tagname
Apr 14, 2026
Merged

Post-merge-review: Fix template-no-link-to-tagname: only flag @tagName, not bare tagName, on angle-bracket <LinkTo>#2652
NullVoxPopuli merged 4 commits intoember-cli:masterfrom
johanrd:night_fix/template-no-link-to-tagname

Conversation

@johanrd
Copy link
Copy Markdown
Contributor

@johanrd johanrd commented Apr 12, 2026

What's broken on master

  • The rule matched every <LinkTo> tag by bare name. In strict-mode templates (.gjs / .gts) a component named LinkTo is only Ember's router link if it is imported from @ember/routing. This caused two bugs:
    1. False positive: a user-authored <LinkTo @tagName="button"> component (no import from @ember/routing) was flagged even though it has nothing to do with Ember's router LinkTo.
    2. False negative: a renamed import such as import { LinkTo as Link } from '@ember/routing' used as <Link @tagName="button"> was not detected because the tag name no longer matches the bare string LinkTo.
  • Additionally, on angle-bracket <LinkTo> the rule previously flagged the bare HTML-attribute tagName as well as the Ember argument @tagName. Bare tagName on angle-bracket invocation is just a passthrough HTML attribute and is not deprecated; only @tagName is.

Fix

  • Detect strict mode via context.filename (.gjs / .gts).
  • Add an ImportDeclaration visitor (strict mode only) that records every local alias imported as LinkTo from @ember/routing into importedLinkComponents. Mirrors the pattern used in template-no-invalid-link-text.
  • In strict mode the angle-bracket visitor only flags elements whose tag is in the tracked set. In classic HBS it keeps the previous behavior (LinkTo / link-to), since HBS has no imports.
  • The curly ({{link-to}} / {{#link-to}}) visitor is unchanged: there are no imports in HBS curly form.
  • Only flag @tagName (not bare tagName) on angle-bracket <LinkTo>.

Test plan

  • Valid: <LinkTo @tagName="button"> in .gjs and .gts with no @ember/routing import (user-authored component must not be flagged).
  • Invalid: import { LinkTo } from '@ember/routing'; + <LinkTo @tagName=...> in both .gjs and .gts.
  • Invalid: renamed import import { LinkTo as Link } from '@ember/routing'; + <Link @tagName="button"> flags <Link> in both .gjs and .gts.
  • Existing HBS cases still covered (including <link-to @tagName> via angle-bracket kebab form in classic templates).
  • All 30 rule tests pass (pnpm vitest run tests/lib/rules/template-no-link-to-tagname.js).

@johanrd johanrd marked this pull request as ready for review April 13, 2026 10:27
…ing import tracking in strict mode

What was broken on master:
- The rule matched every `<LinkTo>` tag by bare name. In strict-mode templates
  (.gjs/.gts) a component named `LinkTo` is only Ember's router link if it is
  imported from `@ember/routing`. This caused two bugs:
  1. False positive: a user-authored `<LinkTo @TagName="button">` component
     (no import) was flagged even though it has nothing to do with Ember's
     router LinkTo.
  2. False negative: a renamed import such as
     `import { LinkTo as Link } from '@ember/routing'` used as
     `<Link @TagName="button">` was not detected because the tag name no
     longer matches the bare string `LinkTo`.
- Additionally: on angle-bracket `<LinkTo>` the rule previously flagged the
  bare HTML-attribute `tagName` as well as the Ember argument `@tagName`.
  Bare `tagName` on angle-bracket invocation is just a passthrough HTML
  attribute and is not deprecated; only `@tagName` is.

Fix:
- Detect strict mode via `context.filename` (.gjs / .gts).
- Add an `ImportDeclaration` visitor (strict mode only) that records every
  local alias imported as `LinkTo` from `@ember/routing` into
  `importedLinkComponents`. Mirrors the pattern used in
  `template-no-invalid-link-text`.
- In strict mode the angle-bracket visitor only flags elements whose tag is
  in the tracked set. In classic HBS it keeps the previous behavior
  (`LinkTo` / `link-to`), since HBS has no imports.
- The curly (`{{link-to}}` / `{{#link-to}}`) visitor is unchanged: there are
  no imports in HBS curly form.
- Only flag `@tagName` (not bare `tagName`) on angle-bracket `<LinkTo>`.

Test plan:
- Valid: `<LinkTo @TagName="button">` in .gjs and .gts with no
  `@ember/routing` import (user-authored component must not be flagged).
- Invalid: `import { LinkTo } from '@ember/routing';` +
  `<LinkTo @TagName=...>` in both .gjs and .gts.
- Invalid: renamed import
  `import { LinkTo as Link } from '@ember/routing';` +
  `<Link @TagName="button">` flags `<Link>` in both .gjs and .gts.
- Existing HBS cases still covered (including `<link-to @TagName>` via
  angle-bracket kebab form in classic templates).
@johanrd johanrd force-pushed the night_fix/template-no-link-to-tagname branch from 47bb30f to 0338b6c Compare April 13, 2026 10:56
johanrd added a commit to johanrd/eslint-plugin-ember that referenced this pull request Apr 13, 2026
The angle-bracket visitor checks `node.tag === 'LinkTo'` by bare name,
which causes two GJS/GTS bugs (the same class as PR ember-cli#2652 fixed for
template-no-link-to-tagname):

- A user-authored `<LinkTo>` from any non-`@ember/routing` source is
  falsely flagged when childless.
- A renamed framework import (`import { LinkTo as Link } from '@ember/routing'`)
  used as `<Link />` is missed entirely.

Adopt the same `@ember/routing` import-tracker pattern: in strict mode,
only flag elements whose tag is in the tracked map (covers renamed
imports). HBS path uses bare-name match (no imports possible). Curly
`{{link-to ...}}` handler is gated to HBS only — `link-to` is not a
valid JS identifier, so it cannot be a user binding in strict mode, and
the strict-mode compiler would already reject the source.

Tests: 21 (was 14) — adds 4 new valid GJS/GTS cases (no-import bare
`<LinkTo>` in .gjs and .gts, with-import block-form `<LinkTo>...</LinkTo>`,
with-renamed-import block-form `<Link>...</Link>`) and 3 new invalid
cases (with-import childless `<LinkTo />` in .gjs and .gts, renamed
childless `<Link />`).

Docs updated to document the strict-mode behavior.
johanrd added a commit to johanrd/eslint-plugin-ember that referenced this pull request Apr 13, 2026
The angle-bracket visitor checks `node.tag === 'LinkTo'` by bare name,
which causes two GJS/GTS bugs (the same class as PR ember-cli#2652 fixed for
template-no-link-to-tagname):

- A user-authored `<LinkTo>` from any non-`@ember/routing` source is
  falsely flagged when childless.
- A renamed framework import (`import { LinkTo as Link } from '@ember/routing'`)
  used as `<Link />` is missed entirely.

Adopt the same `@ember/routing` import-tracker pattern: in strict mode,
only flag elements whose tag is in the tracked map (covers renamed
imports). HBS path uses bare-name match (no imports possible). Curly
`{{link-to ...}}` handler is gated to HBS only — `link-to` is not a
valid JS identifier, so it cannot be a user binding in strict mode, and
the strict-mode compiler would already reject the source.

Tests: 21 (was 14) — adds 4 new valid GJS/GTS cases (no-import bare
`<LinkTo>` in .gjs and .gts, with-import block-form `<LinkTo>...</LinkTo>`,
with-renamed-import block-form `<Link>...</Link>`) and 3 new invalid
cases (with-import childless `<LinkTo />` in .gjs and .gts, renamed
childless `<Link />`).

Docs updated to document the strict-mode behavior.
const filename = context.filename;
const isStrictMode = filename.endsWith('.gjs') || filename.endsWith('.gts');

// In HBS, LinkTo always refers to Ember's router link component.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is not true -- if a user defines a LinkTo component in their app that will be resolved, i believe

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, thanks, yes should maybe soften the comment a bit to "almost always refers to Ember's router link component; a user-defined link-to could shadow it, but detecting that in classic HBS would require resolver-level info the rule doesn't have."?

{
code: '<link-to @route="contact" @tagName="div">Contact</link-to>',
output: null,
errors: [{ message: 'tagName attribute on LinkTo is deprecated' }],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it still deprecated? even now? did we ever remove it? what version of ember?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point. it was removed in Ember 4.0 (Dec 2021).

  • Deprecation ID: ember.built-in-components.legacy-arguments
  • Deprecated in: 3.27 (June 2021, RFC #671)
  • Removed in: 4.0.0 — since 4.0, always renders an <a>; @TagName is no longer supported at all, as I understand it.

So the error message 'tagName attribute on LinkTo is deprecated' is stale — for Ember 4+ it's not deprecated, it's gone. Options: rename the messageId/text to something like '@TagName on is not supported (removed in Ember 4.0)', and possibly reconsider the category.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yae a new error message based on the ember-source version would be good

The comment "LinkTo always refers to..." overstated our certainty in
classic HBS — a user-defined component could shadow it, though we
can't detect that without resolver info.

The message said "deprecated" but @TagName was fully removed in
Ember 4.0 (deprecation ID: ember.built-in-components.legacy-arguments,
deprecated in 3.27, removed in 4.0.0, Dec 2021).
@NullVoxPopuli NullVoxPopuli merged commit eb5b7d1 into ember-cli:master Apr 14, 2026
10 checks passed
@johanrd johanrd deleted the night_fix/template-no-link-to-tagname branch April 14, 2026 21:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants