-module(ghtml). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/ghtml.gleam"). -export([parse_options/1, generate_all/2, main/0]). -export_type([generation_stats/0, cli_options/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. ?MODULEDOC( " ghtml - Gleam HTML Template Generator.\n" "\n" " A preprocessor that converts `.ghtml` template files into Gleam modules\n" " with Lustre `Element(msg)` render functions.\n" "\n" " ## Usage\n" "\n" " ```sh\n" " gleam run -m ghtml # Generate all\n" " gleam run -m ghtml -- force # Force regenerate\n" " gleam run -m ghtml -- watch # Watch mode\n" " gleam run -m ghtml -- clean # Remove orphans\n" " ```\n" ). -type generation_stats() :: {generation_stats, integer(), integer(), integer()}. -type cli_options() :: {cli_options, boolean(), boolean(), boolean(), binary()}. -file("src/ghtml.gleam", 39). ?DOC(" Parse command-line arguments into options\n"). -spec parse_options(list(binary())) -> cli_options(). parse_options(Args) -> Root = begin _pipe = Args, _pipe@1 = gleam@list:find( _pipe, fun(Arg) -> not gleam@list:contains( [<<"force"/utf8>>, <<"clean"/utf8>>, <<"watch"/utf8>>], Arg ) end ), gleam@result:unwrap(_pipe@1, <<"."/utf8>>) end, {cli_options, gleam@list:contains(Args, <<"force"/utf8>>), gleam@list:contains(Args, <<"clean"/utf8>>), gleam@list:contains(Args, <<"watch"/utf8>>), Root}. -file("src/ghtml.gleam", 65). ?DOC(" Run cleanup mode - remove orphaned generated files\n"). -spec run_clean(binary()) -> nil. run_clean(Root) -> Count = ghtml@scanner:cleanup_orphans(Root), gleam_stdlib:println( <<<<"Cleaned up "/utf8, (erlang:integer_to_binary(Count))/binary>>/binary, " orphaned files"/utf8>> ). -file("src/ghtml.gleam", 127). ?DOC(" Process a single template file\n"). -spec process_file(binary(), binary()) -> {ok, nil} | {error, binary()}. process_file(Source_path, Output_path) -> case simplifile:read(Source_path) of {ok, Content} -> Hash = ghtml@cache:hash_content(Content), case ghtml@parser:parse(Content) of {ok, Template} -> Gleam_code = ghtml@codegen:generate( Template, Source_path, Hash ), case simplifile:write(Output_path, Gleam_code) of {ok, _} -> gleam_stdlib:println( <<<<<<"✓ "/utf8, Source_path/binary>>/binary, " → "/utf8>>/binary, Output_path/binary>> ), {ok, nil}; {error, _} -> gleam_stdlib:println( <<"✗ Error writing "/utf8, Output_path/binary>> ), {error, <<"Write error"/utf8>>} end; {error, Errors} -> gleam_stdlib:println( <<<<"✗ Parse errors in "/utf8, Source_path/binary>>/binary, ":"/utf8>> ), gleam_stdlib:println( ghtml@parser:format_errors(Errors, Content) ), {error, <<"Parse error"/utf8>>} end; {error, _} -> gleam_stdlib:println( <<"✗ Error reading "/utf8, Source_path/binary>> ), {error, <<"Read error"/utf8>>} end. -file("src/ghtml.gleam", 106). ?DOC(" Generate all templates in the given directory\n"). -spec generate_all(binary(), boolean()) -> generation_stats(). generate_all(Root, Force) -> _pipe = ghtml@scanner:find_ghtml_files(Root), gleam@list:fold( _pipe, {generation_stats, 0, 0, 0}, fun(Stats, Source_path) -> Output_path = ghtml@scanner:to_output_path(Source_path), case Force orelse ghtml@cache:needs_regeneration( Source_path, Output_path ) of true -> case process_file(Source_path, Output_path) of {ok, _} -> {generation_stats, erlang:element(2, Stats) + 1, erlang:element(3, Stats), erlang:element(4, Stats)}; {error, _} -> {generation_stats, erlang:element(2, Stats), erlang:element(3, Stats), erlang:element(4, Stats) + 1} end; false -> gleam_stdlib:println( <<<<"· "/utf8, Source_path/binary>>/binary, " (unchanged)"/utf8>> ), {generation_stats, erlang:element(2, Stats), erlang:element(3, Stats) + 1, erlang:element(4, Stats)} end end ). -file("src/ghtml.gleam", 71). ?DOC(" Run generation mode - scan and generate templates\n"). -spec run_generate(binary(), cli_options()) -> nil. run_generate(Root, Options) -> gleam_stdlib:println(<<"ghtml v0.1.0"/utf8>>), gleam_stdlib:println(<<""/utf8>>), Stats = generate_all(Root, erlang:element(2, Options)), gleam_stdlib:println(<<""/utf8>>), gleam_stdlib:println( <<"Generated: "/utf8, (erlang:integer_to_binary(erlang:element(2, Stats)))/binary>> ), gleam_stdlib:println( <<"Skipped (unchanged): "/utf8, (erlang:integer_to_binary(erlang:element(3, Stats)))/binary>> ), case erlang:element(4, Stats) > 0 of true -> gleam_stdlib:println( <<"Errors: "/utf8, (erlang:integer_to_binary(erlang:element(4, Stats)))/binary>> ); false -> nil end, Orphans = ghtml@scanner:cleanup_orphans(Root), case Orphans > 0 of true -> gleam_stdlib:println( <<"Removed orphans: "/utf8, (erlang:integer_to_binary(Orphans))/binary>> ); false -> nil end, case erlang:element(4, Options) of true -> gleam_stdlib:println(<<""/utf8>>), _ = ghtml@watcher:start_watching(Root), gleam_erlang_ffi:sleep_forever(); false -> nil end. -file("src/ghtml.gleam", 55). ?DOC(" Main entry point for the CLI\n"). -spec main() -> nil. main() -> Options = parse_options(erlang:element(4, argv:load())), case erlang:element(3, Options) of true -> run_clean(erlang:element(5, Options)); false -> run_generate(erlang:element(5, Options), Options) end.