update solid compiler

This commit is contained in:
2026-07-15 11:28:24 -04:00
parent f1745b8a1b
commit 4e378842e4
8 changed files with 255 additions and 38 deletions

View File

@@ -151,6 +151,9 @@ func TestGoCompilerRenderSpreadRef(t *testing.T) {
{"spread", `export const A = () => { const p = { id: "pid", title: "t" }; return <div {...p} class="x">hi</div>; };`},
{"spread-override", `export const A = () => { const p = { class: "from-p" }; return <div {...p} class="from-attr">hi</div>; };`},
{"ref", `export const A = () => { let r; return <div ref={r}>hi</div>; };`},
{"component-spread", `export const A = () => { const Box = (props) => <div id={props.id} title={props.title}>{props.children}</div>; const p = { id: "pid", title: "t" }; return <Box {...p}>hi</Box>; };`},
{"component-spread-override", `export const A = () => { const Box = (props) => <div class={props.class}>x</div>; const p = { class: "from-p" }; return <Box {...p} class="from-attr" />; };`},
{"component-spread-before", `export const A = () => { const Box = (props) => <div class={props.class}>x</div>; const p = { class: "from-p" }; return <Box class="from-attr" {...p} />; };`},
}
for _, c := range cases {
c := c
@@ -299,3 +302,71 @@ func TestGoCompilerStyleAndInnerHTML(t *testing.T) {
})
}
}
// Regression: HTML boolean attributes (checked, disabled, required, readonly,
// hidden, selected, ...) are presence-based — any attribute value, including
// the string "false", still counts as present. A dynamic false must go through
// setBoolAttribute (which removes the attribute), not setAttribute (which would
// emit e.g. checked="false", read by the browser as checked=true).
func TestGoCompilerBooleanAttrs(t *testing.T) {
root, _ := filepath.Abs("../..")
t.Chdir(root)
src := `export const A = () => {
const no = () => false;
const yes = () => true;
return <div>
<input type="checkbox" checked={no()} disabled={no()} />
<input type="radio" checked={yes()} required={yes()} />
</div>;
};`
out, err := compileSolidGo(src, "bools.tsx", false)
if err != nil {
t.Fatal(err)
}
html, err := renderComponent(t, out)
if err != nil {
t.Fatalf("render: %v\n%s", err, out)
}
for _, broken := range []string{`checked="false"`, `disabled="false"`} {
if strings.Contains(html, broken) {
t.Errorf("false-valued boolean attr still present (reads as true in browser): %q in %s", broken, html)
}
}
for _, present := range []string{`checked=""`, `required=""`} {
if !strings.Contains(html, present) {
t.Errorf("true-valued boolean attr missing: expected %q in %s", present, html)
}
}
}
// Regression: a comment-only JSX expression child ({/* note */}) must be
// dropped like whitespace, not compiled as a real expression. childrenProp
// (component children) already filtered these via renderedChild, but a bare
// root fragment with 2+ children went through genFragment's own filter, which
// only skipped blank text — a comment-only child slipped through and compiled
// to `_$memo(() => /* note */)`, a syntax error that breaks the whole module.
func TestGoCompilerFragmentCommentChild(t *testing.T) {
root, _ := filepath.Abs("../..")
t.Chdir(root)
src := `export const A = () => {
return <>
{/* a comment */}
<div>one</div>
<div>two</div>
</>;
};`
out, err := compileSolidGo(src, "frag-comment.tsx", false)
if err != nil {
t.Fatal(err)
}
if err := validateJS(out); err != nil {
t.Fatalf("compiled output doesn't parse: %v\n--- output ---\n%s", err, out)
}
html, err := renderComponent(t, out)
if err != nil {
t.Fatalf("render: %v\n%s", err, out)
}
if !strings.Contains(html, "one") || !strings.Contains(html, "two") {
t.Errorf("expected both siblings in output, got: %q", html)
}
}