-module(drip@internal@project). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/drip/internal/project.gleam"). -export([root/1, name/1, prefix/1, with_overrides/3, source/1, vendor_directory/1, file_path/2, relative_path/2, vendored_files/2, is_vendored/2, ensure_initialized/1, load_from/1, load/0, write_settings/3, write_changes/1, delete_files/1, vendored_filenames/1, vendored_in/2, rewrite_index_css/1, scaffold/2]). -export_type([project/0, file_change/0, overwrite/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(false). -opaque project() :: {project, binary(), binary(), binary(), drip@internal@source:source()}. -type file_change() :: {created, binary()} | {modified, binary()} | {unmodified, binary()} | {deleted, binary()}. -type overwrite() :: force | preserve. -file("src/drip/internal/project.gleam", 17). ?DOC(false). -spec root(project()) -> binary(). root(Project) -> erlang:element(2, Project). -file("src/drip/internal/project.gleam", 21). ?DOC(false). -spec name(project()) -> binary(). name(Project) -> erlang:element(3, Project). -file("src/drip/internal/project.gleam", 25). ?DOC(false). -spec prefix(project()) -> binary(). prefix(Project) -> erlang:element(4, Project). -file("src/drip/internal/project.gleam", 503). ?DOC(false). -spec is_digit(binary()) -> boolean(). is_digit(Char) -> gleam_stdlib:contains_string(<<"0123456789"/utf8>>, Char). -file("src/drip/internal/project.gleam", 499). ?DOC(false). -spec is_lower_alpha(binary()) -> boolean(). is_lower_alpha(Char) -> gleam_stdlib:contains_string(<<"abcdefghijklmnopqrstuvwxyz"/utf8>>, Char). -file("src/drip/internal/project.gleam", 495). ?DOC(false). -spec is_identifier(binary()) -> boolean(). is_identifier(Char) -> (is_lower_alpha(Char) orelse is_digit(Char)) orelse (Char =:= <<"_"/utf8>>). -file("src/drip/internal/project.gleam", 488). ?DOC(false). -spec is_valid_path_segment(binary()) -> boolean(). is_valid_path_segment(Segment) -> case gleam@string:to_graphemes(Segment) of [] -> false; [First | Rest] -> is_lower_alpha(First) andalso gleam@list:all( Rest, fun is_identifier/1 ) end. -file("src/drip/internal/project.gleam", 484). ?DOC(false). -spec is_valid_prefix(binary()) -> boolean(). is_valid_prefix(Prefix) -> _pipe = gleam@string:split(Prefix, <<"/"/utf8>>), gleam@list:all(_pipe, fun is_valid_path_segment/1). -file("src/drip/internal/project.gleam", 29). ?DOC(false). -spec with_overrides( project(), gleam@option:option(binary()), gleam@option:option(binary()) ) -> {ok, project()} | {error, drip@internal@error:error()}. with_overrides(Project, Prefix, Source) -> gleam@result:'try'(case Prefix of {some, Value} -> case is_valid_prefix(Value) of true -> {ok, {project, erlang:element(2, Project), erlang:element(3, Project), Value, erlang:element(5, Project)}}; false -> {error, {invalid_prefix, Value}} end; none -> {ok, Project} end, fun(Project@1) -> case Source of {some, Value@1} -> gleam@result:map( drip@internal@source:parse(Value@1), fun(Source@1) -> {project, erlang:element(2, Project@1), erlang:element(3, Project@1), erlang:element(4, Project@1), Source@1} end ); none -> {ok, Project@1} end end). -file("src/drip/internal/project.gleam", 51). ?DOC(false). -spec source(project()) -> drip@internal@source:source(). source(Project) -> erlang:element(5, Project). -file("src/drip/internal/project.gleam", 55). ?DOC(false). -spec vendor_directory(project()) -> binary(). vendor_directory(Project) -> filepath:join( erlang:element(2, Project), <<"src/"/utf8, (erlang:element(4, Project))/binary>> ). -file("src/drip/internal/project.gleam", 59). ?DOC(false). -spec theme_css_path(project()) -> binary(). theme_css_path(Project) -> filepath:join(vendor_directory(Project), <<"theme.css"/utf8>>). -file("src/drip/internal/project.gleam", 63). ?DOC(false). -spec index_css_path(project()) -> binary(). index_css_path(Project) -> filepath:join(vendor_directory(Project), <<"index.css"/utf8>>). -file("src/drip/internal/project.gleam", 67). ?DOC(false). -spec entry_css_path(project()) -> binary(). entry_css_path(Project) -> filepath:join( erlang:element(2, Project), <<<<"src/"/utf8, (erlang:element(3, Project))/binary>>/binary, ".css"/utf8>> ). -file("src/drip/internal/project.gleam", 71). ?DOC(false). -spec file_path(project(), binary()) -> binary(). file_path(Project, Filename) -> filepath:join(vendor_directory(Project), Filename). -file("src/drip/internal/project.gleam", 86). ?DOC(false). -spec trim_leading_slash(binary()) -> binary(). trim_leading_slash(Path) -> case gleam_stdlib:string_starts_with(Path, <<"/"/utf8>>) of true -> gleam@string:drop_start(Path, 1); false -> Path end. -file("src/drip/internal/project.gleam", 76). ?DOC(false). -spec relative_path(project(), binary()) -> binary(). relative_path(Project, Path) -> case gleam_stdlib:string_starts_with(Path, erlang:element(2, Project)) of true -> _pipe = Path, _pipe@1 = gleam@string:drop_start( _pipe, string:length(erlang:element(2, Project)) ), trim_leading_slash(_pipe@1); false -> Path end. -file("src/drip/internal/project.gleam", 329). ?DOC(false). -spec read_vendor_directory(project()) -> {ok, list(binary())} | {error, drip@internal@error:error()}. read_vendor_directory(Project) -> Directory = vendor_directory(Project), case simplifile_erl:read_directory(Directory) of {ok, Names} -> {ok, Names}; {error, enoent} -> {ok, []}; {error, Cause} -> {error, {failed_to_read_project_directory, Directory, Cause}} end. -file("src/drip/internal/project.gleam", 308). ?DOC(false). -spec vendored_files(project(), binary()) -> {ok, list(binary())} | {error, drip@internal@error:error()}. vendored_files(Project, Name) -> gleam@result:map( read_vendor_directory(Project), fun(Filenames) -> _pipe = Filenames, _pipe@1 = gleam@list:filter( _pipe, fun(Filename) -> gleam_stdlib:string_starts_with( Filename, <> ) end ), _pipe@2 = gleam@list:sort(_pipe@1, fun gleam@string:compare/2), gleam@list:map( _pipe@2, fun(_capture) -> file_path(Project, _capture) end ) end ). -file("src/drip/internal/project.gleam", 93). ?DOC(false). -spec is_vendored(project(), binary()) -> {ok, boolean()} | {error, drip@internal@error:error()}. is_vendored(Project, Name) -> gleam@result:map( vendored_files(Project, Name), fun(Files) -> Files /= [] end ). -file("src/drip/internal/project.gleam", 246). ?DOC(false). -spec file_exists(binary()) -> {ok, boolean()} | {error, drip@internal@error:error()}. file_exists(Path) -> case simplifile_erl:is_file(Path) of {ok, Present} -> {ok, Present}; {error, Cause} -> {error, {failed_to_check_if_project_file_exists, Path, Cause}} end. -file("src/drip/internal/project.gleam", 98). ?DOC(false). -spec is_initialized(project()) -> {ok, boolean()} | {error, drip@internal@error:error()}. is_initialized(Project) -> file_exists(index_css_path(Project)). -file("src/drip/internal/project.gleam", 103). ?DOC(false). -spec ensure_initialized(project()) -> {ok, nil} | {error, drip@internal@error:error()}. ensure_initialized(Project) -> gleam@result:'try'( is_initialized(Project), fun(Initialized) -> case Initialized of true -> {ok, nil}; false -> {error, {failed_to_find_index_stylesheet, index_css_path(Project)}} end end ). -file("src/drip/internal/project.gleam", 165). ?DOC(false). -spec find_project_root(binary(), integer()) -> {ok, binary()} | {error, drip@internal@error:error()}. find_project_root(Directory, Depth) -> case simplifile_erl:is_file(filepath:join(Directory, <<"gleam.toml"/utf8>>)) of {ok, true} -> {ok, Directory}; _ -> case Depth < 64 of true -> find_project_root( filepath:join(Directory, <<"../"/utf8>>), Depth + 1 ); false -> {error, {failed_to_find_project_root, Directory}} end end. -file("src/drip/internal/project.gleam", 121). ?DOC(false). -spec load_from(binary()) -> {ok, project()} | {error, drip@internal@error:error()}. load_from(Directory) -> gleam@result:'try'( find_project_root(Directory, 0), fun(Root) -> gleam@result:'try'( begin _pipe = simplifile:read( filepath:join(Root, <<"gleam.toml"/utf8>>) ), gleam@result:map_error( _pipe, fun(Field@0) -> {failed_to_read_gleam_toml, Field@0} end ) end, fun(Toml) -> gleam@result:'try'( begin _pipe@1 = tomlet:parse(Toml), gleam@result:map_error( _pipe@1, fun(Field@0) -> {failed_to_parse_gleam_toml, Field@0} end ) end, fun(Document) -> gleam@result:'try'( begin _pipe@2 = tomlet:get_string( Document, [<<"name"/utf8>>] ), gleam@result:map_error( _pipe@2, fun(Field@0) -> {failed_to_get_gleam_toml_value, Field@0} end ) end, fun(Name) -> gleam@result:'try'( case tomlet:get_string( Document, [<<"tools"/utf8>>, <<"drip"/utf8>>, <<"prefix"/utf8>>] ) of {ok, Prefix} -> case is_valid_prefix(Prefix) of true -> {ok, Prefix}; false -> {error, {invalid_prefix, Prefix}} end; {error, {key_not_found, _}} -> {ok, <<"ui"/utf8>>}; {error, Cause} -> {error, {failed_to_get_gleam_toml_value, Cause}} end, fun(Prefix@1) -> gleam@result:'try'( case tomlet:get_string( Document, [<<"tools"/utf8>>, <<"drip"/utf8>>, <<"source"/utf8>>] ) of {ok, Source} -> {ok, Source}; {error, {key_not_found, _}} -> {ok, <<"https://github.com/scott-ray-wilson/drip/releases/latest/download"/utf8>>}; {error, Cause@1} -> {error, {failed_to_get_gleam_toml_value, Cause@1}} end, fun(Raw_source) -> gleam@result:map( drip@internal@source:parse( Raw_source ), fun(Source@1) -> {project, Root, Name, Prefix@1, Source@1} end ) end ) end ) end ) end ) end ) end ). -file("src/drip/internal/project.gleam", 117). ?DOC(false). -spec load() -> {ok, project()} | {error, drip@internal@error:error()}. load() -> load_from(<<"./"/utf8>>). -file("src/drip/internal/project.gleam", 179). ?DOC(false). -spec gleam_toml_path(project()) -> binary(). gleam_toml_path(Project) -> filepath:join(erlang:element(2, Project), <<"gleam.toml"/utf8>>). -file("src/drip/internal/project.gleam", 258). ?DOC(false). -spec write_file(binary(), binary()) -> {ok, nil} | {error, drip@internal@error:error()}. write_file(Path, Body) -> gleam@result:'try'( begin _pipe = simplifile:create_directory_all( filepath:directory_name(Path) ), gleam@result:map_error( _pipe, fun(_capture) -> {failed_to_create_project_directory, Path, _capture} end ) end, fun(_) -> _pipe@1 = simplifile:write(Path, Body), gleam@result:map_error( _pipe@1, fun(_capture@1) -> {failed_to_write_project_file, Path, _capture@1} end ) end ). -file("src/drip/internal/project.gleam", 253). ?DOC(false). -spec read_file(binary()) -> {ok, binary()} | {error, drip@internal@error:error()}. read_file(Path) -> _pipe = simplifile:read(Path), gleam@result:map_error( _pipe, fun(_capture) -> {failed_to_read_project_file, Path, _capture} end ). -file("src/drip/internal/project.gleam", 276). ?DOC(false). -spec write_change(binary(), binary()) -> {ok, file_change()} | {error, drip@internal@error:error()}. write_change(Path, Body) -> gleam@result:'try'(file_exists(Path), fun(Exists) -> case Exists of false -> gleam@result:'try'( write_file(Path, Body), fun(_) -> {ok, {created, Path}} end ); true -> gleam@result:'try'( read_file(Path), fun(Current) -> case Current =:= Body of true -> {ok, {unmodified, Path}}; false -> gleam@result:'try'( write_file(Path, Body), fun(_) -> {ok, {modified, Path}} end ) end end ) end end). -file("src/drip/internal/project.gleam", 184). ?DOC(false). -spec write_settings( project(), gleam@option:option(binary()), gleam@option:option(binary()) ) -> {ok, list(file_change())} | {error, drip@internal@error:error()}. write_settings(Project, Prefix, Source) -> Settings = lists:append([case Prefix of {some, Value} -> [{<<"prefix"/utf8>>, Value}]; none -> [] end, case Source of {some, Value@1} -> [{<<"source"/utf8>>, Value@1}]; none -> [] end]), case Settings of [] -> {ok, []}; _ -> Path = gleam_toml_path(Project), gleam@result:'try'( read_file(Path), fun(Raw) -> gleam@result:'try'( begin _pipe = tomlet:parse(Raw), gleam@result:map_error( _pipe, fun(Field@0) -> {failed_to_parse_gleam_toml, Field@0} end ) end, fun(Document) -> gleam@result:'try'( case tomlet:get( Document, [<<"tools"/utf8>>, <<"drip"/utf8>>] ) of {ok, _} -> {ok, Document}; {error, _} -> _pipe@1 = tomlet:parse( <<(gleam@string:trim_end(Raw))/binary, "\n\n"/utf8>> ), gleam@result:map_error( _pipe@1, fun(Field@0) -> {failed_to_parse_gleam_toml, Field@0} end ) end, fun(Document@1) -> gleam@result:'try'( gleam@list:try_fold( Settings, Document@1, fun(Document@2, Setting) -> {Key, Value@2} = Setting, _pipe@2 = tomlet:set_string( Document@2, [<<"tools"/utf8>>, <<"drip"/utf8>>, Key], Value@2 ), gleam@result:map_error( _pipe@2, fun(Field@0) -> {failed_to_edit_gleam_toml, Field@0} end ) end ), fun(Updated) -> gleam@result:map( write_change( Path, tomlet:to_string(Updated) ), fun(Change) -> [Change] end ) end ) end ) end ) end ) end. -file("src/drip/internal/project.gleam", 267). ?DOC(false). -spec write_changes(list({binary(), binary()})) -> {ok, list(file_change())} | {error, drip@internal@error:error()}. write_changes(Files) -> gleam@list:try_map( Files, fun(File) -> {Path, Body} = File, write_change(Path, Body) end ). -file("src/drip/internal/project.gleam", 303). ?DOC(false). -spec delete_file(binary()) -> {ok, nil} | {error, drip@internal@error:error()}. delete_file(Path) -> _pipe = simplifile_erl:delete(Path), gleam@result:map_error( _pipe, fun(_capture) -> {failed_to_delete_project_file, Path, _capture} end ). -file("src/drip/internal/project.gleam", 296). ?DOC(false). -spec delete_files(list(binary())) -> {ok, list(file_change())} | {error, drip@internal@error:error()}. delete_files(Paths) -> gleam@list:try_map( Paths, fun(Path) -> gleam@result:map(delete_file(Path), fun(_) -> {deleted, Path} end) end ). -file("src/drip/internal/project.gleam", 320). ?DOC(false). -spec vendored_filenames(project()) -> {ok, list(binary())} | {error, drip@internal@error:error()}. vendored_filenames(Project) -> read_vendor_directory(Project). -file("src/drip/internal/project.gleam", 325). ?DOC(false). -spec vendored_in(list(binary()), binary()) -> boolean(). vendored_in(Filenames, Name) -> gleam@list:any( Filenames, fun(Filename) -> gleam_stdlib:string_starts_with(Filename, <>) end ). -file("src/drip/internal/project.gleam", 358). ?DOC(false). -spec index_css_body(list(binary())) -> binary(). index_css_body(Css_files) -> Imports = begin _pipe = Css_files, _pipe@1 = gleam@list:unique(_pipe), _pipe@2 = gleam@list:sort(_pipe@1, fun gleam@string:compare/2), gleam@list:map( _pipe@2, fun(Name) -> <<<<"@import \"./"/utf8, Name/binary>>/binary, "\";"/utf8>> end ) end, <<(gleam@string:join( [<<"/* Generated by drip; do not edit. "/utf8, "Run drip add / drip remove to change it. */"/utf8>>, <<"@import \"./theme.css\";"/utf8>> | Imports], <<"\n"/utf8>> ))/binary, "\n"/utf8>>. -file("src/drip/internal/project.gleam", 367). ?DOC(false). -spec is_element_css(binary()) -> boolean(). is_element_css(Name) -> (gleam_stdlib:string_ends_with(Name, <<".css"/utf8>>) andalso (Name /= <<"theme.css"/utf8>>)) andalso (Name /= <<"index.css"/utf8>>). -file("src/drip/internal/project.gleam", 347). ?DOC(false). -spec current_index_css_body(project()) -> {ok, binary()} | {error, drip@internal@error:error()}. current_index_css_body(Project) -> gleam@result:'try'( read_vendor_directory(Project), fun(Filenames) -> Css_files = gleam@list:filter(Filenames, fun is_element_css/1), {ok, index_css_body(Css_files)} end ). -file("src/drip/internal/project.gleam", 340). ?DOC(false). -spec rewrite_index_css(project()) -> {ok, file_change()} | {error, drip@internal@error:error()}. rewrite_index_css(Project) -> gleam@result:'try'( current_index_css_body(Project), fun(Body) -> write_change(index_css_path(Project), Body) end ). -file("src/drip/internal/project.gleam", 381). ?DOC(false). -spec index_import(project()) -> binary(). index_import(Project) -> <<<<"@import \"./"/utf8, (prefix(Project))/binary>>/binary, "/index.css\";"/utf8>>. -file("src/drip/internal/project.gleam", 377). ?DOC(false). -spec entry_css_body(project()) -> binary(). entry_css_body(Project) -> <<<<<<"@import \"tailwindcss\";"/utf8, "\n"/utf8>>/binary, (index_import(Project))/binary>>/binary, "\n"/utf8>>. -file("src/drip/internal/project.gleam", 439). ?DOC(false). -spec is_tailwind_import(binary()) -> boolean(). is_tailwind_import(Line) -> Line@1 = gleam@string:trim(Line), gleam_stdlib:string_starts_with(Line@1, <<"@import"/utf8>>) andalso gleam_stdlib:contains_string( Line@1, <<"tailwindcss"/utf8>> ). -file("src/drip/internal/project.gleam", 428). ?DOC(false). -spec splice_after_tailwind(list(binary()), binary()) -> list(binary()). splice_after_tailwind(Lines, Import_line) -> case gleam@list:split_while( Lines, fun(Line) -> not is_tailwind_import(Line) end ) of {_, []} -> Lines; {Before, [Anchor | After]} -> lists:append(Before, [Anchor, Import_line | After]) end. -file("src/drip/internal/project.gleam", 408). ?DOC(false). -spec merge_index_import(project(), binary()) -> gleam@option:option(binary()). merge_index_import(Project, Body) -> Import_line = index_import(Project), Lines = gleam@string:split(Body, <<"\n"/utf8>>), Has_index = gleam@list:any( Lines, fun(Line) -> gleam@string:trim(Line) =:= Import_line end ), Has_tailwind = gleam@list:any(Lines, fun is_tailwind_import/1), case {Has_index, Has_tailwind} of {true, true} -> none; {true, false} -> {some, <<<<"@import \"tailwindcss\";"/utf8, "\n"/utf8>>/binary, Body/binary>>}; {false, true} -> {some, gleam@string:join( splice_after_tailwind(Lines, Import_line), <<"\n"/utf8>> )}; {false, false} -> {some, <<(entry_css_body(Project))/binary, Body/binary>>} end. -file("src/drip/internal/project.gleam", 387). ?DOC(false). -spec wire_entry_css(project()) -> {ok, file_change()} | {error, drip@internal@error:error()}. wire_entry_css(Project) -> Path = entry_css_path(Project), gleam@result:'try'(file_exists(Path), fun(Exists) -> case Exists of false -> gleam@result:'try'( write_file(Path, entry_css_body(Project)), fun(_) -> {ok, {created, Path}} end ); true -> gleam@result:'try'( read_file(Path), fun(Body) -> case merge_index_import(Project, Body) of none -> {ok, {unmodified, Path}}; {some, Merged} -> gleam@result:'try'( write_file(Path, Merged), fun(_) -> {ok, {modified, Path}} end ) end end ) end end). -file("src/drip/internal/project.gleam", 464). ?DOC(false). -spec guard_overwrite(list(binary()), overwrite()) -> {ok, nil} | {error, drip@internal@error:error()}. guard_overwrite(Paths, Overwrite) -> case Overwrite of force -> {ok, nil}; preserve -> gleam@list:try_each( Paths, fun(Path) -> gleam@result:'try'( file_exists(Path), fun(Exists) -> case Exists of true -> {error, {project_file_already_exists, Path}}; false -> {ok, nil} end end ) end ) end. -file("src/drip/internal/project.gleam", 446). ?DOC(false). -spec scaffold(project(), overwrite()) -> {ok, list(file_change())} | {error, drip@internal@error:error()}. scaffold(Project, Overwrite) -> Theme = theme_css_path(Project), Index = index_css_path(Project), gleam@result:'try'( guard_overwrite([Theme, Index], Overwrite), fun(_) -> gleam@result:'try'( drip@internal@source:fetch( erlang:element(5, Project), <<"theme.css"/utf8>> ), fun(Theme_body) -> gleam@result:'try'( write_change(Theme, Theme_body), fun(Theme_change) -> gleam@result:'try'( current_index_css_body(Project), fun(Index_body) -> gleam@result:'try'( write_change(Index, Index_body), fun(Index_change) -> gleam@result:'try'( wire_entry_css(Project), fun(Entry) -> {ok, [Theme_change, Index_change, Entry]} end ) end ) end ) end ) end ) end ).