Check named-block content where its yield sits - #61
Conversation
A component call becomes its root element in the blanked output, so named-block content went directly under that root. When the block yields in a deeper element (a <td> or <tbody> under a <table> root), parent rules gave false errors on that content.
🏎️ Benchmark Comparison
Full output |
There was a problem hiding this comment.
🔵 Needs a closer look
The change alters shared resolver/blanking suppression logic that broadly disables parent-dependent validation rules, a correctness-sensitive tradeoff (potential false negatives) that warrants final human judgment despite the change appearing internally consistent and well-tested.
Pull request overview
This PR fixes false positives in the --glint (and no-Glint canonical resolver) validation path where a component call is blanked to its root element. When a component's named block yields into a deeper element (e.g. a <table> root that yields actions inside a <td> and rows inside a <tbody>), the consumer's block content lands directly under the root in the blanked output but under a deeper element at runtime, causing parent-dependent rules (element-permitted-content, prefer-tbody, etc.) to fire incorrectly. The resolver now records which blocks yield below the root (nestedYieldBlocks), and the blanker suppresses parent-dependent rules on those blocks' top-level consumer content, while blocks that yield directly in the root (e.g. <:caption>) remain checked.
Changes:
- Resolver walk refactored
findYieldAncestoraround a newcollectYieldSiteshelper and addedfindNestedYieldBlocks, exposingnestedYieldBlocksthroughTagResolution/ChosenSubstitutionand both resolution-map builders. blank.tsaddscollectNestedBlockContentOffsetsplus aPARENT_DEPENDENT_RULESlist and per-element disables for nested-yield block content.- New fixtures/example and an
it.each(['1','0'])integration test asserting the real<:caption>error still surfaces under Glint on and off.
File summaries
| File | Description |
|---|---|
| lib/resolver/walk.ts | Adds collectYieldSites/findNestedYieldBlocks, refactors findYieldAncestor, populates nestedYieldBlocks on native and element-helper-let resolutions. |
| lib/resolver/build-maps.ts | Plumbs nestedYieldBlocks into the canonical (no-Glint) component attr map. |
| lib/glint.ts | Plumbs nestedYieldBlocks into the Glint-path component attr map. |
| lib/builtin-components.ts | Documents and adds the optional nestedYieldBlocks field on ComponentAttrs. |
| blank.ts | Collects top-level offsets of nested-yield block content and disables PARENT_DEPENDENT_RULES per element. |
| test/glint-fixtures/cell-yield-table-leaf.gts | New leaf component with named blocks yielding at different depths. |
| examples/cell-yield-table-consumer.gts | New consumer exercising nested-yield blocks plus a real <:caption> error. |
| test/integration.test.ts | New test asserting only the genuine <:caption> error remains, Glint on/off. |
Review details
- Files reviewed: 8/8 changed files
- Comments generated: 0
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
With
--glint, a component call becomes its root element in the blanked output. The content of each named block goes directly under that root. At runtime, the content goes where{{yield to=…}}is. When that yield is in a deeper element, the parent rules give false errors.Example: a component with a
<table>root yieldsactionsin a<td>androwsin a<tbody>. A<button>in<:actions>giveselement-permitted-content. A<tr>in<:rows>givesprefer-tbody.Change
nestedYieldBlocks).Test:
cell-yield-table-consumer.gts, with Glint on and off. The real error in<:caption>still shows.Cowritten by Claude