aboutsummaryrefslogtreecommitdiff
path: root/src/lustre/cli/dev.gleam
diff options
context:
space:
mode:
Diffstat (limited to 'src/lustre/cli/dev.gleam')
-rw-r--r--src/lustre/cli/dev.gleam245
1 files changed, 0 insertions, 245 deletions
diff --git a/src/lustre/cli/dev.gleam b/src/lustre/cli/dev.gleam
deleted file mode 100644
index f7e12c2..0000000
--- a/src/lustre/cli/dev.gleam
+++ /dev/null
@@ -1,245 +0,0 @@
-// IMPORTS ---------------------------------------------------------------------
-
-import filepath
-import gleam/dict
-import gleam/io
-import gleam/package_interface.{type Type, Fn, Named, Variable}
-import gleam/result
-import gleam/string
-import glint.{type Command, CommandInput}
-import glint/flag
-import lustre/cli/esbuild
-import lustre/cli/project.{type Module}
-import lustre/cli/step
-import lustre/cli/utils.{guard, keep, map, replace, template, try}
-import simplifile
-
-// COMMANDS --------------------------------------------------------------------
-
-pub fn run() -> Command(Nil) {
- let description =
- "
- "
-
- glint.command(fn(input) {
- let CommandInput(flags: flags, ..) = input
- let assert Ok(host) = flag.get_string(flags, "host")
- let assert Ok(port) = flag.get_string(flags, "port")
- let assert Ok(use_lustre_ui) = flag.get_bool(flags, "use-lustre-ui")
- let assert Ok(spa) = flag.get_bool(flags, "spa")
- let custom_html = flag.get_string(flags, "html")
-
- let script = {
- use <- step.new("Building your project")
- use interface <- step.try(project.interface(), replace(BuildError))
- use module <- step.try(
- dict.get(interface.modules, interface.name),
- replace(ModuleMissing(interface.name)),
- )
- use is_app <- step.try(check_is_lustre_app(interface.name, module), keep)
- use <- step.done("✅ Project compiled successfully")
-
- use <- step.new("Creating the application entry point")
- let root = project.root()
- let tempdir = filepath.join(root, "build/.lustre")
- let _ = simplifile.create_directory_all(tempdir)
-
- let entry =
- template(case is_app {
- True -> "entry-with-start.mjs"
- False -> "entry-with-main.mjs"
- })
- |> string.replace("{app_name}", interface.name)
-
- use html <- step.try(
- case custom_html {
- Ok(custom_html_path) ->
- custom_html_path
- |> simplifile.read
- |> result.map_error(CouldntOpenCustomHtml(_, custom_html_path))
- |> result.map(string.replace(
- _,
- "<script type=\"application/lustre\">",
- "<script type=\"module\" src=\"./index.mjs\">",
- ))
-
- Error(_) if use_lustre_ui ->
- template("index-with-lustre-ui.html")
- |> string.replace("{app_name}", interface.name)
- |> Ok
-
- _ ->
- template("index.html")
- |> string.replace("{app_name}", interface.name)
- |> Ok
- },
- keep,
- )
-
- let assert Ok(_) = simplifile.write(tempdir <> "/entry.mjs", entry)
- let assert Ok(_) = simplifile.write(tempdir <> "/index.html", html)
-
- use _ <- step.run(
- esbuild.bundle(
- filepath.join(tempdir, "entry.mjs"),
- filepath.join(tempdir, "index.mjs"),
- False,
- ),
- map(BundleError),
- )
- use _ <- step.run(esbuild.serve(host, port, spa), map(BundleError))
- step.return(Nil)
- }
-
- case step.execute(script) {
- Ok(_) -> Nil
- Error(error) -> explain(error)
- }
- })
- |> glint.description(description)
- |> glint.unnamed_args(glint.EqArgs(0))
- |> glint.flag("host", {
- let description = ""
- let default = "localhost"
-
- flag.string()
- |> flag.default(default)
- |> flag.description(description)
- })
- |> glint.flag("port", {
- let description = ""
- let default = "1234"
-
- flag.string()
- |> flag.default(default)
- |> flag.description(description)
- })
- |> glint.flag("use-lustre-ui", {
- let description = "Inject lustre/ui's stylesheet. Ignored if --html is set."
- let default = False
-
- flag.bool()
- |> flag.default(default)
- |> flag.description(description)
- })
- |> glint.flag("spa", {
- let description =
- "Serve your app on any route. Useful for apps that do client-side routing."
- let default = False
-
- flag.bool()
- |> flag.default(default)
- |> flag.description(description)
- })
- |> glint.flag("html", {
- let description =
- "Supply a custom HTML file to use as the entry point.
-To inject the Lustre bundle, make sure it includes the following empty script:
-<script type=\"application/lustre\"></script>
- "
- |> string.trim_right
-
- flag.string()
- |> flag.description(description)
- })
-}
-
-// ERROR HANDLING --------------------------------------------------------------
-
-type Error {
- BuildError
- BundleError(esbuild.Error)
- CouldntOpenCustomHtml(error: simplifile.FileError, path: String)
- MainMissing(module: String)
- MainIncorrectType(module: String, got: Type)
- MainBadAppType(module: String, got: Type)
- ModuleMissing(module: String)
-}
-
-fn explain(error: Error) -> Nil {
- case error {
- BuildError -> project.explain(project.BuildError)
-
- BundleError(error) -> esbuild.explain(error)
-
- CouldntOpenCustomHtml(_, path) -> io.println("
-I couldn't open the custom HTML file at `" <> path <> "`.")
-
- MainMissing(module) -> io.println("
-Module `" <> module <> "` doesn't have a public `main` function I can preview.")
-
- MainIncorrectType(module, type_) -> io.println("
-I cannot preview the `main` function exposed by module `" <> module <> "`.
-To start a preview server I need it to take no arguments and return a Lustre
-`App`.
-The one I found has type `" <> project.type_to_string(type_) <> "`.")
-
- // TODO: maybe this could have useful links to `App`/flags...
- MainBadAppType(module, type_) -> io.println("
-I cannot preview the `main` function exposed by module `" <> module <> "`.
-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 `" <> project.type_to_string(type_) <> "`.
-
-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\"
- }")
-
- ModuleMissing(module) -> io.println("
-I couldn't find a public module called `" <> module <> "` in your project.")
- }
-}
-
-// STEPS -----------------------------------------------------------------------
-
-fn check_is_lustre_app(
- module_path: String,
- module: Module,
-) -> Result(Bool, Error) {
- use main <- try(
- dict.get(module.functions, "main"),
- replace(MainMissing(module_path)),
- )
- use <- guard(
- main.parameters != [],
- MainIncorrectType(module_path, Fn(main.parameters, main.return)),
- )
-
- case main.return {
- Named(
- name: "App",
- package: "lustre",
- module: "lustre",
- parameters: [flags, ..],
- ) ->
- case is_compatible_flags_type(flags) {
- True -> Ok(True)
- False -> Error(MainBadAppType(module_path, main.return))
- }
-
- _ -> Ok(False)
- }
-}
-
-// UTILS -----------------------------------------------------------------------
-
-fn is_nil_type(t: Type) -> Bool {
- case t {
- Named(name: "Nil", package: "", module: "gleam", parameters: []) -> True
- _ -> False
- }
-}
-
-fn is_type_variable(t: Type) -> Bool {
- case t {
- Variable(..) -> True
- _ -> False
- }
-}
-
-fn is_compatible_flags_type(t: Type) -> Bool {
- is_nil_type(t) || is_type_variable(t)
-}