Skip to content

Extract rule: template-require-context-role#2601

Open
NullVoxPopuli wants to merge 3 commits intoember-cli:masterfrom
NullVoxPopuli:nvp/template-lint-extract-rule-template-require-context-role
Open

Extract rule: template-require-context-role#2601
NullVoxPopuli wants to merge 3 commits intoember-cli:masterfrom
NullVoxPopuli:nvp/template-lint-extract-rule-template-require-context-role

Conversation

@NullVoxPopuli
Copy link
Contributor

Split from #2371.

Copy link

@NullVoxPopuli-ai-agent NullVoxPopuli-ai-agent left a comment

Choose a reason for hiding this comment

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

Review: template-require-context-role (PR #2601)

Comparison with ember-template-lint require-context-role.js

General correctness:

  1. Role mapping: The original builds its role mapping from a ROOTS object (parent → children) and inverts it to get VALID_PARENTS_FOR_CHILD. The PR defines ROLES_REQUIRING_CONTEXT directly (child → valid parents). Comparing the mappings:

    • Original's inverted mapping produces:
      • cell[row]Missing from the PR. The original's ROOTS.row includes 'cell' but the PR's ROLES_REQUIRING_CONTEXT does not have a cell entry.
      • listitem[group, list] — PR has [list, group, directory]. The PR adds directory which is NOT in the original. directory is a deprecated ARIA role, so this is a debatable addition.
      • option[listbox] — PR has [listbox, group]. The PR adds group which is NOT in the original.
      • rowgroup[grid, table, treegrid] — PR matches (has all three).

    So there are discrepancies in both directions. cell role is completely missing from the PR's map — this means <div role="cell"> inside a <div> (without role="row") will NOT be flagged. The original would flag it.

  2. directory role: The PR includes directory as a valid parent for listitem. This role is deprecated in ARIA 1.2. Not wrong per se but deviates from the original.

  3. option with group parent: The PR allows role="option" inside role="group", but the original does not. According to WAI-ARIA spec, option requires listbox context. group is allowed for grouping options but only within a listbox. This is an over-relaxation.

  4. Parent traversal approach: The original walks up using path.parent (ember-template-lint's AST path) and stops at the first non-presentation parent element. The PR uses an elementStack maintained via enter/exit visitors. This is the standard ESLint approach and works correctly.

  5. aria-hidden handling: The original checks aria-hidden on the current node AND ancestor nodes. The PR checks aria-hidden only on ancestors (via isInsideAriaHidden). However, the original also returns early if the current element has aria-hidden, essentially skipping all children. The PR's approach of checking ancestors seems functionally similar since child elements would find the aria-hidden ancestor.

    Actually, there's a difference: the original checks aria-hidden on the element with the context-requiring role itself and returns early. The PR only checks ancestors via isInsideAriaHidden (which excludes the current element at elementStack.length - 1). So if you have <div aria-hidden="true" role="listitem">, the original would skip it, but the PR would still flag it (since it only checks ancestors). This is a behavioral difference — consider also checking the current node for aria-hidden.

  6. Root-level elements: The PR skips reporting when elementStack.length <= 1 (no parent elements) or when getAccessibleParentRole returns undefined. The original's while loop naturally handles this. The PR's approach is correct — you can't validate context when there's no parent.

  7. Template tag transparency: The PR treats <template> tag as transparent in getAccessibleParentRole. This is correct for the gjs/gts case where <template> is a wrapper, not a semantic HTML element.

  8. Error message format: Different from original but conveys the same information. The original includes a W3C reference URL; the PR does not. Consider adding the reference URL.

Scope analysis (gjs/gts):

This rule only checks role attribute values on GlimmerElementNode. Pure structural/attribute checking. No scope analysis needed.

Overall: The migration has some mapping discrepancies:

  • Missing: cell[row] role context (present in original, absent in PR)
  • Added: directory as valid parent for listitem (not in original)
  • Added: group as valid parent for option (not in original)
  • Behavioral: aria-hidden on the current element itself is not checked

These should be reviewed to ensure they are intentional deviations or corrected to match the original.

🤖 Automated review comparing with ember-template-lint source

NullVoxPopuli and others added 2 commits March 18, 2026 18:43
Align ROLES_REQUIRING_CONTEXT with the original require-context-role rule
from ember-template-lint by:
- Adding missing `cell` role (requires `row` context)
- Removing `directory` from `listitem` parents (not in original)
- Removing `group` from `option` and `menuitemcheckbox` parents (not in original)
- Adding `grid` to `rowheader` parents (present in original)
- Sorting parent arrays alphabetically to match the original's sorted output

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…-mapping

Fix role-to-context mapping to match original ember-template-lint rule
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants