Skip to content

Check named-block content where its yield sits - #61

Merged
johanrd merged 2 commits into
mainfrom
fix/named-block-yield-parent
Sep 15, 2026
Merged

johanrd merged 2 commits into
mainfrom
fix/named-block-yield-parent

Conversation

@johanrd

@johanrd johanrd commented Sep 15, 2026

Copy link
Copy Markdown
Owner

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 yields actions in a <td> and rows in a <tbody>. A <button> in <:actions> gives element-permitted-content. A <tr> in <:rows> gives prefer-tbody.

Change

  • The resolver records the blocks that do not yield directly in the root (nestedYieldBlocks).
  • The blanker disables the parent rules on the top-level elements of these blocks. A block that yields directly in the root is still checked.

Test: cell-yield-table-consumer.gts, with Glint on and off. The real error in <:caption> still shows.

Cowritten by Claude

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.
@johanrd johanrd added bug Something isn't working run-ecosystem-ci Run the (expensive) ecosystem CI on this PR run-bench Run the benchmark comparison on this pull request labels Sep 15, 2026
@github-actions

github-actions Bot commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

🏎️ Benchmark Comparison

Benchmark Control (p50) Experiment (p50) Δ
⚪ extract small template 779.12 µs 785.35 µs +0.8%
⚪ extract medium template 3.20 ms 3.18 ms -0.7%
⚪ extract cross-file resolution 2.88 ms 2.95 ms +2.3%
⚪ extract large template 692.94 µs 691.18 µs -0.3%
⚪ cold run (cache off) 5534.72 ms 5328.91 ms -3.7%
⚪ warm run (all cached) 281.29 ms 283.29 ms +0.7%
⚪ one cached file 266.88 ms 274.51 ms +2.9%
⚪ no glint 4303.76 ms 4307.10 ms +0.1%

🟢 faster · 🔴 slower · 🟡 5–10 %, possibly noise · ⚪ within 5 %

Full output
clk: ~3.09 GHz
cpu: AMD EPYC 7763 64-Core Processor
runtime: node 24.20.0 (x64-linux)

benchmark                    avg (min … max) p75 / p99    (min … top 1%)
-------------------------------------------- -------------------------------
extract small template          1.15 ms/iter   1.11 ms  █                   
                       (632.37 µs … 6.45 ms)   4.08 ms ██                   
                     (  6.78 kb …   3.83 mb) 311.70 kb ███▆▃▂▂▂▂▂▁▁▁▂▂▂▁▂▁▁▁

extract medium template         3.90 ms/iter   4.05 ms  █                   
                        (2.70 ms … 11.20 ms)  10.86 ms ▂█                   
                     ( 36.41 kb …   4.19 mb) 761.64 kb ███▃▄▃▂▂▄▁▂▁▁▁▁▁▁▁▁▁▁

extract cross-file resolution   3.23 ms/iter   3.55 ms   █                  
                         (2.47 ms … 6.41 ms)   5.90 ms ▂██▃                 
                     (125.09 kb …   1.42 mb) 429.39 kb ████▅▄▄▆▄▃▂▃▂▄▂▂▁▃▁▁▁

extract large template        778.15 µs/iter 754.35 µs ▇█                   
                       (603.82 µs … 5.64 ms)   3.41 ms ██                   
                     (  4.54 kb …   1.99 mb)  50.93 kb ██▂▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
clk: ~3.09 GHz
cpu: AMD EPYC 7763 64-Core Processor
runtime: node 24.20.0 (x64-linux)

benchmark                    avg (min … max) p75 / p99    (min … top 1%)
-------------------------------------------- -------------------------------
extract small template          1.20 ms/iter   1.14 ms ▇█                   
                       (625.62 µs … 5.92 ms)   4.87 ms ██▃                  
                     ( 15.05 kb …   3.46 mb) 310.77 kb ███▄▃▃▁▂▂▂▂▂▂▂▂▁▁▁▁▁▁

extract medium template         3.79 ms/iter   3.99 ms  █                   
                        (2.69 ms … 10.26 ms)   9.15 ms  █▃                  
                     ( 91.38 kb …   4.19 mb) 780.43 kb ███▆▄▄▂▂▃▃▁▂▁▂▁▁▂▁▁▁▁

extract cross-file resolution   3.24 ms/iter   3.37 ms  ▅▂█                 
                         (2.45 ms … 6.75 ms)   6.03 ms  ███▂                
                     (183.05 kb …   1.34 mb) 427.45 kb █████▇▄▆▄▃▃▃▂▃▃▂▄▂▁▂▂

extract large template        734.14 µs/iter 751.35 µs  █▃                  
                       (595.45 µs … 3.35 ms)   1.91 ms  ██                  
                     ( 12.68 kb …   1.89 mb)  51.07 kb ████▂▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 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 findYieldAncestor around a new collectYieldSites helper and added findNestedYieldBlocks, exposing nestedYieldBlocks through TagResolution/ChosenSubstitution and both resolution-map builders.
  • blank.ts adds collectNestedBlockContentOffsets plus a PARENT_DEPENDENT_RULES list 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.

@johanrd
johanrd merged commit 9a7b0fb into main Sep 15, 2026
4 checks passed
@github-actions github-actions Bot mentioned this pull request Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working run-bench Run the benchmark comparison on this pull request run-ecosystem-ci Run the (expensive) ecosystem CI on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants