-module(ghtml@codegen). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/ghtml/codegen.gleam"). -export([generate/3]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC( " Gleam code generator from parsed templates.\n" "\n" " Transforms the parsed AST into valid Gleam source code that uses the\n" " Lustre library to render HTML elements. Generated code is formatted\n" " to be compliant with `gleam format`.\n" ). -file("src/ghtml/codegen.gleam", 69). ?DOC(" Extract the filename from a full path\n"). -spec extract_filename(binary()) -> binary(). extract_filename(Path) -> _pipe = Path, _pipe@1 = gleam@string:split(_pipe, <<"/"/utf8>>), _pipe@2 = gleam@list:last(_pipe@1), gleam@result:unwrap(_pipe@2, <<"unknown.ghtml"/utf8>>). -file("src/ghtml/codegen.gleam", 167). ?DOC(" Check if a node or its children need `none`\n"). -spec node_needs_none(ghtml@types:node_()) -> boolean(). node_needs_none(Node) -> case Node of {if_node, _, Then_branch, Else_branch, _} -> case Else_branch of [] -> true; _ -> gleam@list:any(Then_branch, fun node_needs_none/1) orelse gleam@list:any( Else_branch, fun node_needs_none/1 ) end; {each_node, _, _, _, Body, _} -> gleam@list:any(Body, fun node_needs_none/1); {case_node, _, Branches, _} -> gleam@list:any( Branches, fun(B) -> gleam@list:any(erlang:element(3, B), fun node_needs_none/1) end ); {element, _, _, Children, _} -> gleam@list:any(Children, fun node_needs_none/1); {fragment, Children@1, _} -> gleam@list:any(Children@1, fun node_needs_none/1); _ -> false end. -file("src/ghtml/codegen.gleam", 162). ?DOC(" Check if template needs `none` import (if without else)\n"). -spec template_needs_none(list(ghtml@types:node_())) -> boolean(). template_needs_none(Nodes) -> gleam@list:any(Nodes, fun node_needs_none/1). -file("src/ghtml/codegen.gleam", 192). ?DOC(" Check if a node or its children need `fragment`\n"). -spec node_needs_fragment(ghtml@types:node_()) -> boolean(). node_needs_fragment(Node) -> case Node of {if_node, _, Then_branch, Else_branch, _} -> Then_needs = erlang:length(Then_branch) > 1, Else_needs = erlang:length(Else_branch) > 1, ((Then_needs orelse Else_needs) orelse gleam@list:any( Then_branch, fun node_needs_fragment/1 )) orelse gleam@list:any(Else_branch, fun node_needs_fragment/1); {each_node, _, _, _, Body, _} -> (erlang:length(Body) > 1) orelse gleam@list:any( Body, fun node_needs_fragment/1 ); {case_node, _, Branches, _} -> gleam@list:any( Branches, fun(B) -> (erlang:length(erlang:element(3, B)) > 1) orelse gleam@list:any( erlang:element(3, B), fun node_needs_fragment/1 ) end ); {element, _, _, Children, _} -> gleam@list:any(Children, fun node_needs_fragment/1); {fragment, _, _} -> true; _ -> false end. -file("src/ghtml/codegen.gleam", 186). ?DOC(" Check if template needs `fragment` import (multiple roots or multiple children in branches)\n"). -spec template_needs_fragment(list(ghtml@types:node_())) -> boolean(). template_needs_fragment(Nodes) -> (erlang:length(Nodes) > 1) orelse gleam@list:any( Nodes, fun node_needs_fragment/1 ). -file("src/ghtml/codegen.gleam", 220). ?DOC(" Check if user has imported a specific module\n"). -spec has_user_import(list(binary()), binary()) -> boolean(). has_user_import(Imports, Module) -> gleam@list:any( Imports, fun(Imp) -> (gleam_stdlib:string_starts_with(Imp, <>) orelse gleam_stdlib:string_starts_with( Imp, <> )) orelse (Imp =:= Module) end ). -file("src/ghtml/codegen.gleam", 234). ?DOC(" Check if a node or its children need `text`\n"). -spec node_needs_text(ghtml@types:node_()) -> boolean(). node_needs_text(Node) -> case Node of {text_node, _, _} -> true; {expr_node, _, _} -> true; {if_node, _, Then_branch, Else_branch, _} -> gleam@list:any(Then_branch, fun node_needs_text/1) orelse gleam@list:any( Else_branch, fun node_needs_text/1 ); {each_node, _, _, _, Body, _} -> gleam@list:any(Body, fun node_needs_text/1); {case_node, _, Branches, _} -> gleam@list:any( Branches, fun(B) -> gleam@list:any(erlang:element(3, B), fun node_needs_text/1) end ); {element, _, _, Children, _} -> gleam@list:any(Children, fun node_needs_text/1); {fragment, Children@1, _} -> gleam@list:any(Children@1, fun node_needs_text/1) end. -file("src/ghtml/codegen.gleam", 229). ?DOC(" Check if template needs `text` import\n"). -spec template_needs_text(list(ghtml@types:node_())) -> boolean(). template_needs_text(Nodes) -> gleam@list:any(Nodes, fun node_needs_text/1). -file("src/ghtml/codegen.gleam", 255). ?DOC(" Check if a node or its children have each nodes\n"). -spec node_has_each(ghtml@types:node_()) -> boolean(). node_has_each(Node) -> case Node of {each_node, _, _, _, Body, _} -> true orelse gleam@list:any(Body, fun node_has_each/1); {if_node, _, Then_branch, Else_branch, _} -> gleam@list:any(Then_branch, fun node_has_each/1) orelse gleam@list:any( Else_branch, fun node_has_each/1 ); {case_node, _, Branches, _} -> gleam@list:any( Branches, fun(B) -> gleam@list:any(erlang:element(3, B), fun node_has_each/1) end ); {element, _, _, Children, _} -> gleam@list:any(Children, fun node_has_each/1); {fragment, Children@1, _} -> gleam@list:any(Children@1, fun node_has_each/1); _ -> false end. -file("src/ghtml/codegen.gleam", 250). ?DOC(" Check if any nodes have each nodes\n"). -spec template_has_each(list(ghtml@types:node_())) -> boolean(). template_has_each(Nodes) -> gleam@list:any(Nodes, fun node_has_each/1). -file("src/ghtml/codegen.gleam", 275). ?DOC(" Check if a node or its children have each nodes with index\n"). -spec node_has_each_with_index(ghtml@types:node_()) -> boolean(). node_has_each_with_index(Node) -> case Node of {each_node, _, _, {some, _}, Body, _} -> true orelse gleam@list:any(Body, fun node_has_each_with_index/1); {each_node, _, _, none, Body@1, _} -> gleam@list:any(Body@1, fun node_has_each_with_index/1); {if_node, _, Then_branch, Else_branch, _} -> gleam@list:any(Then_branch, fun node_has_each_with_index/1) orelse gleam@list:any( Else_branch, fun node_has_each_with_index/1 ); {case_node, _, Branches, _} -> gleam@list:any( Branches, fun(B) -> gleam@list:any( erlang:element(3, B), fun node_has_each_with_index/1 ) end ); {element, _, _, Children, _} -> gleam@list:any(Children, fun node_has_each_with_index/1); {fragment, Children@1, _} -> gleam@list:any(Children@1, fun node_has_each_with_index/1); _ -> false end. -file("src/ghtml/codegen.gleam", 270). ?DOC(" Check if any nodes have each nodes with index\n"). -spec template_has_each_with_index(list(ghtml@types:node_())) -> boolean(). template_has_each_with_index(Nodes) -> gleam@list:any(Nodes, fun node_has_each_with_index/1). -file("src/ghtml/codegen.gleam", 315). ?DOC(" Check if attrs list contains non-event attributes\n"). -spec has_non_event_attrs(list(ghtml@types:attr())) -> boolean(). has_non_event_attrs(Attrs) -> gleam@list:any(Attrs, fun(Attr) -> case Attr of {static_attr, _, _} -> true; {dynamic_attr, _, _} -> true; {boolean_attr, _} -> true; {event_attr, _, _} -> false end end). -file("src/ghtml/codegen.gleam", 299). ?DOC(" Check if a node or its children have attributes\n"). -spec node_has_attrs(ghtml@types:node_()) -> boolean(). node_has_attrs(Node) -> case Node of {element, _, Attrs, Children, _} -> has_non_event_attrs(Attrs) orelse gleam@list:any( Children, fun node_has_attrs/1 ); {if_node, _, Then_branch, Else_branch, _} -> gleam@list:any(Then_branch, fun node_has_attrs/1) orelse gleam@list:any( Else_branch, fun node_has_attrs/1 ); {each_node, _, _, _, Body, _} -> gleam@list:any(Body, fun node_has_attrs/1); {case_node, _, Branches, _} -> gleam@list:any( Branches, fun(B) -> gleam@list:any(erlang:element(3, B), fun node_has_attrs/1) end ); {fragment, Children@1, _} -> gleam@list:any(Children@1, fun node_has_attrs/1); _ -> false end. -file("src/ghtml/codegen.gleam", 294). ?DOC(" Check if any nodes have attributes\n"). -spec template_has_attrs(list(ghtml@types:node_())) -> boolean(). template_has_attrs(Nodes) -> gleam@list:any(Nodes, fun node_has_attrs/1). -file("src/ghtml/codegen.gleam", 348). ?DOC(" Check if attrs list contains event attributes\n"). -spec has_event_attrs(list(ghtml@types:attr())) -> boolean(). has_event_attrs(Attrs) -> gleam@list:any(Attrs, fun(Attr) -> case Attr of {event_attr, _, _} -> true; _ -> false end end). -file("src/ghtml/codegen.gleam", 332). ?DOC(" Check if a node or its children have events\n"). -spec node_has_events(ghtml@types:node_()) -> boolean(). node_has_events(Node) -> case Node of {element, _, Attrs, Children, _} -> has_event_attrs(Attrs) orelse gleam@list:any( Children, fun node_has_events/1 ); {if_node, _, Then_branch, Else_branch, _} -> gleam@list:any(Then_branch, fun node_has_events/1) orelse gleam@list:any( Else_branch, fun node_has_events/1 ); {each_node, _, _, _, Body, _} -> gleam@list:any(Body, fun node_has_events/1); {case_node, _, Branches, _} -> gleam@list:any( Branches, fun(B) -> gleam@list:any(erlang:element(3, B), fun node_has_events/1) end ); {fragment, Children@1, _} -> gleam@list:any(Children@1, fun node_has_events/1); _ -> false end. -file("src/ghtml/codegen.gleam", 327). ?DOC(" Check if any nodes have event attributes\n"). -spec template_has_events(list(ghtml@types:node_())) -> boolean(). template_has_events(Nodes) -> gleam@list:any(Nodes, fun node_has_events/1). -file("src/ghtml/codegen.gleam", 358). ?DOC(" Check if a tag is a custom element (contains a hyphen)\n"). -spec is_custom_element(binary()) -> boolean(). is_custom_element(Tag) -> gleam_stdlib:contains_string(Tag, <<"-"/utf8>>). -file("src/ghtml/codegen.gleam", 368). ?DOC(" Check if a node or its children have custom elements\n"). -spec node_has_custom_elements(ghtml@types:node_()) -> boolean(). node_has_custom_elements(Node) -> case Node of {element, Tag, _, Children, _} -> is_custom_element(Tag) orelse gleam@list:any( Children, fun node_has_custom_elements/1 ); {if_node, _, Then_branch, Else_branch, _} -> gleam@list:any(Then_branch, fun node_has_custom_elements/1) orelse gleam@list:any( Else_branch, fun node_has_custom_elements/1 ); {each_node, _, _, _, Body, _} -> gleam@list:any(Body, fun node_has_custom_elements/1); {case_node, _, Branches, _} -> gleam@list:any( Branches, fun(B) -> gleam@list:any( erlang:element(3, B), fun node_has_custom_elements/1 ) end ); {fragment, Children@1, _} -> gleam@list:any(Children@1, fun node_has_custom_elements/1); _ -> false end. -file("src/ghtml/codegen.gleam", 363). ?DOC(" Check if any nodes have custom elements\n"). -spec template_has_custom_elements(list(ghtml@types:node_())) -> boolean(). template_has_custom_elements(Nodes) -> gleam@list:any(Nodes, fun node_has_custom_elements/1). -file("src/ghtml/codegen.gleam", 391). ?DOC(" Check if a node or its children have standard HTML elements\n"). -spec node_has_html_elements(ghtml@types:node_()) -> boolean(). node_has_html_elements(Node) -> case Node of {element, Tag, _, Children, _} -> not is_custom_element(Tag) orelse gleam@list:any( Children, fun node_has_html_elements/1 ); {if_node, _, Then_branch, Else_branch, _} -> gleam@list:any(Then_branch, fun node_has_html_elements/1) orelse gleam@list:any( Else_branch, fun node_has_html_elements/1 ); {each_node, _, _, _, Body, _} -> gleam@list:any(Body, fun node_has_html_elements/1); {case_node, _, Branches, _} -> gleam@list:any( Branches, fun(B) -> gleam@list:any( erlang:element(3, B), fun node_has_html_elements/1 ) end ); {fragment, Children@1, _} -> gleam@list:any(Children@1, fun node_has_html_elements/1); _ -> false end. -file("src/ghtml/codegen.gleam", 386). ?DOC(" Check if any nodes have standard HTML elements (non-custom)\n"). -spec template_has_html_elements(list(ghtml@types:node_())) -> boolean(). template_has_html_elements(Nodes) -> gleam@list:any(Nodes, fun node_has_html_elements/1). -file("src/ghtml/codegen.gleam", 77). ?DOC(" Generate import statements for the generated module\n"). -spec generate_imports(ghtml@types:template()) -> binary(). generate_imports(Template) -> Needs_attrs = template_has_attrs(erlang:element(4, Template)), Needs_events = template_has_events(erlang:element(4, Template)), Needs_none = template_needs_none(erlang:element(4, Template)), Needs_fragment = template_needs_fragment(erlang:element(4, Template)), Needs_each = template_has_each(erlang:element(4, Template)), Needs_each_index = template_has_each_with_index(erlang:element(4, Template)), Needs_element = template_has_custom_elements(erlang:element(4, Template)), Needs_html = template_has_html_elements(erlang:element(4, Template)), Needs_text = template_needs_text(erlang:element(4, Template)), User_imports = erlang:element(2, Template), Element_items = [<<"type Element"/utf8>>], Element_items@1 = case Needs_text of true -> lists:append(Element_items, [<<"text"/utf8>>]); false -> Element_items end, Element_items@2 = case Needs_element of true -> lists:append(Element_items@1, [<<"element"/utf8>>]); false -> Element_items@1 end, Element_items@3 = case Needs_none of true -> lists:append(Element_items@2, [<<"none"/utf8>>]); false -> Element_items@2 end, Element_items@4 = case Needs_fragment of true -> lists:append(Element_items@3, [<<"fragment"/utf8>>]); false -> Element_items@3 end, Element_import = <<<<"import lustre/element.{"/utf8, (gleam@string:join(Element_items@4, <<", "/utf8>>))/binary>>/binary, "}"/utf8>>, Base_imports = case Needs_html of true -> <>; false -> Element_import end, Base_imports@1 = case Needs_each of true -> <>; false -> Base_imports end, Imports = case {Needs_attrs, Needs_events} of {true, true} -> <>; {true, false} -> <>; {false, true} -> <>; {false, false} -> Base_imports@1 end, Imports@1 = case Needs_each andalso not has_user_import( User_imports, <<"gleam/list"/utf8>> ) of true -> <>; false -> Imports end, Imports@2 = case Needs_each_index andalso not has_user_import( User_imports, <<"gleam/int"/utf8>> ) of true -> <>; false -> Imports@1 end, Imports@3 = case User_imports of [] -> Imports@2; _ -> User_import_lines = begin _pipe = gleam@list:map( User_imports, fun(Imp) -> <<"import "/utf8, Imp/binary>> end ), gleam@string:join(_pipe, <<"\n"/utf8>>) end, <<<>/binary, User_import_lines/binary>> end, Imports@3. -file("src/ghtml/codegen.gleam", 452). ?DOC(" Generate function parameters from template params (single line, no trailing comma)\n"). -spec generate_params(list({binary(), binary()})) -> binary(). generate_params(Params) -> _pipe = Params, _pipe@1 = gleam@list:map( _pipe, fun(P) -> <<<<(erlang:element(1, P))/binary, ": "/utf8>>/binary, (erlang:element(2, P))/binary>> end ), gleam@string:join(_pipe@1, <<", "/utf8>>). -file("src/ghtml/codegen.gleam", 572). ?DOC(" Generate code for an event handler attribute\n"). -spec generate_event_attr(binary(), binary()) -> binary(). generate_event_attr(Event, Handler) -> case Event of <<"click"/utf8>> -> <<<<"event.on_click("/utf8, Handler/binary>>/binary, ")"/utf8>>; <<"input"/utf8>> -> <<<<"event.on_input("/utf8, Handler/binary>>/binary, ")"/utf8>>; <<"change"/utf8>> -> <<<<"event.on_change("/utf8, Handler/binary>>/binary, ")"/utf8>>; <<"submit"/utf8>> -> <<<<"event.on_submit("/utf8, Handler/binary>>/binary, ")"/utf8>>; <<"blur"/utf8>> -> <<<<"event.on_blur("/utf8, Handler/binary>>/binary, ")"/utf8>>; <<"focus"/utf8>> -> <<<<"event.on_focus("/utf8, Handler/binary>>/binary, ")"/utf8>>; <<"keydown"/utf8>> -> <<<<"event.on_keydown("/utf8, Handler/binary>>/binary, ")"/utf8>>; <<"keyup"/utf8>> -> <<<<"event.on_keyup("/utf8, Handler/binary>>/binary, ")"/utf8>>; <<"keypress"/utf8>> -> <<<<"event.on_keypress("/utf8, Handler/binary>>/binary, ")"/utf8>>; <<"mouseenter"/utf8>> -> <<<<"event.on_mouse_enter("/utf8, Handler/binary>>/binary, ")"/utf8>>; <<"mouseleave"/utf8>> -> <<<<"event.on_mouse_leave("/utf8, Handler/binary>>/binary, ")"/utf8>>; <<"mouseover"/utf8>> -> <<<<"event.on_mouse_over("/utf8, Handler/binary>>/binary, ")"/utf8>>; <<"mouseout"/utf8>> -> <<<<"event.on_mouse_out("/utf8, Handler/binary>>/binary, ")"/utf8>>; _ -> Event_name = case gleam_stdlib:string_starts_with( Event, <<"on:"/utf8>> ) of true -> gleam@string:drop_start(Event, 3); false -> Event end, <<<<<<<<"event.on(\""/utf8, Event_name/binary>>/binary, "\", "/utf8>>/binary, Handler/binary>>/binary, ")"/utf8>> end. -file("src/ghtml/codegen.gleam", 631). ?DOC(" Escape special characters in a string for Gleam code\n"). -spec escape_string(binary()) -> binary(). escape_string(S) -> _pipe = S, _pipe@1 = gleam@string:replace(_pipe, <<"\\"/utf8>>, <<"\\\\"/utf8>>), _pipe@2 = gleam@string:replace(_pipe@1, <<"\""/utf8>>, <<"\\\""/utf8>>), _pipe@3 = gleam@string:replace(_pipe@2, <<"\n"/utf8>>, <<"\\n"/utf8>>), _pipe@4 = gleam@string:replace(_pipe@3, <<"\r"/utf8>>, <<"\\r"/utf8>>), gleam@string:replace(_pipe@4, <<"\t"/utf8>>, <<"\\t"/utf8>>). -file("src/ghtml/codegen.gleam", 651). ?DOC(" Helper function to collapse consecutive spaces/tabs (not newlines)\n"). -spec collapse_spaces(list(binary()), boolean(), list(binary())) -> list(binary()). collapse_spaces(Chars, Saw_space, Acc) -> case Chars of [] -> Acc; [C | Rest] -> case C of <<" "/utf8>> -> case Saw_space of true -> collapse_spaces(Rest, true, Acc); false -> collapse_spaces(Rest, true, [<<" "/utf8>> | Acc]) end; <<"\t"/utf8>> -> case Saw_space of true -> collapse_spaces(Rest, true, Acc); false -> collapse_spaces(Rest, true, [<<" "/utf8>> | Acc]) end; <<"\n"/utf8>> -> collapse_spaces(Rest, false, [C | Acc]); <<"\r"/utf8>> -> collapse_spaces(Rest, false, [C | Acc]); _ -> collapse_spaces(Rest, false, [C | Acc]) end end. -file("src/ghtml/codegen.gleam", 642). ?DOC( " Normalize whitespace by collapsing multiple spaces/tabs to single spaces\n" " Newlines are preserved (they will be escaped later)\n" ). -spec normalize_whitespace(binary()) -> binary(). normalize_whitespace(Text) -> _pipe = Text, _pipe@1 = gleam@string:to_graphemes(_pipe), _pipe@2 = collapse_spaces(_pipe@1, false, []), _pipe@3 = lists:reverse(_pipe@2), erlang:list_to_binary(_pipe@3). -file("src/ghtml/codegen.gleam", 676). ?DOC(" Check if a string is blank (empty or only whitespace)\n"). -spec is_blank(binary()) -> boolean(). is_blank(Text) -> gleam@string:trim(Text) =:= <<""/utf8>>. -file("src/ghtml/codegen.gleam", 609). ?DOC(" Generate code for a text node with whitespace normalization (inline)\n"). -spec generate_text_inline(binary()) -> binary(). generate_text_inline(Content) -> Normalized = normalize_whitespace(Content), case is_blank(Normalized) of true -> <<""/utf8>>; false -> <<<<"text(\""/utf8, (escape_string(Normalized))/binary>>/binary, "\")"/utf8>> end. -file("src/ghtml/codegen.gleam", 409). ?DOC(" Check if a tag is a void element (no children allowed)\n"). -spec is_void_element(binary()) -> boolean(). is_void_element(Tag) -> gleam@list:contains( [<<"area"/utf8>>, <<"base"/utf8>>, <<"br"/utf8>>, <<"col"/utf8>>, <<"embed"/utf8>>, <<"hr"/utf8>>, <<"img"/utf8>>, <<"input"/utf8>>, <<"link"/utf8>>, <<"meta"/utf8>>, <<"param"/utf8>>, <<"source"/utf8>>, <<"track"/utf8>>, <<"wbr"/utf8>>], Tag ). -file("src/ghtml/codegen.gleam", 599). ?DOC(" Find the Lustre function for a known attribute\n"). -spec find_attr_function(binary()) -> {ok, binary()} | {error, nil}. find_attr_function(Name) -> gleam@list:find_map( [{<<"class"/utf8>>, <<"attribute.class"/utf8>>}, {<<"id"/utf8>>, <<"attribute.id"/utf8>>}, {<<"href"/utf8>>, <<"attribute.href"/utf8>>}, {<<"src"/utf8>>, <<"attribute.src"/utf8>>}, {<<"alt"/utf8>>, <<"attribute.alt"/utf8>>}, {<<"type"/utf8>>, <<"attribute.type_"/utf8>>}, {<<"value"/utf8>>, <<"attribute.value"/utf8>>}, {<<"name"/utf8>>, <<"attribute.name"/utf8>>}, {<<"placeholder"/utf8>>, <<"attribute.placeholder"/utf8>>}, {<<"disabled"/utf8>>, <<"attribute.disabled"/utf8>>}, {<<"readonly"/utf8>>, <<"attribute.readonly"/utf8>>}, {<<"checked"/utf8>>, <<"attribute.checked"/utf8>>}, {<<"selected"/utf8>>, <<"attribute.selected"/utf8>>}, {<<"autofocus"/utf8>>, <<"attribute.autofocus"/utf8>>}, {<<"for"/utf8>>, <<"attribute.for"/utf8>>}, {<<"role"/utf8>>, <<"attribute.role"/utf8>>}, {<<"style"/utf8>>, <<"attribute.style"/utf8>>}, {<<"width"/utf8>>, <<"attribute.width"/utf8>>}, {<<"height"/utf8>>, <<"attribute.height"/utf8>>}, {<<"title"/utf8>>, <<"attribute.title"/utf8>>}, {<<"target"/utf8>>, <<"attribute.target"/utf8>>}, {<<"rel"/utf8>>, <<"attribute.rel"/utf8>>}, {<<"action"/utf8>>, <<"attribute.action"/utf8>>}, {<<"method"/utf8>>, <<"attribute.method"/utf8>>}, {<<"required"/utf8>>, <<"attribute.required"/utf8>>}], fun(Pair) -> case erlang:element(1, Pair) =:= Name of true -> {ok, erlang:element(2, Pair)}; false -> {error, nil} end end ). -file("src/ghtml/codegen.gleam", 533). ?DOC(" Generate code for a static attribute\n"). -spec generate_static_attr(binary(), binary()) -> binary(). generate_static_attr(Name, Value) -> case find_attr_function(Name) of {ok, Func} -> <<<<<>/binary, (escape_string(Value))/binary>>/binary, "\")"/utf8>>; {error, _} -> <<<<<<<<"attribute.attribute(\""/utf8, Name/binary>>/binary, "\", \""/utf8>>/binary, (escape_string(Value))/binary>>/binary, "\")"/utf8>> end. -file("src/ghtml/codegen.gleam", 546). ?DOC(" Generate code for a dynamic attribute\n"). -spec generate_dynamic_attr(binary(), binary()) -> binary(). generate_dynamic_attr(Name, Expr) -> case find_attr_function(Name) of {ok, Func} -> <<<<<>/binary, Expr/binary>>/binary, ")"/utf8>>; {error, _} -> <<<<<<<<"attribute.attribute(\""/utf8, Name/binary>>/binary, "\", "/utf8>>/binary, Expr/binary>>/binary, ")"/utf8>> end. -file("src/ghtml/codegen.gleam", 554). ?DOC(" Generate code for a boolean attribute\n"). -spec generate_boolean_attr(binary(), boolean()) -> binary(). generate_boolean_attr(Name, Is_custom) -> case Is_custom of true -> <<<<"attribute.attribute(\""/utf8, Name/binary>>/binary, "\", \"\")"/utf8>>; false -> case gleam@list:contains( [<<"disabled"/utf8>>, <<"readonly"/utf8>>, <<"checked"/utf8>>, <<"selected"/utf8>>, <<"autofocus"/utf8>>, <<"required"/utf8>>], Name ) of true -> case find_attr_function(Name) of {ok, Func} -> <>; {error, _} -> <<<<"attribute.attribute(\""/utf8, Name/binary>>/binary, "\", \"\")"/utf8>> end; false -> <<<<"attribute.attribute(\""/utf8, Name/binary>>/binary, "\", \"\")"/utf8>> end end. -file("src/ghtml/codegen.gleam", 523). ?DOC(" Generate code for a single attribute\n"). -spec generate_attr(ghtml@types:attr(), boolean()) -> binary(). generate_attr(Attr, Is_custom) -> case Attr of {static_attr, Name, Value} -> generate_static_attr(Name, Value); {dynamic_attr, Name@1, Expr} -> generate_dynamic_attr(Name@1, Expr); {event_attr, Event, Handler} -> generate_event_attr(Event, Handler); {boolean_attr, Name@2} -> generate_boolean_attr(Name@2, Is_custom) end. -file("src/ghtml/codegen.gleam", 509). ?DOC(" Generate code for a list of attributes\n"). -spec generate_attrs(list(ghtml@types:attr()), boolean()) -> binary(). generate_attrs(Attrs, Is_custom) -> case gleam@list:is_empty(Attrs) of true -> <<"[]"/utf8>>; false -> Attr_strings = begin _pipe = Attrs, _pipe@1 = gleam@list:map( _pipe, fun(Attr) -> generate_attr(Attr, Is_custom) end ), gleam@string:join(_pipe@1, <<", "/utf8>>) end, <<<<"["/utf8, Attr_strings/binary>>/binary, "]"/utf8>> end. -file("src/ghtml/codegen.gleam", 618). ?DOC(" Generate code for a list of children nodes (inline, comma-separated)\n"). -spec generate_children_inline(list(ghtml@types:node_())) -> binary(). generate_children_inline(Children) -> _pipe = Children, _pipe@1 = gleam@list:filter_map( _pipe, fun(Child) -> Code = generate_node_inline(Child), case gleam@string:trim(Code) of <<""/utf8>> -> {error, nil}; _ -> {ok, Code} end end ), gleam@string:join(_pipe@1, <<", "/utf8>>). -file("src/ghtml/codegen.gleam", 459). ?DOC(" Generate code for a single AST node (inline, no indentation)\n"). -spec generate_node_inline(ghtml@types:node_()) -> binary(). generate_node_inline(Node) -> case Node of {element, Tag, Attrs, Children, _} -> generate_element_inline(Tag, Attrs, Children); {text_node, Content, _} -> generate_text_inline(Content); {expr_node, Expr, _} -> <<<<"text("/utf8, Expr/binary>>/binary, ")"/utf8>>; {if_node, Condition, Then_branch, Else_branch, _} -> generate_if_node_inline(Condition, Then_branch, Else_branch); {each_node, Collection, Item, Index, Body, _} -> generate_each_node_inline(Collection, Item, Index, Body); {case_node, Expr@1, Branches, _} -> generate_case_node_inline(Expr@1, Branches); {fragment, Children@1, _} -> generate_fragment_inline(Children@1) end. -file("src/ghtml/codegen.gleam", 753). ?DOC(" Generate code for a case node\n"). -spec generate_case_node_inline(binary(), list(ghtml@types:case_branch())) -> binary(). generate_case_node_inline(Expr, Branches) -> Branches_code = begin _pipe = Branches, _pipe@1 = gleam@list:map( _pipe, fun(Branch) -> Body_code = generate_branch_content(erlang:element(3, Branch)), <<<<(erlang:element(2, Branch))/binary, " -> "/utf8>>/binary, Body_code/binary>> end ), gleam@string:join(_pipe@1, <<" "/utf8>>) end, <<<<<<<<"case "/utf8, Expr/binary>>/binary, " { "/utf8>>/binary, Branches_code/binary>>/binary, " }"/utf8>>. -file("src/ghtml/codegen.gleam", 702). ?DOC(" Generate code for branch content (single node or fragment for multiple)\n"). -spec generate_branch_content(list(ghtml@types:node_())) -> binary(). generate_branch_content(Nodes) -> case Nodes of [] -> <<"none()"/utf8>>; [Single] -> generate_node_inline(Single); Multiple -> Children = generate_children_inline(Multiple), <<<<"fragment(["/utf8, Children/binary>>/binary, "])"/utf8>> end. -file("src/ghtml/codegen.gleam", 414). ?DOC(" Generate the main render function\n"). -spec generate_function(ghtml@types:template()) -> binary(). generate_function(Template) -> Params = generate_params(erlang:element(3, Template)), case erlang:length(erlang:element(4, Template)) of 0 -> <<<<"pub fn render("/utf8, Params/binary>>/binary, ") -> Element(msg) {\n fragment([])\n}"/utf8>>; 1 -> Body = generate_node_inline( begin _pipe = gleam@list:first(erlang:element(4, Template)), gleam@result:unwrap( _pipe, {text_node, <<""/utf8>>, ghtml@types:point_span(ghtml@types:start_position())} ) end ), <<<<<<<<"pub fn render("/utf8, Params/binary>>/binary, ") -> Element(msg) {\n "/utf8>>/binary, Body/binary>>/binary, "\n}"/utf8>>; _ -> Children = begin _pipe@1 = erlang:element(4, Template), _pipe@2 = gleam@list:filter_map( _pipe@1, fun(Node) -> Code = generate_node_inline(Node), case gleam@string:trim(Code) of <<""/utf8>> -> {error, nil}; _ -> {ok, <<" "/utf8, Code/binary>>} end end ), gleam@string:join(_pipe@2, <<",\n"/utf8>>) end, <<<<<<<<"pub fn render("/utf8, Params/binary>>/binary, ") -> Element(msg) {\n fragment([\n"/utf8>>/binary, Children/binary>>/binary, ",\n ])\n}"/utf8>> end. -file("src/ghtml/codegen.gleam", 59). ?DOC(" Generate Gleam code from a parsed template\n"). -spec generate(ghtml@types:template(), binary(), binary()) -> binary(). generate(Template, Source_path, Hash) -> Filename = extract_filename(Source_path), Header = ghtml@cache:generate_header(Filename, Hash), Imports = generate_imports(Template), Body = generate_function(Template), <<<<<<<<<
>/binary, Imports/binary>>/binary, "\n\n"/utf8>>/binary, Body/binary>>/binary, "\n"/utf8>>. -file("src/ghtml/codegen.gleam", 475). ?DOC(" Generate code for an HTML element (inline format)\n"). -spec generate_element_inline( binary(), list(ghtml@types:attr()), list(ghtml@types:node_()) ) -> binary(). generate_element_inline(Tag, Attrs, Children) -> Is_custom = is_custom_element(Tag), Is_void = is_void_element(Tag), Attrs_code = generate_attrs(Attrs, Is_custom), case Is_custom of true -> Children_code = generate_children_inline(Children), <<<<<<<<<<<<"element(\""/utf8, Tag/binary>>/binary, "\", "/utf8>>/binary, Attrs_code/binary>>/binary, ", ["/utf8>>/binary, Children_code/binary>>/binary, "])"/utf8>>; false -> case Is_void of true -> <<<<<<<<"html."/utf8, Tag/binary>>/binary, "("/utf8>>/binary, Attrs_code/binary>>/binary, ")"/utf8>>; false -> Children_code@1 = generate_children_inline(Children), <<<<<<<<<<<<"html."/utf8, Tag/binary>>/binary, "("/utf8>>/binary, Attrs_code/binary>>/binary, ", ["/utf8>>/binary, Children_code@1/binary>>/binary, "])"/utf8>> end end. -file("src/ghtml/codegen.gleam", 681). ?DOC(" Generate code for an if node (case expression with True/False branches)\n"). -spec generate_if_node_inline( binary(), list(ghtml@types:node_()), list(ghtml@types:node_()) ) -> binary(). generate_if_node_inline(Condition, Then_branch, Else_branch) -> Then_code = generate_branch_content(Then_branch), Else_code = case Else_branch of [] -> <<"none()"/utf8>>; _ -> generate_branch_content(Else_branch) end, <<<<<<<<<<<<"case "/utf8, Condition/binary>>/binary, " { True -> "/utf8>>/binary, Then_code/binary>>/binary, " False -> "/utf8>>/binary, Else_code/binary>>/binary, " }"/utf8>>. -file("src/ghtml/codegen.gleam", 714). ?DOC(" Generate code for an each node (keyed with list.map or list.index_map)\n"). -spec generate_each_node_inline( binary(), binary(), gleam@option:option(binary()), list(ghtml@types:node_()) ) -> binary(). generate_each_node_inline(Collection, Item, Index, Body) -> Body_code = generate_branch_content(Body), case Index of none -> <<<<<<<<<<<<<<<<"keyed.fragment(list.map("/utf8, Collection/binary>>/binary, ", fn("/utf8>>/binary, Item/binary>>/binary, ") { #("/utf8>>/binary, Item/binary>>/binary, ".id, "/utf8>>/binary, Body_code/binary>>/binary, ") }))"/utf8>>; {some, Idx} -> <<<<<<<<<<<<<<<<<<<<"keyed.fragment(list.index_map("/utf8, Collection/binary>>/binary, ", fn("/utf8>>/binary, Item/binary>>/binary, ", "/utf8>>/binary, Idx/binary>>/binary, ") { #(int.to_string("/utf8>>/binary, Idx/binary>>/binary, "), "/utf8>>/binary, Body_code/binary>>/binary, ") }))"/utf8>> end. -file("src/ghtml/codegen.gleam", 766). ?DOC(" Generate code for a fragment node\n"). -spec generate_fragment_inline(list(ghtml@types:node_())) -> binary(). generate_fragment_inline(Children) -> Children_code = generate_children_inline(Children), <<<<"fragment(["/utf8, Children_code/binary>>/binary, "])"/utf8>>.