Skip to content

Don't resolve generic HTMLElement / MathMLElement to a phantom tag#12

Open
johanrd wants to merge 2 commits intomainfrom
fix/fp-htmlelement-generic-fallback
Open

Don't resolve generic HTMLElement / MathMLElement to a phantom tag#12
johanrd wants to merge 2 commits intomainfrom
fix/fp-htmlelement-generic-fallback

Conversation

@johanrd
Copy link
Copy Markdown
Owner

@johanrd johanrd commented May 7, 2026

Summary

A component declaring Signature['Element'] = HTMLElement (the bare generic, not a specific subclass) was being Glint-resolved to <abbr>. Reason: TS's lib.dom.d.ts HTMLElementTagNameMap has many tags mapping to bare HTMLElementabbr, address, b, cite, code, … — and our inversion picked the first one alphabetically. Downstream rules then FP-fired element-permitted-content on legal content because they thought the ancestor was an <abbr>.

This change excludes the bare base classes (HTMLElement, SVGElement, MathMLElement) from the inversion, so a component with a generic Element falls through to 'transparent' (children float to the actual parent — the right behaviour for "I render some generic container").

Surfaced by

Ecosystem CI:

  • ember-power-select: 1× <div> not permitted under <abbr> at power-select.gts:1443
  • HDS: 8× <div> under <abbr>, 3× <option> under <abbr>, 1× <abbr> under <ol> — all from one generic-Element declaration somewhere in the component tree

Test plan

  • New regression test in test/glint.test.ts asserts Signature['Element'] = HTMLElement does NOT resolve to <abbr>
  • Verified MathMLElement exclusion is needed (e.g. annotation, maction, math map to bare MathMLElement); SVGElement exclusion is preventive (no bare-typed SVG entries today, but symmetric for future-proofing)
  • npm test — 128/128 (was 127, +1 new regression test)
  • npm run typecheck:tests — clean
  • npm run build — clean

…hantom tags

A component declaring `Signature['Element'] = HTMLElement` (the bare
generic, not a specific subclass) was being Glint-resolved to <abbr>.
Reason: TS's lib.dom.d.ts HTMLElementTagNameMap has many entries
mapping to bare HTMLElement (`abbr`, `address`, `b`, `cite`, `code`,
...) and our inversion picked whichever appeared first. Downstream
rules then FP-fired element-permitted-content on legal content
because they thought the ancestor was an `<abbr>`.

Exclude bare HTMLElement / SVGElement / MathMLElement from the
inversion so a generic Element declaration falls through to
'transparent' (children float to the actual parent — the right
behaviour when a component renders "some generic container").

Surfaced by ecosystem CI on ember-power-select (1× <div> not permitted
under <abbr> at power-select.gts:1443) and HDS (8× <div> under <abbr>
+ 3× <option> under <abbr> + 1× <abbr> under <ol>, all from one
generic-Element declaration somewhere in the component tree).
@johanrd johanrd added the bug Something isn't working label May 7, 2026
@johanrd johanrd requested a review from Copilot May 7, 2026 11:05
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a Glint element-type resolution edge case where a component declaring a generic base element type (e.g. Signature['Element'] = HTMLElement) could be incorrectly resolved to an arbitrary concrete tag (e.g. <abbr>), leading to downstream false positives in content-model validation.

Changes:

  • Exclude generic base element types (HTMLElement, SVGElement, MathMLElement) from the *TagNameMap inversion used to map DOM element types back to tag names.
  • Add a regression test to ensure generic HTMLElement does not resolve to a “phantom” concrete tag like <abbr>.
  • Add new .gts fixtures to exercise cross-file resolution for the generic-HTMLElement case.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
lib/glint.ts Skips inverting tag-name-map entries whose value is a generic base element type, preventing arbitrary tag selection like HTMLElement → abbr.
test/glint.test.ts Adds regression coverage ensuring Element: HTMLElement does not produce a phantom resolved tag.
test/glint-fixtures/generic-html-element.gts New fixture component declaring Element: HTMLElement to reproduce the prior resolution behavior.
test/glint-fixtures/generic-html-element-consumer.gts New consumer fixture to force cross-file resolution of the generic-HTMLElement component.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/glint.ts
…ent types

Per Copilot review on #12: returning null for generic HTMLElement /
SVGElement / MathMLElement let the built-in name-based fallbacks in
blank.ts fire (e.g. user-defined <Input> incorrectly substituted as
<input>). 'transparent' signals 'Glint resolved this; we just don't
know which specific tag', and blank.ts handles transparent by
floating children to the actual parent — the right semantic.

Test now asserts the 'transparent' entry is recorded (in addition to
'abbr' not appearing).
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants