Packages

Hot reloading while in development for Gleam

Current section

Files

Jump to
radiate src radiate.erl
Raw

src/radiate.erl

-module(radiate).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-define(FILEPATH, "src/radiate.gleam").
-export([new/0, add_dir/2, on_reload/2, before_reload/2, set_initializer/2, start_state/1, start/1]).
-export_type([no_directories/0, has_directories/0, no_initializer/0, has_initializer/0, no_callback/0, has_callback/0, has_before_callback/0, no_before_callback/0, builder/6, module_/0, what/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type no_directories() :: any().
-type has_directories() :: any().
-type no_initializer() :: any().
-type has_initializer() :: any().
-type no_callback() :: any().
-type has_callback() :: any().
-type has_before_callback() :: any().
-type no_before_callback() :: any().
-opaque builder(GCF, GCG, GCH, GCI, GCJ, GCK) :: {builder,
list(binary()),
fun((GCF, binary()) -> GCF),
gleam@option:option(fun((gleam@erlang@process:subject(filespy:change(GCG))) -> {ok,
gleam@otp@actor:initialised(nil, filespy:change(GCG), gleam@erlang@process:subject(filespy:change(GCG)))} |
{error, GCG})),
gleam@option:option(fun((GCF, binary()) -> GCF))} |
{gleam_phantom, GCH, GCI, GCJ, GCK}.
-type module_() :: any().
-type what() :: any().
-file("src/radiate.gleam", 62).
?DOC(" Construct a new builder\n").
-spec new() -> builder(nil, nil, no_directories(), no_initializer(), no_callback(), no_before_callback()).
new() ->
_pipe = fun(State, Path) ->
gleam_stdlib:println(
<<<<"Change in "/utf8, Path/binary>>/binary, ", reloading."/utf8>>
),
State
end,
{builder, [], _pipe, none, none}.
-file("src/radiate.gleam", 80).
?DOC(
" Add a directory to watch. The path can be relative or absolute.\n"
" macOS users: always use `\".\"` or an absolute path, otherwise it won't work\n"
" properly!\n"
).
-spec add_dir(builder(GCR, nil, any(), GCT, GCU, GCV), binary()) -> builder(GCR, nil, has_directories(), GCT, GCU, GCV).
add_dir(Builder, Dir) ->
{builder,
[Dir | erlang:element(2, Builder)],
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder)}.
-file("src/radiate.gleam", 107).
?DOC(" Add a callback to be run after reload\n").
-spec on_reload(
builder(GDI, nil, GDJ, GDK, no_callback(), GDL),
fun((GDI, binary()) -> GDI)
) -> builder(GDI, nil, GDJ, GDK, has_callback(), GDL).
on_reload(Builder, Callback) ->
{builder,
erlang:element(2, Builder),
Callback,
erlang:element(4, Builder),
erlang:element(5, Builder)}.
-file("src/radiate.gleam", 134).
?DOC(" Add a callback to handle a process before reloading modules\n").
-spec before_reload(
builder(GDY, nil, GDZ, GEA, GEB, no_before_callback()),
fun((GDY, binary()) -> GDY)
) -> builder(GDY, nil, GDZ, GEA, GEB, has_before_callback()).
before_reload(Builder, Before_callback) ->
{builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
{some, Before_callback}}.
-file("src/radiate.gleam", 164).
?DOC(
" Add an initializer\n"
"\n"
" This will be run in the file watcher process, so it can be useful to, for\n"
" example, add state to an actor\n"
).
-spec set_initializer(
builder(GEO, nil, GEP, no_initializer(), GEQ, GER),
fun((gleam@erlang@process:subject(filespy:change(nil))) -> {ok,
gleam@otp@actor:initialised(nil, filespy:change(nil), gleam@erlang@process:subject(filespy:change(nil)))} |
{error, nil})
) -> builder(GEO, nil, GEP, has_initializer(), GEQ, GER).
set_initializer(Builder, Initializer) ->
{builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
{some, Initializer},
erlang:element(5, Builder)}.
-file("src/radiate.gleam", 237).
?DOC(" Execute the file reload\n").
-spec execute_reload(
builder(GGC, nil, has_directories(), has_initializer(), any(), any()),
GGC,
binary()
) -> GGC.
execute_reload(Builder, State, Path) ->
Build_result = shellout:command(
<<"gleam"/utf8>>,
[<<"build"/utf8>>],
<<"."/utf8>>,
[]
),
case Build_result of
{ok, _} ->
Mods = code:modified_modules(),
gleam@list:each(Mods, fun code:purge/1),
_ = code:atomic_load(Mods),
(erlang:element(3, Builder))(State, Path);
{error, {_, Message}} ->
_pipe = Message,
gleam_stdlib:print_error(_pipe),
State
end.
-file("src/radiate.gleam", 269).
?DOC(" Start reloader, ensuring that a state has been set\n").
-spec start_state(
builder(nil, nil, has_directories(), has_initializer(), any(), any())
) -> {ok,
gleam@otp@actor:started(gleam@erlang@process:subject(filespy:change(nil)))} |
{error, gleam@otp@actor:start_error()}.
start_state(Builder) ->
_pipe = filespy:new(),
_pipe@3 = filespy:set_initializer(
_pipe,
fun(Subject) ->
Init@1 = case erlang:element(4, Builder) of
{some, Init} -> Init;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"radiate"/utf8>>,
function => <<"start_state"/utf8>>,
line => 281,
value => _assert_fail,
start => 5632,
'end' => 5675,
pattern_start => 5643,
pattern_end => 5653})
end,
Initialised = Init@1(Subject),
case Initialised of
{ok, Initialised@1} ->
_pipe@1 = Initialised@1,
_pipe@2 = gleam@otp@actor:returning(_pipe@1, Subject),
{ok, _pipe@2};
{error, _} ->
{error, <<"Error during initializing"/utf8>>}
end
end
),
_pipe@4 = filespy:add_dirs(_pipe@3, erlang:element(2, Builder)),
_pipe@5 = filespy:set_actor_handler(_pipe@4, fun(State, Msg) -> case Msg of
{custom, _} ->
gleam@otp@actor:continue(State);
{change, Path, Events} ->
gleam@list:fold(
Events,
State,
fun(State@1, Event) -> case Event of
closed ->
case gleam_stdlib:string_ends_with(
Path,
<<".gleam"/utf8>>
) of
true ->
case erlang:element(5, Builder) of
{some, Before} ->
State@2 = Before(
State@1,
Path
),
execute_reload(
Builder,
State@2,
Path
);
_ ->
execute_reload(
Builder,
State@1,
Path
)
end,
State@1;
_ ->
State@1
end;
modified ->
case gleam_stdlib:string_ends_with(
Path,
<<".gleam"/utf8>>
) of
true ->
case erlang:element(5, Builder) of
{some, Before} ->
State@2 = Before(
State@1,
Path
),
execute_reload(
Builder,
State@2,
Path
);
_ ->
execute_reload(
Builder,
State@1,
Path
)
end,
State@1;
_ ->
State@1
end;
_ ->
State@1
end end
),
gleam@otp@actor:continue(State)
end end),
filespy:start(_pipe@5).
-file("src/radiate.gleam", 212).
?DOC(" Start reloader, if no state has been set\n").
-spec start(
builder(nil, nil, has_directories(), no_initializer(), any(), any())
) -> {ok,
gleam@otp@actor:started(gleam@erlang@process:subject(filespy:change(nil)))} |
{error, gleam@otp@actor:start_error()}.
start(Builder) ->
_pipe = Builder,
_pipe@5 = set_initializer(
_pipe,
fun(Subject) ->
Selector = begin
_pipe@1 = gleam_erlang_ffi:new_selector(),
gleam@erlang@process:select(_pipe@1, Subject)
end,
_pipe@2 = gleam@otp@actor:initialised(nil),
_pipe@3 = gleam@otp@actor:returning(_pipe@2, Subject),
_pipe@4 = gleam@otp@actor:selecting(_pipe@3, Selector),
{ok, _pipe@4}
end
),
start_state(_pipe@5).