; };`},
{"show-true", `import { Show } from "solid-js"; export const A = () =>
no}>yes
;`},
{"show-false", `import { Show } from "solid-js"; export const A = () =>
no}>yes
;`},
{"for", `import { For } from "solid-js"; export const A = () =>
{(n) =>
{n}
}
;`},
{"fragment", `export const A = () => { const a = () => "A"; const b = () => "B"; return
{a()}{b()}
; };`},
}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) { assertRenderEquivalent(t, c.name, c.src) })
}
}
func TestGoCompilerRenderSpreadRef(t *testing.T) {
cases := []struct{ name, src string }{
{"spread", `export const A = () => { const p = { id: "pid", title: "t" }; return
hi
; };`},
{"spread-override", `export const A = () => { const p = { class: "from-p" }; return
hi
; };`},
{"ref", `export const A = () => { let r; return
hi
; };`},
}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) { assertRenderEquivalent(t, c.name, c.src) })
}
}
// Compile every real .tsx with the Go compiler and assert it produces parseable
// output. This surfaces constructs the codebase uses that the codegen doesn't
// handle yet (the failures ARE the milestone-3/4 gap list).
func TestGoCompilerCompilesRealFiles(t *testing.T) {
files := walkRepoTSX(t)
var failed, ok int
for _, f := range files {
data, err := os.ReadFile(f)
if err != nil {
continue
}
out, err := compileSolidGo(string(data), f, false)
if err != nil {
failed++
t.Logf("COMPILE-ERR %s: %v", f, err)
continue
}
if err := validateJS(out); err != nil {
failed++
t.Logf("PARSE-ERR %s: %v", f, err)
continue
}
ok++
}
t.Logf("Go compiler: %d/%d real .tsx produced parseable output (%d failed)", ok, len(files), failed)
}
// M5: dev mode wraps components with solid-refresh. Verify the shape and that
// every real file still produces parseable output with instrumentation on.
func TestGoCompilerRefreshInstrumentation(t *testing.T) {
src := `export function Counter() { const c = () => 1; return
{c()}
; }
export const Banner = () => hi;
const NOT_A_COMPONENT = 42;
function helper() { return 5; }`
out, err := compileSolidGo(src, "Refresh.tsx", true)
if err != nil {
t.Fatalf("dev compile: %v", err)
}
if err := validateJS(out); err != nil {
t.Fatalf("dev output does not parse: %v\n%s", err, out)
}
for _, want := range []string{
`from "solid-refresh"`,
"const _REGISTRY = _$$registry();",
`_$$component(_REGISTRY, "Counter", function Counter`,
`_$$component(_REGISTRY, "Banner",`,
`if (import.meta.hot) { _$$refresh("esm", import.meta.hot, _REGISTRY); }`,
} {
if !strings.Contains(out, want) {
t.Errorf("dev output missing %q\n--- output ---\n%s", want, out)
}
}
// Non-components must NOT be wrapped.
if strings.Contains(out, `"NOT_A_COMPONENT"`) || strings.Contains(out, `"helper"`) {
t.Errorf("non-component was wrapped:\n%s", out)
}
// Prod mode must have no refresh instrumentation.
prod, _ := compileSolidGo(src, "Refresh.tsx", false)
if strings.Contains(prod, "solid-refresh") {
t.Errorf("prod output leaked refresh instrumentation:\n%s", prod)
}
}
func TestGoCompilerDevCompilesRealFiles(t *testing.T) {
files := walkRepoTSX(t)
var failed, ok int
for _, f := range files {
data, err := os.ReadFile(f)
if err != nil {
continue
}
out, err := compileSolidGo(string(data), f, true)
if err != nil {
failed++
t.Logf("DEV-COMPILE-ERR %s: %v", f, err)
continue
}
if err := validateJS(out); err != nil {
failed++
t.Logf("DEV-PARSE-ERR %s: %v", f, err)
continue
}
ok++
}
t.Logf("Go compiler (dev): %d/%d real .tsx parseable with refresh (%d failed)", ok, len(files), failed)
}
// Regression: a context provider's JSX children must evaluate lazily (inside the
// provider), or a consumer reads the context before it's set. Eager `children:`
// makes Consumer throw "no ctx"; lazy `get children()` renders correctly.
func TestGoCompilerContextChildren(t *testing.T) {
src := `import { createContext, useContext } from "solid-js";
const Ctx = createContext();
function Provider(props) { return {props.children}; }
function Consumer() { const v = useContext(Ctx); if (!v) throw new Error("no ctx"); return {v}; }
export const A = () => ;`
root, _ := filepath.Abs("../..")
t.Chdir(root)
goJS, err := compileSolidGo(src, "Ctx.tsx", false)
if err != nil {
t.Fatalf("compile: %v", err)
}
html, err := renderComponent(t, goJS)
if err != nil {
t.Fatalf("render (context leaked?): %v\n--- compiled ---\n%s", err, goJS)
}
if !strings.Contains(html, "ok") {
t.Errorf("expected context value in output, got: %q", html)
}
}
// Regression: object `style` must go through _$style (setProperty per key), not
// setAttribute (which stringifies to "[object Object]" and breaks positioning);
// innerHTML must be a property assignment (setAttribute is a no-op → empty icons).
func TestGoCompilerStyleAndInnerHTML(t *testing.T) {
root, _ := filepath.Abs("../..")
t.Chdir(root)
cases := []struct{ name, src, want, absent string }{
{"object-style", `export const A = () => { const s = () => ({ top: "10px", left: "20px" }); return