Follow-up to #1817. That fix addressed the ordering half — <component :is> now reaches its
handler and no longer emits [Error loading component: … open 'component'] with absolute
paths. Confirmed fixed.
The other half reported in the same issue, under "A pure reorder is not sufficient", was not
addressed, and it is the half that the component library actually depends on: an
interpolated :is is not resolved. The component silently emits a comment and its entire
subtree — including <slot /> — is discarded.
Net effect: the same 26 primitives are still unusable. They now fail silently instead of loudly, which is harder to diagnose than before.
Measurement
Rendering <DialogPanel className="probe"><p>dialog-ok</p></DialogPanel> from a page of a
real app, against a fork built at the #1817 fix:
'Error loading component' -> absent (#1817 fixed, good)
'dynamic component' -> FOUND (comment placeholder emitted instead)
'dialog-ok' -> absent (slot content destroyed)
'fixed inset-0' -> absent (the component's own classes never render)
'probe' -> absent (the className prop never renders)For contrast, in the same request, components that do not use <component :is> render
correctly — <Badge> emits its full class list and slot, <CodeBlock> renders with its
client script. So the plugin and the resolver are working; this is specific to the
interpolated :is.
Why it hits every affected component
DialogPanel.stx:
<script server>
export const as = $props.as || 'div'
…
</script>
<component :is="{{ as }}" class="{{ baseClasses }}">
…<slot />…
</component>The interpolated form is not incidental — it is how all of them are written:
$ grep -rl "<component" packages/components/src/ui/ | wc -l
26
$ grep -rl 'is="{{' packages/components/src/ui/ | wc -l
2626 of 26. Every dialog/, dropdown/, listbox/, combobox/, popover/, radio-group/
and stepper/ file.
Note also that as defaults to 'div' — a plain HTML tag name. So these components
simultaneously exercise the other unaddressed sub-defect from #1817: :is resolving to an
HTML tag should render that element rather than be looked up as a component file. Both need
to work for the library to function; fixing interpolation alone would just move the failure
to "no component file named div".
Repro
<!-- components/Wrap.stx -->
<component :is="{{ as }}" class="x"><slot /></component><Wrap as="section"><p>hello</p></Wrap>Expected: <section class="x"><p>hello</p></section>
Actual: a dynamic component comment; <p>hello</p> is gone.
The literal form is worth checking alongside it — #1817's repro B showed
processDynamicComponents handling :is="which" correctly in isolation, so the two forms
may diverge.
Suggested direction
Resolve {{ … }} in the :is value against the component's own scope before the lookup,
and fall back to emitting a plain element when the resolved value is an HTML tag name rather
than treating it as a missing component file.
A test rendering one primitive from each affected family through the full processDirectives
pipeline and asserting the slot content survives would cover this. The current coverage
evidently exercises <component :is> with a literal value only, which is the one form the
library never uses.
Related
- #1817 — ordering fix; this is the remaining half from that report.