sx-http: SX adapter for all SSR — islands render correctly

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: (<sx>), 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) <noreply@anthropic.com>
This commit is contained in:
2026-03-28 19:47:50 +00:00
parent aea9231e0a
commit 6a22699587

View File

@@ -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));