Skip to content

Add template-require-input-type: reject invalid input types; opt-in requireExplicit#40

Draft
johanrd wants to merge 4 commits intomasterfrom
html-validate/template-require-input-type
Draft

Add template-require-input-type: reject invalid input types; opt-in requireExplicit#40
johanrd wants to merge 4 commits intomasterfrom
html-validate/template-require-input-type

Conversation

@johanrd
Copy link
Copy Markdown
Owner

@johanrd johanrd commented Apr 22, 2026

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-require-input-type: reject <input type="..."> values that aren't one of the 22 defined HTML input types (always on — catches silent fallback-to-text bugs). Also provides an opt-in requireExplicit: true mode that flags <input> without a type attribute as a style/consistency check, analogous to the existing template-require-button-type.

  • Premise 1 (correctness): HTML spec §4.10.5 — the input element defines 22 input type states, and the spec says of type: "The attribute's missing value default and invalid value default are both the Text state." So <input type="foo"> silently falls back to text without any error. That's a genuine silent-failure class: the author wrote a value that looks meaningful (or miskeyed a valid one, e.g. tex) and the browser quietly does something else.
  • Premise 2 (style/consistency): <input> with no type attribute is 100% spec-compliant — the missing-value default is Text — so a bare <input> is not a bug per se. Requiring an explicit type is an authoring-consistency choice analogous to the existing template-require-button-type rule: explicit is easier to grep, easier to review, and signals intent even when the author wanted text. This half of the rule is style, not correctness.
  • Conclusion: Flag <input type="..."> with a value outside the HTML-defined set (correctness, always on). Offer a requireExplicit: true schema option (default false) to also flag <input> without a static type — off by default because the correctness argument for inputs is thinner than for <button> (no accidental-submit footgun), so forcing the style half onto every codebase produces false-positive noise. Skip dynamic (type={{this.t}}) values. Fixable to type="text".

Fix

  • New rule lib/rules/template-require-input-type.js; tests in tests/lib/rules/template-require-input-type.js (40 cases, covering both default and requireExplicit: true modes).
  • Schema: { requireExplicit: boolean } (default false). When false, the rule only flags invalid type values; missing type is allowed. When true, also flags <input> without a static type.
  • Mirrors template-require-button-type structure — both meta-shape and visitor. Key difference: inserts the type attribute right after <input rather than trying to parse the open tag (the button rule's regex mis-places attributes past / in self-closing form with other attrs; we don't want that bug).
  • Fix: fixer.insertTextBeforeRange([node.range[0] + 6, ...], ' type="text"').

Prior art

Plugin Equivalent Verified behavior
jsx-a11y No equivalent for <input>.
vuejs-accessibility No equivalent.
lit-a11y No equivalent.
@angular-eslint/template button-has-type Exists for <button>, not <input>. Same design, different tag.
html-validate no-implicit-input-typespec Reports <input> missing a type attribute. Does NOT validate the value (our rule also rejects unknown values, additive).

Peer absence verified by grep -rli input-has-type\|input-type across peer snapshots on 2026-04-22.

Flags (default: invalid values only)

<input type='' />
<input type='foo' />
<input type='TEXTY' />

Flags (with requireExplicit: true)

Adds missing-type to the above:

<input />
<input name='email' />

Allows

<input type='text' />
<input type='email' />
<input type='datetime-local' />
<input type={{this.inputType}} />    {{! dynamic — skipped }}
{{! With default config (requireExplicit: false): }}
<input />
<input name='email' />

Notes

  • Opt-in: not added to any preset config. requireExplicit also off by default.
  • Fixable with --fix to type="text" (the HTML default).
  • template-require-button-type flags <button> without type because the default is submit, which inside a <form> creates an accidental-submit footgun. <input> without type defaults to text, which is benign — hence the split: always flag invalid values (real silent bug), opt-in to flag missing values (style parity with the button rule).

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 22, 2026

🏎️ Benchmark Comparison

Benchmark Control (p50) Experiment (p50) Δ
🟢 js small 14.62 ms 13.53 ms -7.5%
🟢 js medium 6.68 ms 6.52 ms -2.3%
js large 2.65 ms 2.61 ms -1.4%
gjs small 1.25 ms 1.24 ms -0.9%
gjs medium 615.06 µs 623.48 µs +1.4%
gjs large 234.86 µs 235.93 µs +0.5%
gts small 1.24 ms 1.24 ms +0.3%
gts medium 620.36 µs 614.28 µs -1.0%
gts large 237.40 µs 237.94 µs +0.2%

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

Full mitata output
clk: ~3.35 GHz
cpu: Intel(R) Xeon(R) Platinum 8370C CPU @ 2.80GHz
runtime: node 24.15.0 (x64-linux)

benchmark                   avg (min … max) p75 / p99    (min … top 1%)
------------------------------------------- -------------------------------
js small (control)            16.65 ms/iter  17.81 ms █                    
                      (11.73 ms … 29.78 ms)  28.02 ms █ ▂ ▅                
                    (  5.70 mb …  10.55 mb)   7.29 mb ███▃█▁▆█▃▁▁▁▁▁▁▃▃▃▃▃▆

js small (experiment)         14.17 ms/iter  15.45 ms   █                  
                      (12.06 ms … 20.08 ms)  18.77 ms  ▇█▇ ▇   ▂ ▂▅        
                    (  6.13 mb …   8.39 mb)   6.84 mb ▇███▇█▁▄▁█▄██▁▁▄▁▄▁▁▄

                             ┌                                            ┐
                             ╷┌────────────┬──┐                           ╷
          js small (control) ├┤            │  ├───────────────────────────┤
                             ╵└────────────┴──┘                           ╵
                              ╷ ┌───┬──┐        ╷
       js small (experiment)  ├─┤   │  ├────────┤
                              ╵ └───┴──┘        ╵
                             └                                            ┘
                             11.73 ms           19.87 ms           28.02 ms

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

------------------------------------------- -------------------------------
js medium (control)            7.50 ms/iter   8.01 ms  █                   
                       (6.23 ms … 14.05 ms)  13.12 ms ▃█                   
                    (  2.31 mb …   4.75 mb)   3.54 mb ██▇▅▂▅▄▅▂▁▂▂▁▂▁▂▂▁▁▂▂

js medium (experiment)         7.22 ms/iter   7.72 ms  █                   
                       (6.18 ms … 14.68 ms)  12.72 ms  █                   
                    (  2.69 mb …   4.27 mb)   3.52 mb ██▆▃▂▆▃▂▂▃▁▂▁▂▂▁▁▁▁▁▂

                             ┌                                            ┐
                             ╷ ┌──────┬──┐                                ╷
         js medium (control) ├─┤      │  ├────────────────────────────────┤
                             ╵ └──────┴──┘                                ╵
                             ╷┌─────┬──┐                               ╷
      js medium (experiment) ├┤     │  ├───────────────────────────────┤
                             ╵└─────┴──┘                               ╵
                             └                                            ┘
                             6.18 ms            9.65 ms            13.12 ms

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

------------------------------------------- -------------------------------
js large (control)             3.15 ms/iter   2.89 ms  █                   
                       (2.30 ms … 11.67 ms)   9.02 ms  █                   
                    (440.55 kb …   2.29 mb)   1.43 mb ██▃▃▃▁▂▂▁▂▂▁▁▁▁▁▁▁▁▁▁

js large (experiment)          2.91 ms/iter   2.77 ms  █                   
                        (2.45 ms … 8.01 ms)   6.08 ms ▄█                   
                    (312.88 kb …   2.57 mb)   1.43 mb ██▄▂▂▂▂▂▁▁▂▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷ ┌───┬                                      ╷
          js large (control) ├─┤   │──────────────────────────────────────┤
                             ╵ └───┴                                      ╵
                              ╷┌─┬                    ╷
       js large (experiment)  ├┤ │────────────────────┤
                              ╵└─┴                    ╵
                             └                                            ┘
                             2.30 ms            5.66 ms             9.02 ms

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

------------------------------------------- -------------------------------
gjs small (control)            1.39 ms/iter   1.31 ms █                    
                        (1.19 ms … 6.76 ms)   6.23 ms █                    
                    (220.49 kb …   1.80 mb)   1.06 mb █▇▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs small (experiment)         1.38 ms/iter   1.28 ms █                    
                        (1.20 ms … 7.23 ms)   6.53 ms █                    
                    (212.76 kb …   1.60 mb)   1.06 mb █▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌─┬                                       ╷
         gjs small (control) │ │───────────────────────────────────────┤
                             └─┴                                       ╵
                             ┌─┬                                          ╷
      gjs small (experiment) │ │──────────────────────────────────────────┤
                             └─┴                                          ╵
                             └                                            ┘
                             1.19 ms            3.86 ms             6.53 ms

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

------------------------------------------- -------------------------------
gjs medium (control)         669.69 µs/iter 631.44 µs █                    
                      (567.52 µs … 5.84 ms)   3.10 ms █                    
                    ( 78.72 kb …   1.13 mb) 541.46 kb ██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs medium (experiment)      673.89 µs/iter 639.71 µs  █                   
                      (577.19 µs … 6.09 ms)   1.40 ms  █                   
                    (246.18 kb …   1.56 mb) 540.95 kb ▇██▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷┌┬                                          ╷
        gjs medium (control) ├┤│──────────────────────────────────────────┤
                             ╵└┴                                          ╵
                             ╷┌┬            ╷
     gjs medium (experiment) ├┤│────────────┤
                             ╵└┴            ╵
                             └                                            ┘
                             567.52 µs           1.83 ms            3.10 ms

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

------------------------------------------- -------------------------------
gjs large (control)          264.29 µs/iter 255.70 µs  █                   
                      (224.47 µs … 5.46 ms) 353.93 µs  █                   
                    (  7.68 kb …   1.27 mb) 217.40 kb ▅██▅▅▄▅▄▄▂▁▁▁▁▁▁▁▁▁▁▁

gjs large (experiment)       264.57 µs/iter 254.98 µs  █▂                  
                      (226.07 µs … 6.04 ms) 319.35 µs  ██                  
                    ( 15.43 kb … 965.80 kb) 216.55 kb ▅██▆▅▅▄▄▄▅▄▃▂▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷ ┌───────────┬                              ╷
         gjs large (control) ├─┤           │──────────────────────────────┤
                             ╵ └───────────┴                              ╵
                              ╷┌───────────┬                  ╷
      gjs large (experiment)  ├┤           │──────────────────┤
                              ╵└───────────┴                  ╵
                             └                                            ┘
                             224.47 µs         289.20 µs          353.93 µs

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

------------------------------------------- -------------------------------
gts small (control)            1.36 ms/iter   1.28 ms █                    
                        (1.19 ms … 7.22 ms)   6.33 ms █                    
                    (182.16 kb …   1.78 mb)   1.06 mb █▅▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gts small (experiment)         1.35 ms/iter   1.26 ms █                    
                        (1.20 ms … 7.06 ms)   5.85 ms █                    
                    (180.15 kb …   1.76 mb)   1.06 mb █▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌─┬                                          ╷
         gts small (control) │ │──────────────────────────────────────────┤
                             └─┴                                          ╵
                             ┌┬                                       ╷
      gts small (experiment) ││───────────────────────────────────────┤
                             └┴                                       ╵
                             └                                            ┘
                             1.19 ms            3.76 ms             6.33 ms

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

------------------------------------------- -------------------------------
gts medium (control)         676.29 µs/iter 645.19 µs  █                   
                      (567.39 µs … 6.27 ms)   1.52 ms  █▂                  
                    (121.02 kb …   1.26 mb) 541.29 kb ███▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gts medium (experiment)      666.01 µs/iter 631.56 µs  █                   
                      (567.85 µs … 6.32 ms)   1.63 ms  █                   
                    (245.68 kb …   1.04 mb) 540.62 kb ▇█▄▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷┌───┬                                  ╷
        gts medium (control) ├┤   │──────────────────────────────────┤
                             ╵└───┴                                  ╵
                             ╷┌──┬                                        ╷
     gts medium (experiment) ├┤  │────────────────────────────────────────┤
                             ╵└──┴                                        ╵
                             └                                            ┘
                             567.39 µs           1.10 ms            1.63 ms

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

------------------------------------------- -------------------------------
gts large (control)          264.71 µs/iter 256.59 µs  █                   
                      (225.31 µs … 5.43 ms) 317.22 µs  █▆                  
                    ( 27.30 kb … 777.24 kb) 217.04 kb ▅██▆▅▆▆▅▆▅▄▃▂▂▂▁▁▁▁▁▁

gts large (experiment)       264.47 µs/iter 256.80 µs  █                   
                      (224.80 µs … 5.73 ms) 329.40 µs  ██                  
                    (106.06 kb … 868.01 kb) 216.54 kb ▄██▇▇▇▅▅▅▅▃▃▂▂▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷  ┌─────────────┬                      ╷
         gts large (control) ├──┤             │──────────────────────┤
                             ╵  └─────────────┴                      ╵
                             ╷  ┌─────────────┬                           ╷
      gts large (experiment) ├──┤             │───────────────────────────┤
                             ╵  └─────────────┴                           ╵
                             └                                            ┘
                             224.80 µs         277.10 µs          329.40 µs

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

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

Adds a new Ember template lint rule to prevent silent <input type="..."> fallback-to-text bugs by validating static type values against the HTML-defined set, with an optional requireExplicit: true mode to enforce explicit type attributes.

Changes:

  • Introduces template-require-input-type rule with autofix to type="text" for invalid/missing types (when opted-in).
  • Adds comprehensive RuleTester coverage for both .hbs and .gjs parsing modes.
  • Documents the new rule and links it from the README rule table.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
lib/rules/template-require-input-type.js Implements the new rule, including requireExplicit option and autofixes.
tests/lib/rules/template-require-input-type.js Adds test coverage for valid/invalid cases and both configuration modes across hbs/gjs.
docs/rules/template-require-input-type.md Documents rule purpose, examples, configuration, and references.
README.md Adds the rule to the documented rules list.

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

Comment thread lib/rules/template-require-input-type.js Outdated
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

Adds a new Ember template ESLint rule to prevent silent fallback bugs from invalid <input type="..."> values, with an optional mode to require explicit type for consistency.

Changes:

  • Introduces template-require-input-type rule to validate static <input type="..."> values against the HTML-defined set and auto-fix invalids to type="text".
  • Adds an opt-in requireExplicit: true option to flag/fix <input> elements missing a static type.
  • Adds documentation, tests, and README rule index entry for the new rule.

Reviewed changes

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

File Description
lib/rules/template-require-input-type.js Implements the new rule, including autofix and requireExplicit option.
tests/lib/rules/template-require-input-type.js Adds HBS/GJS test coverage for valid/invalid types and requireExplicit behavior.
docs/rules/template-require-input-type.md Documents the rule behavior, configuration, and examples.
README.md Adds the new rule to the rules table.

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

Comment thread lib/rules/template-require-input-type.js Outdated
Comment thread lib/rules/template-require-input-type.js Outdated
Comment thread tests/lib/rules/template-require-input-type.js
Comment thread tests/lib/rules/template-require-input-type.js
@johanrd johanrd force-pushed the html-validate/template-require-input-type branch from b22fa4c to 3790db6 Compare April 22, 2026 17:13
@johanrd johanrd requested a review from Copilot April 24, 2026 08:37
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

Adds a new template lint rule to prevent silent fallback-to-text bugs from invalid <input type="..."> values, with an opt-in mode to require explicit type attributes for consistency.

Changes:

  • Introduces template-require-input-type rule with autofix to type="text" for invalid/missing static type.
  • Adds isNativeElement utility to distinguish native elements vs scope-shadowed component invocations in GJS/GTS.
  • Adds rule documentation, test coverage (HBS + GJS), and README rule index entry.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/lib/rules/template-require-input-type.js Adds HBS/GJS RuleTester coverage for invalid types and optional requireExplicit behavior.
lib/utils/is-native-element.js Adds shared native-element detection utility with optional scope-shadowing check.
lib/rules/template-require-input-type.js Implements the new rule, validation logic, and autofixes.
docs/rules/template-require-input-type.md Documents rule purpose, options, and examples.
README.md Adds the rule to the documentation table of available rules.

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

Comment thread lib/utils/is-native-element.js
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

Adds a new template lint rule to prevent silent <input type="..."> fallback-to-text bugs, with an optional stricter mode to require explicit type declarations for consistency across codebases.

Changes:

  • Introduces template-require-input-type rule to flag invalid static type values (always) and optionally require explicit type on <input> (requireExplicit: true).
  • Adds a shared isNativeElement utility to distinguish native tags from scope-shadowed component invocations in GJS/GTS.
  • Adds rule documentation, test coverage, and README rule index entry.

Reviewed changes

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

Show a summary per file
File Description
lib/rules/template-require-input-type.js Implements the new rule, including autofixes and requireExplicit option handling.
lib/utils/is-native-element.js Adds shared native-element detection with optional scope-shadowing checks.
tests/lib/rules/template-require-input-type.js Adds RuleTester coverage for both .hbs and .gjs modes, including shadowing cases.
docs/rules/template-require-input-type.md Documents the rule behavior, options, and examples.
README.md Adds the rule to the rules table/index.

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

Comment thread lib/utils/is-native-element.js Outdated
Comment thread lib/utils/is-native-element.js
Comment thread lib/rules/template-require-input-type.js Outdated
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.

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

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.

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

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.

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

@johanrd johanrd force-pushed the html-validate/template-require-input-type branch from 8d686dc to e3074e8 Compare April 26, 2026 08:12
@johanrd johanrd requested a review from Copilot April 26, 2026 08:42
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.

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

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 5 out of 5 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 thread lib/rules/template-require-input-type.js Outdated
Comment thread docs/rules/template-require-input-type.md Outdated
@johanrd johanrd force-pushed the html-validate/template-require-input-type branch from b15038f to b0067f0 Compare April 27, 2026 14:01
@johanrd johanrd force-pushed the html-validate/template-require-input-type branch from f0d8fbe to 148a2a3 Compare April 27, 2026 19:29
@johanrd johanrd requested a review from Copilot April 27, 2026 19:34
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 5 out of 5 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 +69 to +81
GlimmerElementNode(node) {
if (node.tag !== 'input') {
return;
}
// In strict GJS, a lowercase local binding can shadow the native
// `<input>` element. `isNativeElement` consults html/svg/mathml tag
// lists and checks bindings in the scope chain to filter out
// scope-shadowed cases.
if (!isNativeElement(node, sourceCode)) {
return;
}

const typeAttr = node.attributes?.find((attr) => attr.name === 'type');
Comment on lines +41 to +42
// to `type="text"`. (Output loses the pre-slash space because the
// valueless attr range ends at `type`; prettier will re-insert if needed.)
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