Island SSR: defislands render to HTML server-side with hydration markers
Islands now render their initial state as HTML on the server, like React SSR. The client hydrates with reactive behavior on boot. Root causes fixed: - is_signal/signal_value now recognize Dict-based signals (from signals.sx) in addition to native Signal values - Register "context" as a primitive so the CEK deref frame handler can read scope stacks for reactive tracking - Load adapter-html.sx into kernel for SX-level render-to-html (islands use this instead of the OCaml render module) - Component accessors (params, body, has-children?, affinity) handle Island values with ? suffix aliases - Add platform primitives: make-raw-html, raw-html-content, empty-dict?, for-each-indexed, cek-call Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -150,6 +150,16 @@ let () = Sx_primitives.register "scope-peek" (fun args ->
|
||||
(match stack with v :: _ -> v | [] -> Nil)
|
||||
| _ -> Nil)
|
||||
|
||||
let () = Sx_primitives.register "context" (fun args ->
|
||||
match args with
|
||||
| [String name] | [String name; _] ->
|
||||
let stack = try Hashtbl.find _scope_stacks name with Not_found -> [] in
|
||||
(match stack, args with
|
||||
| v :: _, _ -> v
|
||||
| [], [_; default_val] -> default_val
|
||||
| [], _ -> Nil)
|
||||
| _ -> Nil)
|
||||
|
||||
(** collect! — lazy scope accumulator. Creates root scope if missing,
|
||||
emits value (deduplicates). Used by cssx and spread components. *)
|
||||
let () = Sx_primitives.register "collect!" (fun args ->
|
||||
@@ -376,13 +386,29 @@ let make_server_env () =
|
||||
| [List items; v] -> List (items @ [v])
|
||||
| _ -> raise (Eval_error "append!: expected list and value"));
|
||||
|
||||
(* HTML renderer *)
|
||||
(* HTML renderer — OCaml render module provides the shell renderer;
|
||||
adapter-html.sx provides the SX-level render-to-html *)
|
||||
Sx_render.setup_render_env env;
|
||||
|
||||
(* Render-mode flags *)
|
||||
bind "set-render-active!" (fun _args -> Nil);
|
||||
bind "render-active?" (fun _args -> Bool true);
|
||||
|
||||
(* Raw HTML — platform primitives for adapter-html.sx *)
|
||||
bind "make-raw-html" (fun args ->
|
||||
match args with [String s] -> RawHTML s | [v] -> RawHTML (value_to_string v) | _ -> Nil);
|
||||
bind "raw-html-content" (fun args ->
|
||||
match args with [RawHTML s] -> String s | [String s] -> String s | _ -> String "");
|
||||
bind "empty-dict?" (fun args ->
|
||||
match args with [Dict d] -> Bool (Hashtbl.length d = 0) | _ -> Bool true);
|
||||
bind "for-each-indexed" (fun args ->
|
||||
match args with
|
||||
| [fn_val; List items] | [fn_val; ListRef { contents = items }] ->
|
||||
List.iteri (fun i item ->
|
||||
ignore (Sx_ref.eval_expr (List [fn_val; Number (float_of_int i); item]) (Env env))
|
||||
) items; Nil
|
||||
| _ -> Nil);
|
||||
|
||||
(* Scope stack — platform primitives for render-time dynamic scope.
|
||||
Used by aser for spread/provide/emit patterns.
|
||||
Module-level so step-sf-context can check it via get-primitive. *)
|
||||
@@ -764,21 +790,27 @@ let make_server_env () =
|
||||
bind "component-params" (fun args ->
|
||||
match args with
|
||||
| [Component c] -> List (List.map (fun s -> String s) c.c_params)
|
||||
| [Island i] -> List (List.map (fun s -> String s) i.i_params)
|
||||
| _ -> Nil);
|
||||
|
||||
bind "component-body" (fun args ->
|
||||
match args with
|
||||
| [Component c] -> c.c_body
|
||||
| [Island i] -> i.i_body
|
||||
| _ -> Nil);
|
||||
|
||||
bind "component-has-children" (fun args ->
|
||||
let has_children_impl = NativeFn ("component-has-children?", fun args ->
|
||||
match args with
|
||||
| [Component c] -> Bool c.c_has_children
|
||||
| _ -> Bool false);
|
||||
| [Island i] -> Bool i.i_has_children
|
||||
| _ -> Bool false) in
|
||||
ignore (env_bind env "component-has-children" has_children_impl);
|
||||
ignore (env_bind env "component-has-children?" has_children_impl);
|
||||
|
||||
bind "component-affinity" (fun args ->
|
||||
match args with
|
||||
| [Component c] -> String c.c_affinity
|
||||
| [Island _] -> String "client"
|
||||
| _ -> String "auto");
|
||||
|
||||
bind "keyword-name" (fun args ->
|
||||
|
||||
Reference in New Issue
Block a user