Skip to content

Add template-no-duplicate-form-names: flag duplicate form-control name within a form#43

Closed
johanrd wants to merge 4 commits into
masterfrom
html-validate/template-no-duplicate-form-names
Closed

Add template-no-duplicate-form-names: flag duplicate form-control name within a form#43
johanrd wants to merge 4 commits into
masterfrom
html-validate/template-no-duplicate-form-names

Conversation

@johanrd

@johanrd johanrd commented Apr 22, 2026

Copy link
Copy Markdown
Owner

Note

This is part of a series where Claude has audited eslint-plugin-ember against jsx-a11y, vuejs-accessibility, angular-eslint, lit-a11y and html-validate, ember-template-lint, and the HTML and WCAG specs.

Summary

Add template-no-duplicate-form-names: flag two form controls sharing a name attribute within the same <form> (or template root if no enclosing form). Radio groups and repeated submit/reset/button types are explicitly allowed to share a name.

  • Premise 1: Per HTML spec §4.10.22.4 — Constructing the entry list, the serializer iterates submittable form controls and emits one entry per contributing control. Two regular controls with the same name both contribute, and server-side frameworks that expect a single value will silently see one — usually not the one the author intended.
  • Premise 2: The spec distinguishes three categories for name-collision purposes:
    • Non-submitting<input type="button"|"reset">, <button type="button"|"reset">. These do not contribute to the form-data entry list at all, so their name can't collide with anything.
    • Activation-only<input type="radio">, <input type="submit">, <button type="submit"> (and <button> with default type). Sharing a name with same-type siblings is the legitimate pattern: a radio group, or multiple submits distinguished by value.
    • Regular — everything else. Two regular controls with the same name is a real collision.
  • Conclusion: Skip non-submitting controls entirely. Allow activation-only groups to share a name with same-type siblings. Flag two regular controls (or mixed groups) with the same static name within the same <form> scope. Skip dynamic names (name={{this.field}}) and disabled controls.

What the rule does not try to detect

The rule does not try to answer "does this form actually submit data?" That question is not statically decidable from HBS alone:

  • <form method="get|post"> defaults to native submission, but event.preventDefault() in a {{on "submit"}} handler cancels it.
  • <form method="dialog"> defaults to dialog-close without constructing an entry list — but a {{on "submit"}} handler can still call new FormData(form) and observe duplicate names there. The FormData construction algorithm doesn't check the form's method.
  • form.submit() called from JS never fires a submit event, so form attributes are irrelevant to that path.

So we restrict ourselves to a control-level invariant (the skip condition above), which holds regardless of how — or whether — the form actually submits. A duplicate name on <input type="button"> can never collide with anything in any construction path. Duplicate regular-control names can, in any path that reads FormData, regardless of form method.

Fix

  • New rule lib/rules/template-no-duplicate-form-names.js; tests in tests/lib/rules/template-no-duplicate-form-names.js (44 cases).
  • Per-form Map<name, {type}> populated as we visit GlimmerElementNodes. findEnclosingFormOrRoot walks parents for the scoping form; absent form, the template root is the scope.
  • NON_SUBMITTING_TYPES = {button, reset}<input>/<button> of these types are skipped before the map is even consulted.
  • SHARED_NAME_TYPES = {radio, submit} — activation-only groups: same type in the same scope is allowed; mixing with a different shared type, or with a regular control, is flagged.

Prior art

Plugin Equivalent Verified behavior
jsx-a11y No equivalent rule.
vuejs-accessibility No equivalent rule.
lit-a11y No equivalent rule.
@angular-eslint/template no-duplicate-attributes Different concern — flags the same attribute twice on one element, not same name across controls.
html-validate form-dup-namespec Full implementation with name[] array handling, hidden-input-default-value pattern, and a complete form-associated-element registry. Our port is a subset.

Peer absence verified by grep -rli "form-dup\|dup-name\|duplicate.*name" on 2026-04-22.

Flags

<form>
  <input name='email' />
  <input name='email' />           {{! duplicate }}
</form>

<form>
  <input type='text' name='field' />
  <textarea name='field'></textarea>   {{! different tag, same name }}
</form>

<form>
  <input type='radio' name='a' />
  <input type='submit' name='a' />    {{! both in shared set, but different types }}
</form>

Allows

{{! Radio group — legitimate shared name }}
<form>
  <input type='radio' name='color' value='red' />
  <input type='radio' name='color' value='blue' />
</form>

{{! Multiple submit buttons with same name — legitimate }}
<form>
  <button type='submit' name='a' value='save'>Save</button>
  <button type='submit' name='a' value='publish'>Publish</button>
</form>

{{! Dynamic name — skipped }}
<form>
  <input name={{this.fieldName}} />
  <input name='email' />
</form>

{{! Disabled controls don't submit }}
<form>
  <input name='a' />
  <input name='a' disabled />
</form>

Edge case — not flagged (not endorsed either)

<form>
  <input type='button' name='x' />
  <input type='text' name='x' />
</form>

Why not flagged: <input type="button"> is spec-guaranteed to contribute nothing to FormData construction per HTML §4.10.22.4 step 5 — the algorithm skips it regardless of who invokes it (native submit, {{on "submit"}} handler, new FormData(form) called directly from JS, etc.). So there is no entry-list collision — the text input is the only contributor of name x.

This is not an endorsement of the pattern. Putting the same name on a button and a real field is weird and probably a mistake. The rule doesn't flag it only because the no-collision claim is airtight at the spec level. If you find yourself writing this, consider whether the button really needs a name attribute — usually it doesn't.

Notes

  • Not ported from html-validate (v1 scope): name[] array syntax; the <input type="hidden"> + <input type="checkbox"> default-value pattern. Both are rarely seen in Ember apps; can be added on demand.
  • Opt-in: not added to any preset config.

@github-actions

github-actions Bot commented Apr 22, 2026

Copy link
Copy Markdown

🏎️ Benchmark Comparison

Benchmark Control (p50) Experiment (p50) Δ
🟢 js small 15.06 ms 14.36 ms -4.7%
🟢 js medium 7.33 ms 7.04 ms -4.0%
js large 2.87 ms 2.82 ms -1.9%
gjs small 1.24 ms 1.22 ms -1.1%
gjs medium 619.60 µs 611.87 µs -1.2%
gjs large 246.91 µs 243.88 µs -1.2%
gts small 1.22 ms 1.23 ms +1.3%
gts medium 613.40 µs 613.85 µs +0.1%
gts large 244.14 µs 242.77 µs -0.6%

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

Full mitata output
clk: ~3.08 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.09 ms/iter  19.19 ms █                    
                      (12.62 ms … 32.03 ms)  29.55 ms █ ▅ ▂                
                    (  5.56 mb …  10.13 mb)   7.27 mb █▆█▆█▁▆▁█▁▆▃▁▁▃▃▁▁▃▁▃

js small (experiment)         14.91 ms/iter  15.54 ms  ▄▄ █   ▄            
                      (13.14 ms … 20.57 ms)  19.07 ms  ████▅  █            
                    (  6.75 mb …   8.16 mb)   6.84 mb ███████▁██▅▅▅▅▁▁▅▁▁▁▅

                             ┌                                            ┐
                             ╷┌──────────┬────┐                           ╷
          js small (control) ├┤          │    ├───────────────────────────┤
                             ╵└──────────┴────┘                           ╵
                              ╷ ┌──┬─┐        ╷
       js small (experiment)  ├─┤  │ ├────────┤
                              ╵ └──┴─┘        ╵
                             └                                            ┘
                             12.62 ms           21.09 ms           29.55 ms

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

------------------------------------------- -------------------------------
js medium (control)            7.99 ms/iter   8.37 ms  █                   
                       (6.83 ms … 13.30 ms)  12.53 ms ▂█▄                  
                    (  2.61 mb …   4.65 mb)   3.53 mb ███▆▂▆▃▃▅▂▃▂▂▃▂▂▂▁▁▁▂

js medium (experiment)         7.61 ms/iter   7.68 ms ▃█                   
                       (6.66 ms … 15.23 ms)  15.12 ms ██                   
                    (  2.97 mb …   3.98 mb)   3.51 mb ███▅▅▂▃▁▂▂▁▁▁▁▁▁▁▁▂▁▂

                             ┌                                            ┐
                              ╷┌────┬─┐                     ╷
         js medium (control)  ├┤    │ ├─────────────────────┤
                              ╵└────┴─┘                     ╵
                             ╷┌───┬                                       ╷
      js medium (experiment) ├┤   │───────────────────────────────────────┤
                             ╵└───┴                                       ╵
                             └                                            ┘
                             6.66 ms           10.89 ms            15.12 ms

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

------------------------------------------- -------------------------------
js large (control)             3.32 ms/iter   3.12 ms  █                   
                        (2.52 ms … 9.16 ms)   7.82 ms  █                   
                    (733.46 kb …   2.82 mb)   1.43 mb ▃█▇▃▂▂▁▂▁▁▂▁▂▂▂▁▁▁▁▁▁

js large (experiment)          3.08 ms/iter   2.92 ms  █                   
                        (2.62 ms … 7.55 ms)   6.02 ms  █                   
                    (294.81 kb …   2.61 mb)   1.43 mb ██▄▃▂▂▂▂▂▁▁▁▁▁▁▁▂▁▁▁▁

                             ┌                                            ┐
                             ╷ ┌────┬                                     ╷
          js large (control) ├─┤    │─────────────────────────────────────┤
                             ╵ └────┴                                     ╵
                              ╷┌──┬                        ╷
       js large (experiment)  ├┤  │────────────────────────┤
                              ╵└──┴                        ╵
                             └                                            ┘
                             2.52 ms            5.17 ms             7.82 ms

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

------------------------------------------- -------------------------------
gjs small (control)            1.37 ms/iter   1.33 ms █                    
                        (1.19 ms … 5.59 ms)   4.48 ms █▅                   
                    (220.88 kb …   1.66 mb)   1.06 mb ██▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs small (experiment)         1.35 ms/iter   1.26 ms █                    
                        (1.18 ms … 5.79 ms)   4.67 ms █                    
                    (224.15 kb …   1.91 mb)   1.06 mb █▅▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷┌┬                                        ╷
         gjs small (control) ├┤│────────────────────────────────────────┤
                             ╵└┴                                        ╵
                             ┌─┬                                          ╷
      gjs small (experiment) │ │──────────────────────────────────────────┤
                             └─┴                                          ╵
                             └                                            ┘
                             1.18 ms            2.93 ms             4.67 ms

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

------------------------------------------- -------------------------------
gjs medium (control)         674.22 µs/iter 637.96 µs █                    
                      (587.54 µs … 5.55 ms)   3.57 ms █                    
                    ( 94.53 kb …   1.10 mb) 541.99 kb █▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs medium (experiment)      658.21 µs/iter 631.67 µs  █                   
                      (582.52 µs … 4.78 ms)   1.24 ms ▃█                   
                    ( 98.80 kb … 991.32 kb) 540.91 kb ███▂▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌┬                                           ╷
        gjs medium (control) ││───────────────────────────────────────────┤
                             └┴                                           ╵
                             ┌┬        ╷
     gjs medium (experiment) ││────────┤
                             └┴        ╵
                             └                                            ┘
                             582.52 µs           2.08 ms            3.57 ms

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

------------------------------------------- -------------------------------
gjs large (control)          271.08 µs/iter 263.32 µs ▂█                   
                      (235.99 µs … 4.64 ms) 393.48 µs ██▃                  
                    (153.67 kb … 989.72 kb) 217.13 kb ███▅█▇▂▂▂▁▁▁▁▁▁▁▁▁▁▁▁

gjs large (experiment)       266.94 µs/iter 261.22 µs  █                   
                      (234.34 µs … 4.33 ms) 383.24 µs ▆█                   
                    (183.82 kb … 801.95 kb) 216.59 kb ██▇▃█▆▂▂▂▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷ ┌───────┬                                  ╷
         gjs large (control) ├─┤       │──────────────────────────────────┤
                             ╵ └───────┴                                  ╵
                             ╷┌───────┬                                ╷
      gjs large (experiment) ├┤       │────────────────────────────────┤
                             ╵└───────┴                                ╵
                             └                                            ┘
                             234.34 µs         313.91 µs          393.48 µs

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

------------------------------------------- -------------------------------
gts small (control)            1.31 ms/iter   1.24 ms █                    
                        (1.18 ms … 5.82 ms)   4.84 ms █                    
                    (222.06 kb …   1.49 mb)   1.06 mb █▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gts small (experiment)         1.32 ms/iter   1.25 ms █                    
                        (1.20 ms … 5.66 ms)   4.62 ms █                    
                    (536.62 kb …   1.69 mb)   1.05 mb █▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌─┬                                          ╷
         gts small (control) │ │──────────────────────────────────────────┤
                             └─┴                                          ╵
                             ┌─┬                                       ╷
      gts small (experiment) │ │───────────────────────────────────────┤
                             └─┴                                       ╵
                             └                                            ┘
                             1.18 ms            3.01 ms             4.84 ms

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

------------------------------------------- -------------------------------
gts medium (control)         656.69 µs/iter 628.26 µs  █                   
                      (583.50 µs … 4.96 ms)   1.37 ms ▃█                   
                    (107.47 kb …   1.68 mb) 541.45 kb ██▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gts medium (experiment)      662.44 µs/iter 627.68 µs █                    
                      (583.76 µs … 5.17 ms)   3.24 ms █                    
                    (261.62 kb …   1.38 mb) 541.06 kb █▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌┬           ╷
        gts medium (control) ││───────────┤
                             └┴           ╵
                             ┌┬                                           ╷
     gts medium (experiment) ││───────────────────────────────────────────┤
                             └┴                                           ╵
                             └                                            ┘
                             583.50 µs           1.91 ms            3.24 ms

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

------------------------------------------- -------------------------------
gts large (control)          264.62 µs/iter 260.14 µs  █                   
                      (233.53 µs … 5.14 ms) 316.40 µs  █▂▆                 
                    (182.70 kb …   1.00 mb) 217.01 kb ▄███▄▂▃▇▄▅▃▂▁▁▁▁▁▁▁▁▁

gts large (experiment)       266.84 µs/iter 259.50 µs  █▄                  
                      (233.46 µs … 4.69 ms) 330.70 µs  ██                  
                    (215.70 kb … 905.02 kb) 216.81 kb ███▅▃▄█▆▄▂▁▂▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷ ┌───────────┬                       ╷
         gts large (control) ├─┤           │───────────────────────┤
                             ╵ └───────────┴                       ╵
                             ╷ ┌────────────┬                             ╷
      gts large (experiment) ├─┤            │─────────────────────────────┤
                             ╵ └────────────┴                             ╵
                             └                                            ┘
                             233.46 µs         282.08 µs          330.70 µs

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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds a new Ember template lint rule to detect duplicate static name attributes across submittable form controls within the same <form> scope (or template root when no enclosing form), with explicit allowances for radio groups and repeated submit controls.

Changes:

  • Added template-no-duplicate-form-names rule implementation with per-form/root name tracking and control-type exceptions.
  • Added comprehensive rule tests for both GJS (<template>) and HBS parsing modes.
  • Added rule documentation and listed the rule in the README rules table.

Reviewed changes

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

File Description
lib/rules/template-no-duplicate-form-names.js Implements the new duplicate-form-control-name detection rule with form scoping and type-based exceptions.
tests/lib/rules/template-no-duplicate-form-names.js Adds valid/invalid test cases for the rule in both GJS and HBS modes.
docs/rules/template-no-duplicate-form-names.md Documents rule behavior, rationale, and limitations.
README.md Adds the rule to the rules index/table.

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

Comment thread tests/lib/rules/template-no-duplicate-form-names.js Outdated
Comment thread docs/rules/template-no-duplicate-form-names.md Outdated
Comment thread lib/rules/template-no-duplicate-form-names.js Outdated

Copilot AI left a comment

Copy link
Copy Markdown

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 5 comments.


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

Comment thread lib/rules/template-no-duplicate-form-names.js
Comment thread lib/rules/template-no-duplicate-form-names.js Outdated
Comment thread lib/rules/template-no-duplicate-form-names.js
Comment thread docs/rules/template-no-duplicate-form-names.md Outdated
Comment thread tests/lib/rules/template-no-duplicate-form-names.js Outdated

Copilot AI left a comment

Copy link
Copy Markdown

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 8 out of 8 changed files in this pull request and generated 8 comments.


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

Comment thread lib/rules/template-no-duplicate-landmark-elements.js Outdated
Comment thread docs/rules/template-no-duplicate-form-names.md Outdated
Comment thread lib/rules/template-no-duplicate-form-names.js Outdated
Comment thread lib/rules/template-no-duplicate-form-names.js Outdated
Comment thread tests/lib/utils/control-flow-test.js Outdated
Comment thread tests/lib/utils/control-flow-test.js Outdated
Comment thread lib/utils/control-flow.js Outdated
Comment thread lib/utils/control-flow.js Outdated
johanrd added a commit that referenced this pull request Apr 24, 2026
…tton type fallback

- lib/utils/control-flow.js: header comment & createConditionalScope
  example updated to reflect post-PR API (isBranchingBlockStatement /
  isConditionalBlockBranch / each-with-else support)
- tests/lib/utils/control-flow-test.js: drop unused imports (isConditional
  BlockBranch, setParent)
- lib/rules/template-no-duplicate-form-names.js: header comment now
  reflects the full 'share category' taxonomy (radio group + submit-like
  mix); getControlType treats invalid/unknown button[type] as 'submit'
  per HTML §4.10.9 default, and invalid/unknown input[type] as 'text'
  per HTML §4.10.5
- docs/rules/template-no-duplicate-form-names.md: mention input[type=image]
  and bare <button> explicitly; rewrite the share-category explanation
@johanrd
johanrd requested a review from Copilot April 24, 2026 13:38

Copilot AI left a comment

Copy link
Copy Markdown

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 8 out of 8 changed files in this pull request and generated 3 comments.


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

Comment thread lib/rules/template-no-duplicate-form-names.js Outdated
Comment thread lib/utils/control-flow.js Outdated
Comment thread lib/utils/control-flow.js

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@johanrd
johanrd requested a review from Copilot April 24, 2026 18:15

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@johanrd
johanrd requested a review from Copilot April 24, 2026 18:57

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@johanrd
johanrd requested a review from Copilot April 26, 2026 08:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI left a comment

Copy link
Copy Markdown

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 8 out of 8 changed files in this pull request and generated 7 comments.


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

Comment thread docs/rules/template-no-duplicate-form-names.md
Comment thread docs/rules/template-no-duplicate-form-names.md Outdated
Comment thread lib/rules/template-no-duplicate-form-names.js
Comment thread lib/rules/template-no-duplicate-form-names.js
Comment thread lib/rules/template-no-duplicate-form-names.js
Comment thread lib/rules/template-no-duplicate-form-names.js
Comment thread lib/rules/template-no-duplicate-form-names.js
johanrd added a commit that referenced this pull request Apr 27, 2026
johanrd added a commit that referenced this pull request Apr 27, 2026
…tton type fallback

- lib/utils/control-flow.js: header comment & createConditionalScope
  example updated to reflect post-PR API (isBranchingBlockStatement /
  isConditionalBlockBranch / each-with-else support)
- tests/lib/utils/control-flow-test.js: drop unused imports (isConditional
  BlockBranch, setParent)
- lib/rules/template-no-duplicate-form-names.js: header comment now
  reflects the full 'share category' taxonomy (radio group + submit-like
  mix); getControlType treats invalid/unknown button[type] as 'submit'
  per HTML §4.10.9 default, and invalid/unknown input[type] as 'text'
  per HTML §4.10.5
- docs/rules/template-no-duplicate-form-names.md: mention input[type=image]
  and bare <button> explicitly; rewrite the share-category explanation
@johanrd
johanrd force-pushed the html-validate/template-no-duplicate-form-names branch from b24e352 to 152f800 Compare April 27, 2026 14:01
@johanrd
johanrd force-pushed the html-validate/template-no-duplicate-form-names branch from 152f800 to 31c618c Compare April 27, 2026 14:59
@johanrd
johanrd force-pushed the html-validate/template-no-duplicate-form-names branch from b8c7c40 to 0c19b04 Compare April 27, 2026 19:32
@johanrd
johanrd requested a review from Copilot April 27, 2026 19:34

Copilot AI left a comment

Copy link
Copy Markdown

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 8 out of 8 changed files in this pull request and generated 2 comments.


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

Comment on lines +140 to +145
if (t.kind === 'absent') {
return 'submit';
}
// Dynamic or empty type (mustache / concat / valueless) — the runtime
// value is unknown. Return a sentinel so the caller can skip duplicate-
// name checks for this node rather than baking in a wrong default.

Copilot AI Apr 27, 2026

Copy link

Choose a reason for hiding this comment

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

getControlType() treats a valueless <button type> (represented as an attribute with no value, i.e. t.kind === 'empty') as unknown and skips duplicate-name checks for that button. In HTML, an empty/invalid type value falls back to the default (submit for <button>), so this creates a false negative. Consider treating t.kind === 'empty' the same as absent (default to submit) for <button> type parsing, and reserve unknown for truly dynamic values (mustache/concat).

Suggested change
if (t.kind === 'absent') {
return 'submit';
}
// Dynamic or empty type (mustache / concat / valueless) — the runtime
// value is unknown. Return a sentinel so the caller can skip duplicate-
// name checks for this node rather than baking in a wrong default.
if (t.kind === 'absent' || t.kind === 'empty') {
return 'submit';
}
// Dynamic type (mustache / concat) — the runtime value is unknown.
// Return a sentinel so the caller can skip duplicate-name checks for
// this node rather than baking in a wrong default.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

DISAGREE — getControlType handles kind:'empty' for input → 'text'. No false negative here.

Comment thread lib/utils/control-flow.js
conditionalStack.at(-1).add(key);
}
} else {
seenStack.push(keys);

Copilot AI Apr 27, 2026

Copy link

Choose a reason for hiding this comment

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

createConditionalScope.exitConditional() promotes keys from a finished conditional by seenStack.push(keys), which grows seenStack by one Set per conditional in the template. Since has() linearly scans seenStack, this can degrade to O(#conditionals) per lookup in large templates. You can avoid this unbounded stack growth by merging keys into the current outer set (e.g. seenStack.at(-1)) when leaving the outermost conditional instead of pushing a new set.

Suggested change
seenStack.push(keys);
for (const key of keys) {
seenStack.at(-1).add(key);
}

Copilot uses AI. Check for mistakes.
@johanrd johanrd closed this Apr 28, 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.

2 participants