From 78c6e77a0b98332a62f755c64ddb85b1fa0ac77f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20R=C3=B8ed?= Date: Thu, 7 May 2026 12:23:18 +0200 Subject: [PATCH 1/5] fix: inject placeholder src/alt on `` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a thin wrapper component renders `` and the consumer supplies src/alt via the splat, the blanker erased `...attributes` and html-validate then saw an attribute-less , FP-firing `element-required-attributes` (src) and `wcag/h37` (alt). At runtime the splat provides them. Symmetric to the existing `tryInjectInputType` (blank.ts:670) for ``: when `` has `...attributes`, inject whitespace-valued `src=' '` and `alt=' '` placeholders into Glimmer-only blank regions in the open tag. processAttribute's DynamicValue conversion (length>=3 + all-whitespace) then surfaces them as "present, value unknowable". Trade-off on slot space: minimal `` has only one 13-char Glimmer-only slot (the splat) which fits one 9-char placeholder. Real-world usually has additional Glimmer-only attrs/modifiers (an @arg, a {{on …}}) that provide enough room for both src and alt. Tests cover both cases. Surfaced by ecosystem CI on super-rentals (rental/image.gjs). --- blank.ts | 54 ++++++++++++++++++++++++++++++++++++++++++++++ test/blank.test.ts | 32 +++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/blank.ts b/blank.ts index d161cc2..ef3d960 100644 --- a/blank.ts +++ b/blank.ts @@ -667,6 +667,54 @@ function substituteSelfClosingVoidComponent( // // No-op when no suitable attr area is found — `no-implicit-input-type` // will still fire in that case, but the user can silence per-site. + +// Symmetric to tryInjectInputType but for void natives whose required attrs +// can be supplied by the consumer via `...attributes`. Without injection, +// `` blanks to an attribute-less `` and html-validate +// FP-fires `element-required-attributes` (src) and `wcag/h37` (alt) even +// though both come from the splat at runtime. +// +// Injects whitespace-valued placeholders (`src=' '` / `alt=' '`) into +// Glimmer-only blank regions in the open tag; `processAttribute`'s +// DynamicValue conversion (>=3-char whitespace threshold) then surfaces them +// as "present, value unknowable". Skipped when the consumer already wrote +// `src=` / `alt=` explicitly. +// +// Currently scoped to ; the same shape applies to +// ///