Skip to content

Commit 88237d9

Browse files
committed
fix(template-anchor-has-content): unwrap mustache literals via shared helper (Copilot review)
Extract a new `getStaticAttrValue` util that resolves literal-valued mustaches (`{{"foo"}}`, `{{true}}`, `{{-1}}`) and single-part concat statements (`"{{true}}"`) to their static string value. `isAriaHiddenTrue` now delegates to the helper, so quoted-mustache forms of aria-hidden (e.g. `<a aria-hidden="{{true}}">link</a>`) are recognised the same as their text-node counterparts when walking descendants for accessible content. Byte-identical carrier of lib/utils/static-attr-value.js across all PRs that land it.
1 parent e178d9d commit 88237d9

1 file changed

Lines changed: 13 additions & 17 deletions

File tree

lib/rules/template-anchor-has-content.js

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,28 @@
11
const { isNativeElement } = require('../utils/is-native-element');
2+
const { getStaticAttrValue } = require('../utils/static-attr-value');
23

34
function isDynamicValue(value) {
45
return value?.type === 'GlimmerMustacheStatement' || value?.type === 'GlimmerConcatStatement';
56
}
67

78
// Returns true if the `aria-hidden` attribute is explicitly set to "true"
8-
// (case-insensitive) or mustache-literal `{{true}}`. Per WAI-ARIA 1.2 §6.6
9-
// + aria-hidden value table, valueless / empty-string `aria-hidden` resolves
10-
// to the default `undefined` — NOT `true` — so those forms do NOT hide the
9+
// (case-insensitive) or mustache-literal `{{true}}` / `{{"true"}}` / the
10+
// quoted-mustache concat equivalents. Per WAI-ARIA 1.2 §6.6 + aria-hidden
11+
// value table, valueless / empty-string `aria-hidden` resolves to the
12+
// default `undefined` — NOT `true` — so those forms do NOT hide the
1113
// element per spec. This aligns with the spec-first decisions in #2717 /
12-
// #19 / #33, and diverges from jsx-a11y's JSX-coercion convention.
14+
// #19 / #33, and diverges from jsx-a11y's JSX-coercion convention. All
15+
// shape-unwrapping is delegated to the shared `getStaticAttrValue` helper.
1316
function isAriaHiddenTrue(attr) {
14-
if (!attr?.value) {
17+
if (!attr) {
1518
return false;
1619
}
17-
if (attr.value.type === 'GlimmerTextNode') {
18-
return attr.value.chars.trim().toLowerCase() === 'true';
19-
}
20-
if (attr.value.type === 'GlimmerMustacheStatement') {
21-
const path = attr.value.path;
22-
if (path?.type === 'GlimmerBooleanLiteral') {
23-
return path.value === true;
24-
}
25-
if (path?.type === 'GlimmerStringLiteral') {
26-
return path.value.trim().toLowerCase() === 'true';
27-
}
20+
const resolved = getStaticAttrValue(attr.value);
21+
if (resolved === undefined) {
22+
// Dynamic — can't prove truthy.
23+
return false;
2824
}
29-
return false;
25+
return resolved.trim().toLowerCase() === 'true';
3026
}
3127

3228
// True if the anchor itself declares an accessible name via a statically

0 commit comments

Comments
 (0)