-module(lustre@cli@dev). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([run/0]). -export_type([error/0]). -type error() :: build_error | {bundle_error, lustre@cli@esbuild:error()} | {main_missing, binary()} | {main_incorrect_type, binary(), lustre@cli@project:type()} | {main_bad_app_type, binary(), lustre@cli@project:type()} | {module_missing, binary()}. -spec explain(error()) -> nil. explain(Error) -> case Error of build_error -> lustre@cli@project:explain(build_error); {bundle_error, Error@1} -> lustre@cli@esbuild:explain(Error@1); {main_missing, Module} -> gleam@io:println( <<<<" Module `"/utf8, Module/binary>>/binary, "` doesn't have a public `main` function I can preview."/utf8>> ); {main_incorrect_type, Module@1, Type_} -> gleam@io:println( <<<<<<<<" I cannot preview the `main` function exposed by module `"/utf8, Module@1/binary>>/binary, "`. To start a preview server I need it to take no arguments and return a Lustre `App`. The one I found has type `"/utf8>>/binary, (lustre@cli@project:type_to_string(Type_))/binary>>/binary, "`."/utf8>> ); {main_bad_app_type, Module@2, Type_@1} -> gleam@io:println( <<<<<<<<" I cannot preview the `main` function exposed by module `"/utf8, Module@2/binary>>/binary, "`. To start a preview server I need it to return a Lustre `App` that doesn't need any flags. The one I found has type `"/utf8>>/binary, (lustre@cli@project:type_to_string(Type_@1))/binary>>/binary, "`. Its return type should look something like this: import lustre.{type App} pub fn main() -> App(flags, model, msg) { todo as \"your Lustre application to preview\" }"/utf8>> ); {module_missing, Module@3} -> gleam@io:println( <<<<" I couldn't find a public module called `"/utf8, Module@3/binary>>/binary, "` in your project."/utf8>> ) end. -spec index_html(binary(), binary(), boolean()) -> binary(). index_html(App_name, Container_id, Include_styles) -> Styles = case Include_styles of true -> lustre@element@html:link( [lustre@attribute:rel(<<"stylesheet"/utf8>>), lustre@attribute:href( <<"https://cdn.jsdelivr.net/gh/lustre-labs/ui/priv/styles.css"/utf8>> )] ); false -> lustre@element:none() end, _pipe = lustre@element@html:html( [], [lustre@element@html:head( [], [lustre@element@html:meta( [lustre@attribute:attribute( <<"charset"/utf8>>, <<"utf-8"/utf8>> )] ), lustre@element@html:meta( [lustre@attribute:attribute( <<"name"/utf8>>, <<"viewport"/utf8>> ), lustre@attribute:attribute( <<"content"/utf8>>, <<"width=device-width, initial-scale=1"/utf8>> )] ), lustre@element@html:title([], App_name), lustre@element@html:script( [lustre@attribute:type_(<<"module"/utf8>>), lustre@attribute:src(<<"./index.mjs"/utf8>>)], <<""/utf8>> ), Styles] ), lustre@element@html:body( [], [lustre@element@html:'div'( [lustre@attribute:id(Container_id)], [] )] )] ), _pipe@1 = lustre@element:to_string(_pipe), gleam@string:append(<<""/utf8>>, _pipe@1). -spec is_nil_type(lustre@cli@project:type()) -> boolean(). is_nil_type(T) -> case T of {named, <<"Nil"/utf8>>, <<""/utf8>>, <<"gleam"/utf8>>, []} -> true; _ -> false end. -spec is_type_variable(lustre@cli@project:type()) -> boolean(). is_type_variable(T) -> case T of {variable, _} -> true; _ -> false end. -spec is_compatible_flags_type(lustre@cli@project:type()) -> boolean(). is_compatible_flags_type(T) -> is_nil_type(T) orelse is_type_variable(T). -spec check_is_lustre_app(binary(), lustre@cli@project:module_()) -> {ok, boolean()} | {error, error()}. check_is_lustre_app(Module_path, Module) -> lustre@cli@utils:'try'( gleam@dict:get(erlang:element(3, Module), <<"main"/utf8>>), lustre@cli@utils:replace({main_missing, Module_path}), fun(Main) -> lustre@cli@utils:guard( erlang:element(2, Main) /= [], {main_incorrect_type, Module_path, {fn, erlang:element(2, Main), erlang:element(3, Main)}}, fun() -> case erlang:element(3, Main) of {named, <<"App"/utf8>>, <<"lustre"/utf8>>, <<"lustre"/utf8>>, [Flags | _]} -> case is_compatible_flags_type(Flags) of true -> {ok, true}; false -> {error, {main_bad_app_type, Module_path, erlang:element(3, Main)}} end; _ -> {ok, false} end end ) end ). -spec run() -> glint:command(nil). run() -> Description = <<" "/utf8>>, _pipe@2 = glint:command( fun(Input) -> {command_input, _, Flags, _} = Input, _assert_subject = glint@flag:get_string(Flags, <<"host"/utf8>>), {ok, Host} = 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/cli/dev"/utf8>>, function => <<"run"/utf8>>, line => 27}) end, _assert_subject@1 = glint@flag:get_string(Flags, <<"port"/utf8>>), {ok, Port} = case _assert_subject@1 of {ok, _} -> _assert_subject@1; _assert_fail@1 -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@1, module => <<"lustre/cli/dev"/utf8>>, function => <<"run"/utf8>>, line => 28}) end, _assert_subject@2 = glint@flag:get_bool( Flags, <<"include-styles"/utf8>> ), {ok, Include_styles} = case _assert_subject@2 of {ok, _} -> _assert_subject@2; _assert_fail@2 -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@2, module => <<"lustre/cli/dev"/utf8>>, function => <<"run"/utf8>>, line => 29}) end, Script = (lustre@cli@step:new( <<"Building your project"/utf8>>, fun() -> lustre@cli@step:'try'( lustre@cli@project:interface(), lustre@cli@utils:replace(build_error), fun(Interface) -> lustre@cli@step:'try'( gleam@dict:get( erlang:element(4, Interface), erlang:element(2, Interface) ), lustre@cli@utils:replace( {module_missing, erlang:element(2, Interface)} ), fun(Module) -> lustre@cli@step:'try'( check_is_lustre_app( erlang:element(2, Interface), Module ), fun gleam@function:identity/1, fun(Is_app) -> lustre@cli@step:done( <<"✅ Project compiled successfully"/utf8>>, fun() -> lustre@cli@step:new( <<"Creating the application entry point"/utf8>>, fun() -> Root = lustre@cli@project:root( ), Tempdir = filepath:join( Root, <<"build/.lustre"/utf8>> ), _ = simplifile:create_directory_all( Tempdir ), Entry = begin _pipe = case Is_app of true -> <<" import { start } from '../dev/javascript/lustre/lustre.mjs'; import { main } from '../dev/javascript/${app_name}/${app_name}.mjs'; start(main(), ${container_id}); "/utf8>>; false -> <<" import { main } from '../dev/javascript/${app_name}/${app_name}.mjs'; main(); "/utf8>> end, _pipe@1 = gleam@string:replace( _pipe, <<"${app_name}"/utf8>>, erlang:element( 2, Interface ) ), gleam@string:replace( _pipe@1, <<"${container_id}"/utf8>>, <<"app"/utf8>> ) end, Html = index_html( erlang:element( 2, Interface ), <<"app"/utf8>>, Include_styles ), _assert_subject@3 = simplifile:write( <>, Entry ), {ok, _} = case _assert_subject@3 of {ok, _} -> _assert_subject@3; _assert_fail@3 -> erlang:error( #{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@3, module => <<"lustre/cli/dev"/utf8>>, function => <<"run"/utf8>>, line => 65} ) end, _assert_subject@4 = simplifile:write( <>, Html ), {ok, _} = case _assert_subject@4 of {ok, _} -> _assert_subject@4; _assert_fail@4 -> erlang:error( #{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@4, module => <<"lustre/cli/dev"/utf8>>, function => <<"run"/utf8>>, line => 66} ) end, lustre@cli@step:run( lustre@cli@esbuild:bundle( filepath:join( Tempdir, <<"entry.mjs"/utf8>> ), filepath:join( Tempdir, <<"index.mjs"/utf8>> ), false ), lustre@cli@utils:map( fun(Field@0) -> {bundle_error, Field@0} end ), fun(_) -> lustre@cli@step:run( lustre@cli@esbuild:serve( Host, Port ), lustre@cli@utils:map( fun(Field@0) -> {bundle_error, Field@0} end ), fun(_) -> lustre@cli@step:return( nil ) end ) end ) end ) end ) end ) end ) end ) end )), case lustre@cli@step:execute(Script) of {ok, _} -> nil; {error, Error} -> explain(Error) end end ), _pipe@3 = glint:description(_pipe@2, Description), _pipe@4 = glint:unnamed_args(_pipe@3, {eq_args, 0}), _pipe@7 = glint:flag( _pipe@4, <<"host"/utf8>>, begin Description@1 = <<""/utf8>>, Default = <<"0.0.0.0"/utf8>>, _pipe@5 = glint@flag:string(), _pipe@6 = glint@flag:default(_pipe@5, Default), glint@flag:description(_pipe@6, Description@1) end ), _pipe@10 = glint:flag( _pipe@7, <<"port"/utf8>>, begin Description@2 = <<""/utf8>>, Default@1 = <<"1234"/utf8>>, _pipe@8 = glint@flag:string(), _pipe@9 = glint@flag:default(_pipe@8, Default@1), glint@flag:description(_pipe@9, Description@2) end ), glint:flag( _pipe@10, <<"include-styles"/utf8>>, begin Description@3 = <<""/utf8>>, Default@2 = false, _pipe@11 = glint@flag:bool(), _pipe@12 = glint@flag:default(_pipe@11, Default@2), glint@flag:description(_pipe@12, Description@3) end ).