From 6a226995879a3af2fef5df7740a39b16922336d7 Mon Sep 17 00:00:00 2001 From: giles Date: Sat, 28 Mar 2026 19:47:50 +0000 Subject: [PATCH] =?UTF-8?q?sx-http:=20SX=20adapter=20for=20all=20SSR=20?= =?UTF-8?q?=E2=80=94=20islands=20render=20correctly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch SSR + shell from native Sx_render to SX adapter (render-to-html from adapter-html.sx) via CEK evaluator. Handles reactive primitives (signals, deref, computed) for island bodies. Native renderer as fallback. Header island SSRs correctly: (), tagline, copyright, path. Stepper renders body but ~cssx/tw shows as raw SX (client-affinity component not expanded server-side, client not expanding either). Co-Authored-By: Claude Opus 4.6 (1M context) --- hosts/ocaml/bin/sx_server.ml | 51 ++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/hosts/ocaml/bin/sx_server.ml b/hosts/ocaml/bin/sx_server.ml index 62f6d8e1..f2eb11b2 100644 --- a/hosts/ocaml/bin/sx_server.ml +++ b/hosts/ocaml/bin/sx_server.ml @@ -1517,29 +1517,30 @@ let http_render_page env path = | String s | SxExpr s -> s | _ -> serialize_value body_result in - (* Debug: dump aser for homepage *) - if path = "/sx/" then begin - try - let oc = open_out "/tmp/sx-aser-home.txt" in - output_string oc body_str; - close_out oc; - Printf.eprintf "[debug] Wrote aser for %s: %d bytes\n%!" path (String.length body_str) - with _ -> () - end; let t2 = Unix.gettimeofday () in - (* Phase 2: SSR — render to HTML using streaming buffer renderer. - Writes directly to buffer, no intermediate string allocations. *) + (* Phase 2: SSR — render to HTML using the SX adapter (render-to-html + from adapter-html.sx) via the CEK evaluator. This handles reactive + primitives (signals, deref, computed) correctly for island SSR. + Falls back to native Sx_render if the SX adapter isn't available. *) let body_html = try let body_exprs = Sx_parser.parse_all body_str in let body_expr = match body_exprs with | [e] -> e | [] -> Nil | _ -> List (Symbol "<>" :: body_exprs) in - let buf = Buffer.create 65536 in - (try Sx_render.render_to_buffer buf body_expr env - with e -> Printf.eprintf "[http-ssr] partial for %s: %s\n%!" path (Printexc.to_string e)); - Buffer.contents buf + if env_has env "render-to-html" then begin + (* SX adapter — handles signals, islands, CSSX *) + let render_call = List [Symbol "render-to-html"; + List [Symbol "quote"; body_expr]; + Env env] in + let result = Sx_ref.eval_expr render_call (Env env) in + match result with + | String s | RawHTML s -> s + | _ -> Sx_runtime.value_to_str result + end else + (* Fallback: native renderer *) + Sx_render.render_to_html_streaming body_expr env with e -> - Printf.eprintf "[http-ssr] failed: %s\n%!" (Printexc.to_string e); "" + Printf.eprintf "[http-ssr] failed for %s: %s\n%!" path (Printexc.to_string e); "" in let t3 = Unix.gettimeofday () in (* Phase 3: Shell — render directly to buffer for zero-copy output *) @@ -1567,7 +1568,19 @@ let http_render_page env path = Keyword "meta-html"; String ""; ] in let shell_call = List (Symbol "~shared:shell/sx-page-shell" :: shell_args) in - let html = Sx_render.render_to_html_streaming shell_call env in + (* Use SX adapter for shell too — it's an SX component *) + let html = + if env_has env "render-to-html" then begin + let render_call = List [Symbol "render-to-html"; + List [Symbol "quote"; shell_call]; + Env env] in + let result = Sx_ref.eval_expr render_call (Env env) in + match result with + | String s | RawHTML s -> s + | _ -> Sx_runtime.value_to_str result + end else + Sx_render.render_to_html_streaming shell_call env + in let t4 = Unix.gettimeofday () in Printf.eprintf "[sx-http] %s route=%.3fs aser=%.3fs ssr=%.3fs shell=%.3fs total=%.3fs html=%d\n%!" path (t1 -. t0) (t2 -. t1) (t3 -. t2) (t4 -. t3) (t4 -. t0) (String.length html); @@ -1733,9 +1746,7 @@ let http_inject_shell_statics env static_dir sx_sxc = (String.length pages_sx) (List.length (String.split_on_char '\n' pages_sx)); ignore (env_bind env "__shell-pages-sx" (String pages_sx)); - (* Add CSS to hide island placeholder SX text until client hydrates *) - let island_css = "[data-sx-island]{font-size:0;line-height:0;overflow:hidden;max-height:0}" in - ignore (env_bind env "__shell-sx-css" (String (sx_css ^ "\n" ^ island_css))); + ignore (env_bind env "__shell-sx-css" (String sx_css)); ignore (env_bind env "__shell-sx-css-classes" (String "")); ignore (env_bind env "__shell-asset-url" (String "/static")); ignore (env_bind env "__shell-sx-js-hash" (String sx_js_hash));