Skip to content

Resolve Element for block-param-yielded curried sub-components#18

Open
johanrd wants to merge 2 commits intomainfrom
fix/fp-yielded-curried-component
Open

Resolve Element for block-param-yielded curried sub-components#18
johanrd wants to merge 2 commits intomainfrom
fix/fp-yielded-curried-component

Conversation

@johanrd
Copy link
Copy Markdown
Owner

@johanrd johanrd commented May 7, 2026

Summary

Pattern: <SelectBase as |C|><C.Options>...</C.Options></SelectBase>, where SelectBase yields a curried sub-component (C.Options) that renders <select>. At runtime <option> children are correctly inside <select>. Statically, Glint's emit surfaces emitComponent(...).element as any for the curried ref, so the resolver fell through to 'transparent'<option> children floated to <SelectBase>'s <div> and element-permitted-content FP-fired.

The componentRef expression (the first argument to __glintDSL__.resolve(...)) has type TOC<OptionsSig> — a generic with the Signature as its first type-argument. resolveElementFromComponentRefType reads Element off aliasTypeArguments[0] directly, the same way the class-form path reads .element off the call type.

Discovery

Glint's emit for the consumer template is roughly:

emitComponent(__glintDSL__.resolve(C?.Options)())

(emitComponent(...)).element is any for this shape (Glint's TOC overload doesn't fully propagate through block-param dereferences). But getTypeAtLocation(C?.Options) returns TOC<OptionsSig> — confirmed via debug logging during this investigation. From there aliasTypeArguments[0] is OptionsSig, which has Element: HTMLSelectElement as a property.

Surfaced by

Ecosystem CI on hashicorp/design-system — the largest single FP cluster: 107× <option> not permitted under <div>, all from <HdsFormSelectBase as |C|><C.Options>...</C.Options></HdsFormSelectBase>-style invocations across the showcase.

Test plan

  • New test asserts componentTagMap resolves <C.Options>'select' against the fixture
  • npm test — 128/128 passing
  • npm run typecheck:tests — clean
  • npm run build — clean

Companion

Stacks naturally with #16 (fix/fp-toc-satisfies-element) — both target the unknown/any element-type fallback. This one covers the curried block-param yield case and the const X: TOC<Sig> = … annotation form (via aliasTypeArguments); #16 covers the ... satisfies TOC<Sig> form (via walking the variable declaration).

Pattern: `<SelectBase as |C|><C.Options>...</C.Options></SelectBase>`,
where SelectBase yields a curried sub-component (`C.Options`) that
renders `<select>`. At runtime `<option>` children are correctly
inside `<select>`. Statically, Glint's emit surfaces
`emitComponent(...).element` as `any` for the curried ref, so our
resolver fell through to `'transparent'` — the `<option>` children
floated to `<SelectBase>`'s `<div>` and `element-permitted-content`
FP-fired.

The componentRef expression's *type* (the first argument to
`__glintDSL__.resolve(...)`) is `TOC<OptionsSig>` — a generic with
the Signature as its first type-argument. Read `Element` off
`aliasTypeArguments[0]` directly, the same way the existing class-form
path reads `.element` off the call type.

Same recovery also helps the `: TOC<S> =` annotation form (covered
already by fix/fp-toc-satisfies-element via the satisfies-walk-back —
this path is more direct).

Surfaced by ecosystem CI on HDS (107× `<option>` not permitted under
`<div>`, all from `<HdsFormSelectBase as |C|><C.Options>...</C.Options>`).
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 improves Glint-based element resolution for Ember templates by recovering a component’s rendered DOM tag from the type of the component reference expression when Glint’s emitted emitComponent(...).element is any/unknown (notably for block-param-yielded curried sub-components like <C.Options>).

Changes:

  • Add a fallback resolution path that extracts Signature['Element'] from componentRef’s generic type arguments (e.g. TOC<OptionsSig>) and maps it to an HTML tag.
  • Add a new cross-file fixture covering the <SelectBase as |C|><C.Options>...</C.Options></SelectBase> pattern.
  • Add an integration test asserting <C.Options> resolves to 'select' in componentTagMap.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
lib/glint.ts Adds resolveElementFromComponentRefType fallback when .element is any/unknown, enabling correct tag resolution for yielded curried refs.
test/glint.test.ts Adds an integration test that verifies <C.Options> resolves to 'select'.
test/glint-fixtures/yielded-curried-component.gts Adds a fixture exercising a yielded curried sub-component with Element: HTMLSelectElement.

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

Comment thread lib/glint.ts Outdated
Comment thread lib/glint.ts Outdated
Per Copilot review on #18:

1. Removed the reference to resolveElementFromSatisfiesTOC from the
   inline comment — that helper exists on a sibling branch
   (fix/fp-toc-satisfies-element / PR #16) but not on this one. Stale
   on its own.
2. Added a fallback path for when TOC's type-args land on a
   TypeReference rather than aliasTypeArguments (i.e., when TOC is
   declared as `interface TOC<S>` instead of `type TOC<S> = …`). The
   public `checker.getTypeArguments(refType as TypeReference)` covers
   that case. aliasTypeArguments handles the type-alias form.
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 3 out of 3 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 3 out of 3 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