Lustre's CLI and development tooling: zero-config dev server, bundling, and scaffolding.
at main 3.0 kB view raw
1// IMPORTS --------------------------------------------------------------------- 2 3import filepath 4import gleam/bool 5import gleam/erlang/process 6import gleam/http/request.{type Request, Request} 7import gleam/http/response.{type Response} 8import gleam/io 9import gleam/option.{None, Some} 10import gleam/regex 11import gleam/result 12import gleam/string_builder 13import lustre_dev_tools/cli.{type Cli, do, try} 14import lustre_dev_tools/cmd 15import lustre_dev_tools/error.{type Error, CannotStartDevServer} 16import lustre_dev_tools/project 17import lustre_dev_tools/server/live_reload 18import lustre_dev_tools/server/proxy 19import mist 20import simplifile 21import wisp 22 23pub fn start(port: Int) -> Cli(Nil) { 24 let assert Ok(cwd) = cmd.cwd() 25 let assert Ok(root) = filepath.expand(filepath.join(cwd, project.root())) 26 27 use proxy <- do(proxy.get()) 28 29 case proxy { 30 Some(_) -> 31 io.println( 32 " 33[WARNING] Support for proxying requests to another server is currently still 34**experimental**. It's functionality or api may change is breaking ways even 35between minor versions. If you run into any problems please open an issue over 36at https://github.com/lustre-labs/dev-tools/issues/new 37 ", 38 ) 39 None -> Nil 40 } 41 use flags <- do(cli.get_flags()) 42 43 use make_socket <- try(live_reload.start(root, flags)) 44 use _ <- try( 45 fn(req: Request(mist.Connection)) -> Response(mist.ResponseData) { 46 use <- proxy.middleware(req, proxy) 47 48 case request.path_segments(req) { 49 // We're going to inject a script that connects to /lustre-dev-tools over 50 // websockets. Whenever we detect a file change we can broadcast a reload 51 // message and get the client to hard refresh the page. 52 ["lustre-dev-tools"] -> make_socket(req) 53 [] -> 54 Request(..req, path: "/index.html") 55 |> wisp.mist_handler(handler(_, root), "") 56 57 // For everything else we're just going to serve any static files directly 58 // from the project's root. 59 _ -> wisp.mist_handler(handler(_, root), "")(req) 60 } 61 } 62 |> mist.new 63 |> mist.port(port) 64 |> mist.start_http 65 |> result.map_error(CannotStartDevServer), 66 ) 67 68 cli.return(process.sleep_forever()) 69} 70 71fn handler(req: wisp.Request, root: String) -> wisp.Response { 72 use <- inject_live_reload(req, root) 73 use <- wisp.serve_static(req, under: "/", from: root) 74 75 handler(Request(..req, path: "/index.html"), root) 76} 77 78fn inject_live_reload( 79 req: wisp.Request, 80 root: String, 81 k: fn() -> wisp.Response, 82) -> wisp.Response { 83 let assert Ok(is_interesting) = regex.from_string(".*\\.html$") 84 use <- bool.lazy_guard(!regex.check(is_interesting, req.path), k) 85 let path = filepath.join(root, req.path) 86 87 case simplifile.is_file(path) { 88 Ok(False) | Error(_) -> k() 89 Ok(True) -> { 90 let assert Ok(html) = simplifile.read(path) 91 92 html 93 |> live_reload.inject 94 |> string_builder.from_string 95 |> wisp.html_response(200) 96 } 97 } 98}