-module(lustre_dev_tools@esbuild).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([download/2, bundle/3, bundle_node_modules/2]).
-export_type([package_json/0]).
-type package_json() :: {package_json, binary(), gleam@option:option(binary())}.
-spec module_main_file(binary()) -> {ok, binary()} |
{error, gleam@json:decode_error()}.
module_main_file(Json) ->
Dot_encoder = gleam@dynamic:field(
<<"import"/utf8>>,
fun gleam@dynamic:string/1
),
Exports_decoder = gleam@dynamic:field(<<"."/utf8>>, Dot_encoder),
Decoder = gleam@dynamic:decode2(
fun(Field@0, Field@1) -> {package_json, Field@0, Field@1} end,
gleam@dynamic:field(<<"main"/utf8>>, fun gleam@dynamic:string/1),
gleam@dynamic:optional_field(<<"exports"/utf8>>, Exports_decoder)
),
gleam@result:map(
gleam@json:decode(Json, Decoder),
fun(Package_json) -> case erlang:element(3, Package_json) of
{some, Exports} ->
Exports;
none ->
erlang:element(2, Package_json)
end end
).
-spec get_module_main(binary()) -> {ok, binary()} |
{error, lustre_dev_tools@error:error()}.
get_module_main(Name) ->
Root = lustre_dev_tools@project:root(),
Module = filepath:join(Root, <<"node_modules/"/utf8, Name/binary>>),
Module_json = filepath:join(Module, <<"package.json"/utf8>>),
case simplifile_erl:is_directory(Module) of
{ok, false} ->
{error, {dependency_not_found, Name}};
{error, _} ->
{error, {dependency_not_found, Name}};
{ok, true} ->
gleam@result:'try'(
begin
_pipe = simplifile:read(Module_json),
gleam@result:replace_error(_pipe, {dependency_faulty, Name})
end,
fun(Json) ->
gleam@result:'try'(
begin
_pipe@1 = module_main_file(Json),
gleam@result:replace_error(
_pipe@1,
{dependency_faulty, Name}
)
end,
fun(Main_file) ->
_assert_subject = filepath:expand(
filepath:join(Module, Main_file)
),
{ok, Main} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"lustre_dev_tools/esbuild"/utf8>>,
function => <<"get_module_main"/utf8>>,
line => 132})
end,
{ok, Main}
end
)
end
)
end.
-spec check_esbuild_exists(binary()) -> boolean().
check_esbuild_exists(Path) ->
case simplifile_erl:is_file(Path) of
{ok, true} ->
true;
{ok, false} ->
false;
{error, _} ->
false
end.
-spec get_download_url_and_hash(binary(), binary()) -> {ok,
{binary(), binary()}} |
{error, lustre_dev_tools@error:error()}.
get_download_url_and_hash(Os, Cpu) ->
Base = <<"https://registry.npmjs.org/@esbuild/"/utf8>>,
case {Os, Cpu} of
{<<"android"/utf8>>, <<"arm"/utf8>>} ->
{ok,
{<>,
<<"545CF157B0E42E407AC1412F73876119414314D9E31982EBD1E9073336DA5365"/utf8>>}};
{<<"android"/utf8>>, <<"arm64"/utf8>>} ->
{ok,
{<>,
<<"DFB0A873B1BB9698EF42561B9513FC4A7D8392CE84FBD44FC276883B82AB087E"/utf8>>}};
{<<"android"/utf8>>, <<"x64"/utf8>>} ->
{ok,
{<>,
<<"B5D9D170F469BE483F3E16DA6033DFD064ED3DF788C6DC238BA6FE3232BF5653"/utf8>>}};
{<<"darwin"/utf8>>, <<"aarch64"/utf8>>} ->
{ok,
{<>,
<<"8CEC451CBD47E228D2D0B740B62870471C22813348398061C06D829BC6610EA9"/utf8>>}};
{<<"darwin"/utf8>>, <<"amd64"/utf8>>} ->
{ok,
{<>,
<<"8CEC451CBD47E228D2D0B740B62870471C22813348398061C06D829BC6610EA9"/utf8>>}};
{<<"darwin"/utf8>>, <<"arm64"/utf8>>} ->
{ok,
{<>,
<<"8CEC451CBD47E228D2D0B740B62870471C22813348398061C06D829BC6610EA9"/utf8>>}};
{<<"darwin"/utf8>>, <<"x86_64"/utf8>>} ->
{ok,
{<>,
<<"4AEC252E72CD56FD31341D960D8DCF0BEBFB858587061FE36B9047FD591C68A3"/utf8>>}};
{<<"freebsd"/utf8>>, <<"aarch64"/utf8>>} ->
{ok,
{<>,
<<"739E9F6DD3121DB0CD03B70B14C4D17A1854970272B0F988BD035AA876BE254B"/utf8>>}};
{<<"freebsd"/utf8>>, <<"amd64"/utf8>>} ->
{ok,
{<>,
<<"3E937C849B21B89244A8A62B473E36EEFE793F5BDF602BEDBB314DD33DDBE7EE"/utf8>>}};
{<<"linux"/utf8>>, <<"aarch64"/utf8>>} ->
{ok,
{<>,
<<"D3523B8F7B2540BA5A15C4EE4C747B31DFDC496C7A8A3F3FB0ECCB3008647DB7"/utf8>>}};
{<<"linux"/utf8>>, <<"arm"/utf8>>} ->
{ok,
{<>,
<<"99EEB37F5C1AB8750D9CAB6AB04469EF5CA32847B25E1461215276920AFB01B2"/utf8>>}};
{<<"linux"/utf8>>, <<"arm64"/utf8>>} ->
{ok,
{<>,
<<"D3523B8F7B2540BA5A15C4EE4C747B31DFDC496C7A8A3F3FB0ECCB3008647DB7"/utf8>>}};
{<<"linux"/utf8>>, <<"ia32"/utf8>>} ->
{ok,
{<>,
<<"3D69F7B90C62E6D94140355A92EDB15B8BFB934096C6E518BE41DAD6249BF38E"/utf8>>}};
{<<"linux"/utf8>>, <<"x64"/utf8>>} ->
{ok,
{<>,
<<"73CA82A3C9049315027E60A50AF53C2ABFDE678BF66562B407FACA7FD3FAD6F4"/utf8>>}};
{<<"linux"/utf8>>, <<"x86_64"/utf8>>} ->
{ok,
{<>,
<<"73CA82A3C9049315027E60A50AF53C2ABFDE678BF66562B407FACA7FD3FAD6F4"/utf8>>}};
{<<"win32"/utf8>>, <<"arm64"/utf8>>} ->
{ok,
{<>,
<<"2D0EC6ED7C5BA6F2D99CBB1428C1FAABFA7D42E7435BC40474C5787DCD1FF37C"/utf8>>}};
{<<"win32"/utf8>>, <<"ia32"/utf8>>} ->
{ok,
{<>,
<<"5BFBF08A8EDC16D53FE2103C68705DC3B4ABDFA6C44919B9602495ABA523BA46"/utf8>>}};
{<<"win32"/utf8>>, <<"x64"/utf8>>} ->
{ok,
{<>,
<<"03EFF9A74ED7C72C8E4ACE85F6BFD2D097169D8D6E7D691AE1D7959F2912B785"/utf8>>}};
{<<"win32"/utf8>>, <<"x86_64"/utf8>>} ->
{ok,
{<>,
<<"03EFF9A74ED7C72C8E4ACE85F6BFD2D097169D8D6E7D691AE1D7959F2912B785"/utf8>>}};
{<<"netbsd"/utf8>>, <<"x64"/utf8>>} ->
{ok,
{<>,
<<"C8F6E2CB79B1DDC2AD42C0AE25FB2A769A989E36B917B231CF9847B683D6DD8D"/utf8>>}};
{<<"openbsd"/utf8>>, <<"x64"/utf8>>} ->
{ok,
{<>,
<<"AFEBEAD35BB5A1B921C126E70E0D76CF04DB64FA53C60E0779816CFA9E1F9A11"/utf8>>}};
{<<"sunos"/utf8>>, <<"x64"/utf8>>} ->
{ok,
{<>,
<<"B1E9F969433574BD43A293FA3A3C71C88B8C4CF841957DAAA2CF83A90ADAAB7E"/utf8>>}};
{_, _} ->
{error, {unknown_platform, <<"esbuild"/utf8>>, Os, Cpu}}
end.
-spec check_esbuild_integrity(bitstring(), binary()) -> {ok, nil} |
{error, lustre_dev_tools@error:error()}.
check_esbuild_integrity(Bin, Expected_hash) ->
Hash = gleam_crypto_ffi:hash(sha256, Bin),
Hash_string = binary:encode_hex(Hash),
case Hash_string =:= Expected_hash of
true ->
{ok, nil};
false ->
{error, invalid_esbuild_binary}
end.
-spec write_esbuild(bitstring(), binary(), binary()) -> {ok, nil} |
{error, lustre_dev_tools@error:error()}.
write_esbuild(Bin, Outdir, Outfile) ->
_ = simplifile:create_directory_all(Outdir),
_pipe = simplifile_erl:write_bits(Outfile, Bin),
gleam@result:map_error(
_pipe,
fun(_capture) ->
{cannot_write_file, _capture, filepath:join(Outdir, Outfile)}
end
).
-spec set_file_permissions(binary()) -> {ok, nil} |
{error, lustre_dev_tools@error:error()}.
set_file_permissions(File) ->
Permissions = {file_permissions,
gleam@set:from_list([read, write, execute]),
gleam@set:from_list([read, execute]),
gleam@set:from_list([read, execute])},
_pipe = simplifile:set_permissions(File, Permissions),
gleam@result:map_error(
_pipe,
fun(_capture) -> {cannot_set_permissions, _capture, File} end
).
-spec exec_esbuild(binary(), list(binary())) -> {ok, binary()} |
{error, lustre_dev_tools@error:error()}.
exec_esbuild(Root, Options) ->
_pipe = lustre_dev_tools_ffi:exec(
<<"./build/.lustre/bin/esbuild"/utf8>>,
Options,
Root
),
gleam@result:map_error(
_pipe,
fun(Pair) -> {bundle_error, erlang:element(2, Pair)} end
).
-spec get_esbuild(binary()) -> {ok, bitstring()} |
{error, lustre_dev_tools@error:error()}.
get_esbuild(Url) ->
_pipe = lustre_dev_tools_ffi:get_esbuild(Url),
gleam@result:map_error(_pipe, fun(Field@0) -> {network_error, Field@0} end).
-spec unzip_esbuild(bitstring()) -> {ok, bitstring()} |
{error, lustre_dev_tools@error:error()}.
unzip_esbuild(Gzip) ->
_pipe = lustre_dev_tools_ffi:unzip_esbuild(Gzip),
gleam@result:map_error(_pipe, fun(Field@0) -> {unzip_error, Field@0} end).
-spec download(binary(), binary()) -> lustre_dev_tools@cli:cli(nil).
download(Os, Cpu) ->
lustre_dev_tools@cli:log(
<<"Downloading esbuild"/utf8>>,
fun() ->
Outdir = filepath:join(
lustre_dev_tools@project:root(),
<<"build/.lustre/bin"/utf8>>
),
Outfile = filepath:join(Outdir, <<"esbuild"/utf8>>),
case check_esbuild_exists(Outfile) of
true ->
lustre_dev_tools@cli:success(
<<"Esbuild already installed!"/utf8>>,
fun() -> lustre_dev_tools@cli:return(nil) end
);
false ->
lustre_dev_tools@cli:log(
<<"Detecting platform"/utf8>>,
fun() ->
lustre_dev_tools@cli:'try'(
get_download_url_and_hash(Os, Cpu),
fun(_use0) ->
{Url, Hash} = _use0,
Max_url_size = (lustre_dev_tools@utils:term_width(
)
- 17)
- 2,
Shortened_url = lustre_dev_tools@utils:shorten_url(
Url,
Max_url_size
),
lustre_dev_tools@cli:log(
<<"Downloading from "/utf8,
Shortened_url/binary>>,
fun() ->
lustre_dev_tools@cli:'try'(
get_esbuild(Url),
fun(Tarball) ->
lustre_dev_tools@cli:log(
<<"Checking the downloaded tarball"/utf8>>,
fun() ->
lustre_dev_tools@cli:'try'(
check_esbuild_integrity(
Tarball,
Hash
),
fun(_) ->
lustre_dev_tools@cli:log(
<<"Unzipping esbuild"/utf8>>,
fun() ->
lustre_dev_tools@cli:'try'(
unzip_esbuild(
Tarball
),
fun(
Bin
) ->
lustre_dev_tools@cli:'try'(
write_esbuild(
Bin,
Outdir,
Outfile
),
fun(
_
) ->
lustre_dev_tools@cli:'try'(
set_file_permissions(
Outfile
),
fun(
_
) ->
lustre_dev_tools@cli:success(
<<"Esbuild installed!"/utf8>>,
fun(
) ->
lustre_dev_tools@cli:return(
nil
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
end
).
-spec bundle(binary(), binary(), boolean()) -> lustre_dev_tools@cli:cli(nil).
bundle(Input_file, Output_file, Is_prod) ->
lustre_dev_tools@cli:do(
download(lustre_dev_tools_ffi:get_os(), lustre_dev_tools_ffi:get_cpu()),
fun(_) ->
Root = lustre_dev_tools@project:root(),
Flags = [<<"--bundle"/utf8>>,
<<"--external:node:*"/utf8>>,
<<"--format=esm"/utf8>>,
<<"--outfile="/utf8, Output_file/binary>>],
Options = case Is_prod of
true ->
[Input_file, <<"--minify"/utf8>> | Flags];
false ->
[Input_file | Flags]
end,
lustre_dev_tools@cli:log(
<<"Bundling with esbuild"/utf8>>,
fun() ->
lustre_dev_tools@cli:do(
lustre_dev_tools@esbuild@preprocess:copy_deep_ffi(),
fun(_) ->
lustre_dev_tools@cli:'try'(
exec_esbuild(Root, Options),
fun(_) ->
lustre_dev_tools@cli:success(
<<<<"Bundle produced at `"/utf8,
Output_file/binary>>/binary,
"`"/utf8>>,
fun() ->
lustre_dev_tools@cli:return(nil)
end
)
end
)
end
)
end
)
end
).
-spec bundle_node_modules(list(binary()), binary()) -> lustre_dev_tools@cli:cli(nil).
bundle_node_modules(Modules, Build_dir) ->
lustre_dev_tools@cli:log(
<<"Bundling all used node modules"/utf8>>,
fun() ->
lustre_dev_tools@cli:do(
download(
lustre_dev_tools_ffi:get_os(),
lustre_dev_tools_ffi:get_cpu()
),
fun(_) ->
Dir = filepath:join(Build_dir, <<"modules"/utf8>>),
lustre_dev_tools@cli:'try'(
(gleam@list:try_each(
Modules,
fun(Module_name) ->
gleam@result:'try'(
get_module_main(Module_name),
fun(Module_main) ->
Output_file = filepath:join(
Dir,
<>
),
Options = [Module_main,
<<"--bundle"/utf8>>,
<<"--external:node:*"/utf8>>,
<<"--format=esm"/utf8>>,
<<"--tree-shaking=false"/utf8>>,
<<"--outfile="/utf8,
Output_file/binary>>],
exec_esbuild(
lustre_dev_tools@project:root(),
Options
)
end
)
end
)),
fun(_) ->
lustre_dev_tools@cli:success(
<<<<"Bundled all node modules at `"/utf8,
Dir/binary>>/binary,
"`"/utf8>>,
fun() -> lustre_dev_tools@cli:return(nil) end
)
end
)
end
)
end
).