Skip to content

BUGFIX: template-no-yield-only — guard against native <template> as first HBS child#59

Closed
johanrd wants to merge 1 commit intomasterfrom
fix/template-no-yield-only-body-length-guard
Closed

BUGFIX: template-no-yield-only — guard against native <template> as first HBS child#59
johanrd wants to merge 1 commit intomasterfrom
fix/template-no-yield-only-body-length-guard

Conversation

@johanrd
Copy link
Copy Markdown
Owner

@johanrd johanrd commented Apr 24, 2026

Summary

  • template-no-yield-only's gjs-wrapper detection descends into body[0].children whenever body[0].tag === 'template', without checking body.length. In classic HBS, a native HTML <template> element can appear anywhere, and the rule mistakes it for the GJS module wrapper.
  • Concrete false positive: <template>{{yield}}</template>{{#each items}}item{{/each}}. The rule walks the inner <template>'s children, sees only {{yield}}, and flags the whole template as yield-only — even though the real template has a {{#each}} block and is not yield-only.
  • Fix: require body.length === 1 before descending, matching the shape of the sibling rule template-no-bare-yield.js.

Context

Closes drift with template-no-bare-yield.js, which has this guard. Both rules are near-duplicates of the same upstream ember-template-lint/no-yield-only; the sibling kept pace, this one didn't.

Independent of ember-cli#2736 (ember-cli/eslint-plugin-ember) and the CI fix there — that PR addresses the comment-node handling; this one addresses a separately latent wrapper-detection bug. They touch the same file but different regions.

Test plan

  • Added an HBS-only regression case <template>{{yield}}</template>{{#each items}}item{{/each}} to validHbs; without the guard it false-positives, with it the template is correctly accepted.
  • The new case is HBS-only because GJS/GTS disallows nested <template> (parse error); split into validHbsOnly and fed to the hbs rule tester.
  • pnpm exec vitest run tests/lib/rules/template-no-yield-only.js — 19/19 pass.

…first HBS child

In classic HBS, a native HTML `<template>` element can appear anywhere in
a template body — including as the first child. The current gjs-wrapper
detection descends into `body[0].children` whenever `body[0].tag === 'template'`,
so an HBS template like `<template>{{yield}}</template>{{#each items}}…{{/each}}`
is mistakenly treated as the GJS `<template>` module wrapper: the rule
walks the inner `<template>`'s children, sees only `{{yield}}`, and
flags it as yield-only — a false positive.

The GJS module wrapper is always `body.length === 1`. Add that guard so
we only descend in the actual GJS/GTS case. Matches the shape of the
sibling rule `template-no-bare-yield.js`.

Added an HBS-only regression test (the construct is invalid in GJS/GTS,
where a nested `<template>` is a parse error).
@github-actions
Copy link
Copy Markdown

🏎️ Benchmark Comparison

Benchmark Control (p50) Experiment (p50) Δ
js small 14.67 ms 14.60 ms -0.5%
🟢 js medium 7.32 ms 7.07 ms -3.4%
🟢 js large 2.90 ms 2.81 ms -2.9%
🔴 gjs small 1.27 ms 1.34 ms +5.3%
gjs medium 623.23 µs 623.82 µs +0.1%
gjs large 247.55 µs 246.02 µs -0.6%
gts small 1.24 ms 1.25 ms +0.3%
gts medium 621.10 µs 618.84 µs -0.4%
gts large 245.67 µs 245.64 µs -0.0%

🟢 faster · 🔴 slower · 🟠 slightly slower · ⚪ within 2%

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

benchmark                   avg (min … max) p75 / p99    (min … top 1%)
------------------------------------------- -------------------------------
js small (control)            17.44 ms/iter  19.33 ms █ ▅                  
                      (12.71 ms … 32.20 ms)  28.50 ms █▂█ ▂                
                    (  5.19 mb …  10.13 mb)   7.26 mb ███▄█▇▁▄▄▄▁▁▁▄▁▄▄▄▄▄▄

js small (experiment)         15.17 ms/iter  15.70 ms  █                   
                      (13.43 ms … 22.61 ms)  22.40 ms ▅█▂▇▂                
                    (  6.75 mb …   8.30 mb)   6.83 mb ██████▆▃▁▁█▁▁▁▁▁▁▁▁▁▃

                             ┌                                            ┐
                             ╷┌───────────┬─────┐                         ╷
          js small (control) ├┤           │     ├─────────────────────────┤
                             ╵└───────────┴─────┘                         ╵
                               ╷┌───┬─┐                  ╷
       js small (experiment)   ├┤   │ ├──────────────────┤
                               ╵└───┴─┘                  ╵
                             └                                            ┘
                             12.71 ms           20.60 ms           28.50 ms

summary
  js small (experiment)
   1.15x faster than js small (control)

------------------------------------------- -------------------------------
js medium (control)            8.01 ms/iter   8.48 ms  █                   
                       (6.86 ms … 14.54 ms)  13.37 ms ▂█                   
                    (  2.93 mb …   4.27 mb)   3.54 mb ██▇▅▂▄▄▄▁▂▁▄▂▂▁▁▂▁▁▁▂

js medium (experiment)         7.72 ms/iter   7.71 ms █▆                   
                       (6.76 ms … 14.51 ms)  13.36 ms ██                   
                    (  1.37 mb …   5.62 mb)   3.51 mb ██▆▃▃▄▃▃▁▂▃▂▂▁▁▁▁▂▂▁▂

                             ┌                                            ┐
                              ╷┌─────┬───┐                                ╷
         js medium (control)  ├┤     │   ├────────────────────────────────┤
                              ╵└─────┴───┘                                ╵
                             ╷┌─────┬                                     ╷
      js medium (experiment) ├┤     │─────────────────────────────────────┤
                             ╵└─────┴                                     ╵
                             └                                            ┘
                             6.76 ms           10.06 ms            13.37 ms

summary
  js medium (experiment)
   1.04x faster than js medium (control)

------------------------------------------- -------------------------------
js large (control)             3.39 ms/iter   3.29 ms  █                   
                        (2.69 ms … 8.85 ms)   8.50 ms ██                   
                    (300.85 kb …   3.39 mb)   1.44 mb ██▃▃▂▃▂▂▁▂▂▂▂▁▁▁▁▁▁▁▁

js large (experiment)          3.10 ms/iter   2.92 ms  █                   
                        (2.63 ms … 7.53 ms)   5.97 ms  █                   
                    (239.32 kb …   3.32 mb)   1.43 mb ██▄▂▂▂▂▂▂▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷┌────┬                                      ╷
          js large (control) ├┤    │──────────────────────────────────────┤
                             ╵└────┴                                      ╵
                             ╷┌──┬                     ╷
       js large (experiment) ├┤  │─────────────────────┤
                             ╵└──┴                     ╵
                             └                                            ┘
                             2.63 ms            5.57 ms             8.50 ms

summary
  js large (experiment)
   1.09x faster than js large (control)

------------------------------------------- -------------------------------
gjs small (control)            1.43 ms/iter   1.36 ms █                    
                        (1.21 ms … 7.10 ms)   5.72 ms █▃                   
                    (283.16 kb …   1.66 mb)   1.06 mb ██▁▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs small (experiment)         1.83 ms/iter   2.36 ms █                    
                        (1.23 ms … 8.86 ms)   6.78 ms █   ▅                
                    (354.13 kb …   1.78 mb)   1.06 mb █▄▁▃█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌─┬                                 ╷
         gjs small (control) │ │─────────────────────────────────┤
                             └─┴                                 ╵
                             ┌────┬───┐                                   ╷
      gjs small (experiment) │    │   ├───────────────────────────────────┤
                             └────┴───┘                                   ╵
                             └                                            ┘
                             1.21 ms            3.99 ms             6.78 ms

summary
  gjs small (control)
   1.28x faster than gjs small (experiment)

------------------------------------------- -------------------------------
gjs medium (control)         735.20 µs/iter 674.79 µs █                    
                      (585.56 µs … 6.30 ms)   3.51 ms █                    
                    (372.55 kb …   1.15 mb) 542.94 kb ██▂▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs medium (experiment)      682.62 µs/iter 643.16 µs █                    
                      (589.99 µs … 6.11 ms)   3.02 ms █                    
                    (207.55 kb …   1.08 mb) 541.18 kb █▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌─┬                                          ╷
        gjs medium (control) │ │──────────────────────────────────────────┤
                             └─┴                                          ╵
                             ┌┬                                    ╷
     gjs medium (experiment) ││────────────────────────────────────┤
                             └┴                                    ╵
                             └                                            ┘
                             585.56 µs           2.05 ms            3.51 ms

summary
  gjs medium (experiment)
   1.08x faster than gjs medium (control)

------------------------------------------- -------------------------------
gjs large (control)          280.84 µs/iter 264.54 µs █                    
                      (235.89 µs … 5.83 ms) 712.22 µs ██                   
                    (153.77 kb … 802.31 kb) 216.92 kb ██▆▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs large (experiment)       271.23 µs/iter 261.75 µs  █                   
                      (234.55 µs … 5.31 ms) 385.00 µs ▂█▆                  
                    ( 46.23 kb …   1.06 mb) 216.81 kb ███▄█▆▂▂▂▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷┌──┬                                        ╷
         gjs large (control) ├┤  │────────────────────────────────────────┤
                             ╵└──┴                                        ╵
                             ┌──┬          ╷
      gjs large (experiment) │  │──────────┤
                             └──┴          ╵
                             └                                            ┘
                             234.55 µs         473.38 µs          712.22 µs

summary
  gjs large (experiment)
   1.04x faster than gjs large (control)

------------------------------------------- -------------------------------
gts small (control)            1.33 ms/iter   1.27 ms █                    
                        (1.20 ms … 6.27 ms)   4.45 ms █                    
                    (667.51 kb …   1.57 mb)   1.06 mb █▄▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gts small (experiment)         1.34 ms/iter   1.27 ms █                    
                        (1.21 ms … 6.27 ms)   4.65 ms █                    
                    (220.16 kb …   1.80 mb)   1.05 mb █▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌─┬                                       ╷
         gts small (control) │ │───────────────────────────────────────┤
                             └─┴                                       ╵
                             ┌─┬                                          ╷
      gts small (experiment) │ │──────────────────────────────────────────┤
                             └─┴                                          ╵
                             └                                            ┘
                             1.20 ms            2.93 ms             4.65 ms

summary
  gts small (control)
   1.01x faster than gts small (experiment)

------------------------------------------- -------------------------------
gts medium (control)         673.82 µs/iter 635.76 µs  █                   
                      (588.52 µs … 5.93 ms)   1.69 ms ▇█                   
                    ( 55.32 kb …   1.07 mb) 542.30 kb ██▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gts medium (experiment)      667.08 µs/iter 634.25 µs  █                   
                      (586.13 µs … 5.52 ms)   1.40 ms ▂█                   
                    ( 93.48 kb …   1.04 mb) 539.80 kb ██▄▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷┌──┬                                        ╷
        gts medium (control) ├┤  │────────────────────────────────────────┤
                             ╵└──┴                                        ╵
                             ╷┌─┬                             ╷
     gts medium (experiment) ├┤ │─────────────────────────────┤
                             ╵└─┴                             ╵
                             └                                            ┘
                             586.13 µs           1.14 ms            1.69 ms

summary
  gts medium (experiment)
   1.01x faster than gts medium (control)

------------------------------------------- -------------------------------
gts large (control)          267.38 µs/iter 261.30 µs  █▂                  
                      (234.29 µs … 5.16 ms) 354.41 µs  ██                  
                    (216.09 kb … 667.23 kb) 216.99 kb ███▆▅█▆▄▂▂▂▁▁▁▁▁▁▁▁▁▁

gts large (experiment)       269.27 µs/iter 262.09 µs  █▂                  
                      (234.59 µs … 4.82 ms) 347.79 µs  ██                  
                    ( 16.63 kb … 692.91 kb) 216.55 kb ▆██▆▃█▆▅▂▂▂▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷ ┌─────────┬                                ╷
         gts large (control) ├─┤         │────────────────────────────────┤
                             ╵ └─────────┴                                ╵
                             ╷ ┌──────────┬                             ╷
      gts large (experiment) ├─┤          │─────────────────────────────┤
                             ╵ └──────────┴                             ╵
                             └                                            ┘
                             234.29 µs         294.35 µs          354.41 µs

summary
  gts large (control)
   1.01x faster than gts large (experiment)

@johanrd johanrd closed this Apr 24, 2026
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.

1 participant