src/rally@tree_shaker.erl

-module(rally@tree_shaker).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/rally/tree_shaker.gleam").
-export([shake/2]).

-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.

-file("src/rally/tree_shaker.gleam", 604).
-spec render_import(glance:definition(glance:import())) -> binary().
render_import(Def) ->
    Imp = erlang:element(3, Def),
    Base = <<"import "/utf8, (erlang:element(3, Imp))/binary>>,
    Alias_part = case erlang:element(4, Imp) of
        {some, {named, Name}} ->
            <<" as "/utf8, Name/binary>>;

        {some, {discarded, Name@1}} ->
            <<" as _"/utf8, Name@1/binary>>;

        none ->
            <<""/utf8>>
    end,
    Unqualified_types = gleam@list:map(
        erlang:element(5, Imp),
        fun(Ut) -> case erlang:element(3, Ut) of
                {some, Alias} ->
                    <<<<<<"type "/utf8, (erlang:element(2, Ut))/binary>>/binary,
                            " as "/utf8>>/binary,
                        Alias/binary>>;

                none ->
                    <<"type "/utf8, (erlang:element(2, Ut))/binary>>
            end end
    ),
    Unqualified_values = gleam@list:map(
        erlang:element(6, Imp),
        fun(Uv) -> case erlang:element(3, Uv) of
                {some, Alias@1} ->
                    <<<<(erlang:element(2, Uv))/binary, " as "/utf8>>/binary,
                        Alias@1/binary>>;

                none ->
                    erlang:element(2, Uv)
            end end
    ),
    All_unqualified = lists:append(Unqualified_types, Unqualified_values),
    Unqualified_part = case All_unqualified of
        [] ->
            <<""/utf8>>;

        Items ->
            <<<<".{"/utf8, (gleam@string:join(Items, <<", "/utf8>>))/binary>>/binary,
                "}"/utf8>>
    end,
    <<<<Base/binary, Alias_part/binary>>/binary, Unqualified_part/binary>>.

-file("src/rally/tree_shaker.gleam", 586).
-spec render_attr_expr(glance:expression()) -> binary().
render_attr_expr(Expr) ->
    case Expr of
        {string, _, Value} ->
            <<<<"\""/utf8, Value/binary>>/binary, "\""/utf8>>;

        {variable, _, Name} ->
            Name;

        _ ->
            <<"..."/utf8>>
    end.

-file("src/rally/tree_shaker.gleam", 578).
-spec render_attribute(glance:attribute()) -> binary().
render_attribute(Attr) ->
    Args = begin
        _pipe = erlang:element(3, Attr),
        _pipe@1 = gleam@list:map(_pipe, fun render_attr_expr/1),
        gleam@string:join(_pipe@1, <<", "/utf8>>)
    end,
    <<<<<<<<"@"/utf8, (erlang:element(2, Attr))/binary>>/binary, "("/utf8>>/binary,
            Args/binary>>/binary,
        ")"/utf8>>.

-file("src/rally/tree_shaker.gleam", 554).
-spec extract_client_functions(
    glance:module_(),
    gleam@set:set(binary()),
    binary()
) -> list(binary()).
extract_client_functions(Ast, Client_fn_names, Source) ->
    _pipe = erlang:element(6, Ast),
    _pipe@1 = gleam@list:filter(
        _pipe,
        fun(Def) ->
            gleam@set:contains(
                Client_fn_names,
                erlang:element(3, erlang:element(3, Def))
            )
        end
    ),
    gleam@list:map(
        _pipe@1,
        fun(Def@1) ->
            Fn_source = rally_tree_shaker_ffi:byte_slice(
                Source,
                erlang:element(2, erlang:element(3, Def@1))
            ),
            Attrs = begin
                _pipe@2 = erlang:element(2, Def@1),
                gleam@list:filter_map(
                    _pipe@2,
                    fun(Attr) -> case erlang:element(2, Attr) of
                            <<"external"/utf8>> ->
                                {ok, render_attribute(Attr)};

                            _ ->
                                {error, nil}
                        end end
                )
            end,
            case Attrs of
                [] ->
                    Fn_source;

                _ ->
                    <<<<(gleam@string:join(Attrs, <<"\n"/utf8>>))/binary,
                            "\n"/utf8>>/binary,
                        Fn_source/binary>>
            end
        end
    ).

-file("src/rally/tree_shaker.gleam", 545).
-spec filter_constants(
    list(glance:definition(glance:constant())),
    gleam@set:set(binary())
) -> list(glance:definition(glance:constant())).
filter_constants(Constants, Client_refs) ->
    gleam@list:filter(
        Constants,
        fun(Def) ->
            gleam@set:contains(
                Client_refs,
                erlang:element(3, erlang:element(3, Def))
            )
        end
    ).

-file("src/rally/tree_shaker.gleam", 533).
-spec filter_type_aliases(
    list(glance:definition(glance:type_alias())),
    gleam@set:set(binary()),
    gleam@set:set(binary())
) -> list(glance:definition(glance:type_alias())).
filter_type_aliases(Aliases, Server_symbols, Client_refs) ->
    gleam@list:filter(
        Aliases,
        fun(Def) ->
            Name = erlang:element(3, erlang:element(3, Def)),
            not gleam@set:contains(Server_symbols, Name) andalso ((gleam@set:contains(
                Client_refs,
                Name
            )
            orelse (Name =:= <<"Model"/utf8>>))
            orelse (Name =:= <<"Msg"/utf8>>))
        end
    ).

-file("src/rally/tree_shaker.gleam", 512).
-spec filter_types(
    list(glance:definition(glance:custom_type())),
    gleam@set:set(binary()),
    gleam@set:set(binary())
) -> list(glance:definition(glance:custom_type())).
filter_types(Types, Server_symbols, Client_refs) ->
    gleam@list:filter(
        Types,
        fun(Def) ->
            Name = erlang:element(3, erlang:element(3, Def)),
            Is_server = gleam@set:contains(Server_symbols, Name),
            case Is_server of
                false ->
                    true;

                true ->
                    gleam@set:contains(Client_refs, Name) orelse gleam@list:any(
                        erlang:element(7, erlang:element(3, Def)),
                        fun(V) ->
                            gleam@set:contains(
                                Client_refs,
                                erlang:element(2, V)
                            )
                        end
                    )
            end
        end
    ).

-file("src/rally/tree_shaker.gleam", 505).
-spec last_segment(binary()) -> binary().
last_segment(Module_path) ->
    case begin
        _pipe = gleam@string:split(Module_path, <<"/"/utf8>>),
        gleam@list:last(_pipe)
    end of
        {ok, Seg} ->
            Seg;

        {error, nil} ->
            Module_path
    end.

-file("src/rally/tree_shaker.gleam", 470).
-spec filter_imports(
    list(glance:definition(glance:import())),
    gleam@set:set(binary()),
    gleam@set:set(binary())
) -> list(glance:definition(glance:import())).
filter_imports(Imports, Server_symbols, Client_refs) ->
    gleam@list:filter(
        Imports,
        fun(Def) ->
            Imp = erlang:element(3, Def),
            Imports_server_type = gleam@list:any(
                erlang:element(5, Imp),
                fun(Uq) ->
                    gleam@set:contains(Server_symbols, erlang:element(2, Uq))
                end
            ),
            case Imports_server_type of
                true ->
                    false;

                false ->
                    Has_unqualified_ref = gleam@list:any(
                        erlang:element(6, Imp),
                        fun(Uv) ->
                            gleam@set:contains(
                                Client_refs,
                                erlang:element(2, Uv)
                            )
                        end
                    )
                    orelse gleam@list:any(
                        erlang:element(5, Imp),
                        fun(Ut) ->
                            gleam@set:contains(
                                Client_refs,
                                erlang:element(2, Ut)
                            )
                        end
                    ),
                    Module_alias = case erlang:element(4, Imp) of
                        {some, {named, Name}} ->
                            Name;

                        _ ->
                            last_segment(erlang:element(3, Imp))
                    end,
                    Module_referenced = gleam@set:contains(
                        Client_refs,
                        Module_alias
                    ),
                    Has_unqualified_ref orelse Module_referenced
            end
        end
    ).

-file("src/rally/tree_shaker.gleam", 445).
-spec extract_type_refs(glance:type()) -> list(binary()).
extract_type_refs(T) ->
    case T of
        {named_type, _, Name, Module, Parameters} ->
            Mod_ref = case Module of
                {some, M} ->
                    [M];

                none ->
                    []
            end,
            lists:append(
                [[Name],
                    Mod_ref,
                    gleam@list:flat_map(Parameters, fun extract_type_refs/1)]
            );

        {tuple_type, _, Elements} ->
            gleam@list:flat_map(Elements, fun extract_type_refs/1);

        {function_type, _, Parameters@1, Return} ->
            lists:append(
                gleam@list:flat_map(Parameters@1, fun extract_type_refs/1),
                extract_type_refs(Return)
            );

        {variable_type, _, Name@1} ->
            [Name@1];

        {hole_type, _, _} ->
            []
    end.

-file("src/rally/tree_shaker.gleam", 333).
-spec extract_pattern_refs(glance:pattern()) -> list(binary()).
extract_pattern_refs(Pattern) ->
    case Pattern of
        {pattern_variant, _, Module, Constructor, Arguments, _} ->
            Module_refs = case Module of
                {some, Module@1} ->
                    [Module@1];

                none ->
                    []
            end,
            lists:append(
                [[Constructor],
                    Module_refs,
                    gleam@list:flat_map(Arguments, fun(Arg) -> case Arg of
                                {labelled_field, _, _, P} ->
                                    extract_pattern_refs(P);

                                {shorthand_field, _, _} ->
                                    [];

                                {unlabelled_field, P@1} ->
                                    extract_pattern_refs(P@1)
                            end end)]
            );

        {pattern_tuple, _, Elements} ->
            gleam@list:flat_map(Elements, fun extract_pattern_refs/1);

        {pattern_list, _, Elements@1, Tail} ->
            lists:append(
                gleam@list:flat_map(Elements@1, fun extract_pattern_refs/1),
                case Tail of
                    {some, Tail@1} ->
                        extract_pattern_refs(Tail@1);

                    none ->
                        []
                end
            );

        {pattern_assignment, _, Pattern@1, _} ->
            extract_pattern_refs(Pattern@1);

        {pattern_bit_string, _, Segments} ->
            gleam@list:flat_map(
                Segments,
                fun(Segment) ->
                    extract_pattern_refs(erlang:element(1, Segment))
                end
            );

        {pattern_int, _, _} ->
            [];

        {pattern_float, _, _} ->
            [];

        {pattern_string, _, _} ->
            [];

        {pattern_discard, _, _} ->
            [];

        {pattern_variable, _, _} ->
            [];

        {pattern_concatenate, _, _, _, _} ->
            []
    end.

-file("src/rally/tree_shaker.gleam", 231).
-spec extract_expr_refs(glance:expression()) -> list(binary()).
extract_expr_refs(Expr) ->
    case Expr of
        {variable, _, Name} ->
            [Name];

        {call, _, Function, Arguments} ->
            lists:append(
                extract_expr_refs(Function),
                gleam@list:flat_map(Arguments, fun(A) -> case A of
                            {labelled_field, _, _, V} ->
                                extract_expr_refs(V);

                            {shorthand_field, _, _} ->
                                [];

                            {unlabelled_field, V@1} ->
                                extract_expr_refs(V@1)
                        end end)
            );

        {fn, _, _, _, Body} ->
            gleam@list:flat_map(Body, fun extract_statement_refs/1);

        {block, _, Statements} ->
            gleam@list:flat_map(Statements, fun extract_statement_refs/1);

        {'case', _, Subjects, Clauses} ->
            lists:append(
                gleam@list:flat_map(Subjects, fun extract_expr_refs/1),
                gleam@list:flat_map(
                    Clauses,
                    fun(C) ->
                        lists:append(
                            [gleam@list:flat_map(
                                    erlang:element(2, C),
                                    fun(Patterns) ->
                                        gleam@list:flat_map(
                                            Patterns,
                                            fun extract_pattern_refs/1
                                        )
                                    end
                                ),
                                case erlang:element(3, C) of
                                    {some, Guard} ->
                                        extract_expr_refs(Guard);

                                    none ->
                                        []
                                end,
                                extract_expr_refs(erlang:element(4, C))]
                        )
                    end
                )
            );

        {tuple, _, Elements} ->
            gleam@list:flat_map(Elements, fun extract_expr_refs/1);

        {list, _, Elements@1, Rest} ->
            lists:append(
                gleam@list:flat_map(Elements@1, fun extract_expr_refs/1),
                case Rest of
                    {some, R} ->
                        extract_expr_refs(R);

                    none ->
                        []
                end
            );

        {record_update, _, _, _, Record, Fields} ->
            lists:append(
                extract_expr_refs(Record),
                gleam@list:flat_map(
                    Fields,
                    fun(F) -> case erlang:element(3, F) of
                            {some, V@2} ->
                                extract_expr_refs(V@2);

                            none ->
                                []
                        end end
                )
            );

        {field_access, _, Container, _} ->
            extract_expr_refs(Container);

        {binary_operator, _, _, Left, Right} ->
            lists:append(extract_expr_refs(Left), extract_expr_refs(Right));

        {negate_int, _, Value} ->
            extract_expr_refs(Value);

        {negate_bool, _, Value@1} ->
            extract_expr_refs(Value@1);

        {panic, _, Message} ->
            case Message of
                {some, V@3} ->
                    extract_expr_refs(V@3);

                none ->
                    []
            end;

        {todo, _, Message@1} ->
            case Message@1 of
                {some, V@4} ->
                    extract_expr_refs(V@4);

                none ->
                    []
            end;

        {tuple_index, _, Tuple, _} ->
            extract_expr_refs(Tuple);

        {fn_capture, _, _, Function@1, Arguments_before, Arguments_after} ->
            lists:append(
                [extract_expr_refs(Function@1),
                    gleam@list:flat_map(
                        Arguments_before,
                        fun(A@1) -> case A@1 of
                                {labelled_field, _, _, V@5} ->
                                    extract_expr_refs(V@5);

                                {shorthand_field, _, _} ->
                                    [];

                                {unlabelled_field, V@6} ->
                                    extract_expr_refs(V@6)
                            end end
                    ),
                    gleam@list:flat_map(Arguments_after, fun(A@2) -> case A@2 of
                                {labelled_field, _, _, V@7} ->
                                    extract_expr_refs(V@7);

                                {shorthand_field, _, _} ->
                                    [];

                                {unlabelled_field, V@8} ->
                                    extract_expr_refs(V@8)
                            end end)]
            );

        {bit_string, _, Segments} ->
            gleam@list:flat_map(
                Segments,
                fun(Seg) -> extract_expr_refs(erlang:element(1, Seg)) end
            );

        {echo, _, Expression, _} ->
            case Expression of
                {some, V@9} ->
                    extract_expr_refs(V@9);

                none ->
                    []
            end;

        {int, _, _} ->
            [];

        {float, _, _} ->
            [];

        {string, _, _} ->
            []
    end.

-file("src/rally/tree_shaker.gleam", 222).
-spec extract_statement_refs(glance:statement()) -> list(binary()).
extract_statement_refs(Stmt) ->
    case Stmt of
        {use, _, _, Expr} ->
            extract_expr_refs(Expr);

        {assignment, _, _, _, _, Expr@1} ->
            extract_expr_refs(Expr@1);

        {assert, _, Expr@2, _} ->
            extract_expr_refs(Expr@2);

        {expression, Expr@3} ->
            extract_expr_refs(Expr@3)
    end.

-file("src/rally/tree_shaker.gleam", 217).
-spec extract_fn_references(glance:function_()) -> list(binary()).
extract_fn_references(F) ->
    _pipe = erlang:element(7, F),
    gleam@list:flat_map(_pipe, fun extract_statement_refs/1).

-file("src/rally/tree_shaker.gleam", 374).
?DOC(" Collect all referenced names from client function bodies, signatures, and types.\n").
-spec collect_all_client_refs(
    glance:module_(),
    gleam@set:set(binary()),
    gleam@set:set(binary())
) -> gleam@set:set(binary()).
collect_all_client_refs(Ast, Client_fn_names, Server_symbols) ->
    Client_fns = begin
        _pipe = erlang:element(6, Ast),
        gleam@list:filter(
            _pipe,
            fun(Def) ->
                gleam@set:contains(
                    Client_fn_names,
                    erlang:element(3, erlang:element(3, Def))
                )
            end
        )
    end,
    Body_refs = gleam@list:flat_map(
        Client_fns,
        fun(Def@1) -> extract_fn_references(erlang:element(3, Def@1)) end
    ),
    Sig_refs = gleam@list:flat_map(
        Client_fns,
        fun(Def@2) ->
            Function = erlang:element(3, Def@2),
            Param_refs = gleam@list:flat_map(
                erlang:element(5, Function),
                fun(P) -> case erlang:element(4, P) of
                        {some, T} ->
                            extract_type_refs(T);

                        none ->
                            []
                    end end
            ),
            Return_refs = case erlang:element(6, Function) of
                {some, T@1} ->
                    extract_type_refs(T@1);

                none ->
                    []
            end,
            lists:append(Param_refs, Return_refs)
        end
    ),
    Client_types = begin
        _pipe@1 = erlang:element(3, Ast),
        gleam@list:filter(
            _pipe@1,
            fun(Def@3) ->
                not gleam@set:contains(
                    Server_symbols,
                    erlang:element(3, erlang:element(3, Def@3))
                )
            end
        )
    end,
    Type_refs = gleam@list:flat_map(
        Client_types,
        fun(Def@4) ->
            gleam@list:flat_map(
                erlang:element(7, erlang:element(3, Def@4)),
                fun(V) ->
                    lists:append(
                        [[erlang:element(2, V)],
                            gleam@list:flat_map(
                                erlang:element(3, V),
                                fun(F) -> case F of
                                        {labelled_variant_field, T@2, _} ->
                                            extract_type_refs(T@2);

                                        {unlabelled_variant_field, T@3} ->
                                            extract_type_refs(T@3)
                                    end end
                            )]
                    )
                end
            )
        end
    ),
    Alias_refs = gleam@list:flat_map(
        erlang:element(4, Ast),
        fun(Def@5) ->
            Name = erlang:element(3, erlang:element(3, Def@5)),
            case gleam@set:contains(Server_symbols, Name) orelse (((Name /= <<"Model"/utf8>>)
            andalso (Name /= <<"Msg"/utf8>>))
            andalso not gleam@set:contains(Client_fn_names, Name)) of
                true ->
                    [];

                false ->
                    extract_type_refs(
                        erlang:element(6, erlang:element(3, Def@5))
                    )
            end
        end
    ),
    _pipe@2 = lists:append([Body_refs, Sig_refs, Type_refs, Alias_refs]),
    gleam@set:from_list(_pipe@2).

-file("src/rally/tree_shaker.gleam", 188).
-spec expand_reachable(
    gleam@set:set(binary()),
    list(glance:definition(glance:function_())),
    gleam@set:set(binary()),
    gleam@set:set(binary())
) -> gleam@set:set(binary()).
expand_reachable(Frontier, Private_fns, All_private, Visited) ->
    case gleam@set:is_empty(Frontier) of
        true ->
            Visited;

        _ ->
            New_refs = begin
                _pipe = Private_fns,
                _pipe@1 = gleam@list:filter(
                    _pipe,
                    fun(Def) ->
                        gleam@set:contains(
                            Frontier,
                            erlang:element(3, erlang:element(3, Def))
                        )
                    end
                ),
                _pipe@2 = gleam@list:flat_map(
                    _pipe@1,
                    fun(Def@1) ->
                        extract_fn_references(erlang:element(3, Def@1))
                    end
                ),
                _pipe@3 = gleam@set:from_list(_pipe@2),
                _pipe@4 = gleam@set:intersection(_pipe@3, All_private),
                gleam@set:difference(_pipe@4, Visited)
            end,
            expand_reachable(
                New_refs,
                Private_fns,
                All_private,
                gleam@set:union(Visited, New_refs)
            )
    end.

-file("src/rally/tree_shaker.gleam", 158).
-spec collect_reachable_private_fns(
    glance:module_(),
    list(glance:definition(glance:function_())),
    gleam@set:set(binary())
) -> gleam@set:set(binary()).
collect_reachable_private_fns(Ast, Client_fns, Server_fns) ->
    Private_fns = begin
        _pipe = erlang:element(6, Ast),
        gleam@list:filter(
            _pipe,
            fun(Def) ->
                Function = erlang:element(3, Def),
                (erlang:element(4, Function) /= public) andalso not gleam@set:contains(
                    Server_fns,
                    erlang:element(3, Function)
                )
            end
        )
    end,
    Private_fn_names = begin
        _pipe@1 = gleam@list:map(
            Private_fns,
            fun(Def@1) -> erlang:element(3, erlang:element(3, Def@1)) end
        ),
        gleam@set:from_list(_pipe@1)
    end,
    Initial = begin
        _pipe@2 = Client_fns,
        _pipe@3 = gleam@list:flat_map(
            _pipe@2,
            fun(Def@2) -> extract_fn_references(erlang:element(3, Def@2)) end
        ),
        _pipe@4 = gleam@set:from_list(_pipe@3),
        gleam@set:intersection(_pipe@4, Private_fn_names)
    end,
    expand_reachable(Initial, Private_fns, Private_fn_names, Initial).

-file("src/rally/tree_shaker.gleam", 132).
-spec type_references_server_symbol(glance:type(), gleam@set:set(binary())) -> boolean().
type_references_server_symbol(T, Server_symbols) ->
    case T of
        {named_type, _, Name, _, Parameters} ->
            gleam@set:contains(Server_symbols, Name) orelse gleam@list:any(
                Parameters,
                fun(P) -> type_references_server_symbol(P, Server_symbols) end
            );

        {tuple_type, _, Elements} ->
            gleam@list:any(
                Elements,
                fun(E) -> type_references_server_symbol(E, Server_symbols) end
            );

        {function_type, _, Parameters@1, Return} ->
            gleam@list:any(
                Parameters@1,
                fun(P@1) ->
                    type_references_server_symbol(P@1, Server_symbols)
                end
            )
            orelse type_references_server_symbol(Return, Server_symbols);

        {variable_type, _, _} ->
            false;

        {hole_type, _, _} ->
            false
    end.

-file("src/rally/tree_shaker.gleam", 121).
?DOC(
    " A function is server-only if:\n"
    " - its name starts with \"server_\"\n"
    " - its name is \"load\"\n"
    " - any parameter type references a server symbol\n"
).
-spec is_server_function(glance:function_(), gleam@set:set(binary())) -> boolean().
is_server_function(F, Server_symbols) ->
    (gleam_stdlib:string_starts_with(erlang:element(3, F), <<"server_"/utf8>>)
    orelse (erlang:element(3, F) =:= <<"load"/utf8>>))
    orelse gleam@list:any(
        erlang:element(5, F),
        fun(Param) -> case erlang:element(4, Param) of
                {some, T} ->
                    type_references_server_symbol(T, Server_symbols);

                none ->
                    false
            end end
    ).

-file("src/rally/tree_shaker.gleam", 10).
?DOC(
    " Extract client-safe source code from a page module.\n"
    " Removes server_* functions, load, and anything only reachable from server code.\n"
    " server_symbols: known server-only type names (e.g. \"ServerContext\", handler message types)\n"
).
-spec shake(binary(), list(binary())) -> binary().
shake(Source, Server_symbols) ->
    Server_set = gleam@set:from_list(Server_symbols),
    case glance:module(Source) of
        {error, unexpected_end_of_input} ->
            Source;

        {error, {unexpected_token, _, _}} ->
            Source;

        {ok, Ast} ->
            Server_fns = begin
                _pipe = erlang:element(6, Ast),
                _pipe@1 = gleam@list:filter_map(
                    _pipe,
                    fun(Def) ->
                        Function = erlang:element(3, Def),
                        case is_server_function(Function, Server_set) of
                            true ->
                                {ok, erlang:element(3, Function)};

                            false ->
                                {error, nil}
                        end
                    end
                ),
                gleam@set:from_list(_pipe@1)
            end,
            Client_pub_fns = begin
                _pipe@2 = erlang:element(6, Ast),
                gleam@list:filter(
                    _pipe@2,
                    fun(Def@1) ->
                        Function@1 = erlang:element(3, Def@1),
                        (erlang:element(4, Function@1) =:= public) andalso not gleam@set:contains(
                            Server_fns,
                            erlang:element(3, Function@1)
                        )
                    end
                )
            end,
            Reachable_private = collect_reachable_private_fns(
                Ast,
                Client_pub_fns,
                Server_fns
            ),
            All_client_fn_names = begin
                _pipe@3 = gleam@list:map(
                    Client_pub_fns,
                    fun(Def@2) ->
                        erlang:element(3, erlang:element(3, Def@2))
                    end
                ),
                _pipe@4 = gleam@set:from_list(_pipe@3),
                gleam@set:union(_pipe@4, Reachable_private)
            end,
            Client_refs = collect_all_client_refs(
                Ast,
                All_client_fn_names,
                Server_set
            ),
            Client_imports = filter_imports(
                erlang:element(2, Ast),
                Server_set,
                Client_refs
            ),
            Client_types = filter_types(
                erlang:element(3, Ast),
                Server_set,
                Client_refs
            ),
            Client_type_aliases = filter_type_aliases(
                erlang:element(4, Ast),
                Server_set,
                Client_refs
            ),
            Client_constants = filter_constants(
                erlang:element(5, Ast),
                Client_refs
            ),
            Client_functions = extract_client_functions(
                Ast,
                All_client_fn_names,
                Source
            ),
            Import_lines = gleam@list:map(Client_imports, fun render_import/1),
            Type_lines = gleam@list:map(
                Client_types,
                fun(Ct) ->
                    rally_tree_shaker_ffi:byte_slice(
                        Source,
                        erlang:element(2, erlang:element(3, Ct))
                    )
                end
            ),
            Type_alias_lines = gleam@list:map(
                Client_type_aliases,
                fun(Ta) ->
                    rally_tree_shaker_ffi:byte_slice(
                        Source,
                        erlang:element(2, erlang:element(3, Ta))
                    )
                end
            ),
            Const_lines = gleam@list:map(
                Client_constants,
                fun(C) ->
                    rally_tree_shaker_ffi:byte_slice(
                        Source,
                        erlang:element(2, erlang:element(3, C))
                    )
                end
            ),
            <<(gleam@string:join(
                    lists:append(
                        [Import_lines,
                            Type_lines,
                            Type_alias_lines,
                            Const_lines,
                            Client_functions]
                    ),
                    <<"\n\n"/utf8>>
                ))/binary,
                "\n"/utf8>>
    end.