diff --git a/.formatter.exs b/.formatter.exs index 376fc86..8ab6168 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -1,5 +1,5 @@ # Used by "mix format" [ inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"], - import_deps: [:typed_struct] + import_deps: [:typed_struct, :plug] ] diff --git a/lib/mix/tasks/site.dev.ex b/lib/mix/tasks/site.dev.ex new file mode 100644 index 0000000..47f4c14 --- /dev/null +++ b/lib/mix/tasks/site.dev.ex @@ -0,0 +1,35 @@ +defmodule Mix.Tasks.Site.Dev do + @moduledoc "Build the site, watch for changes, and serve the built site" + @shortdoc "run site.watch and site.serve" + use Mix.Task + + @impl Mix.Task + def run(_args) do + {:ok, watch_pid} = + Task.start_link(fn -> + Mix.Task.run("site.watch") + end) + + {:ok, serve_pid} = + Task.start_link(fn -> + Mix.Task.run("site.serve") + end) + + unless iex_running?() do + watch_ref = Process.monitor(watch_pid) + serve_ref = Process.monitor(serve_pid) + + receive do + {:DOWN, ^watch_ref, _, _, _} -> + :ok + + {:DOWN, ^serve_ref, _, _, _} -> + :ok + end + end + end + + defp iex_running? do + Code.ensure_loaded?(IEx) and IEx.started?() + end +end diff --git a/lib/mix/tasks/site.serve.ex b/lib/mix/tasks/site.serve.ex new file mode 100644 index 0000000..cbebd5b --- /dev/null +++ b/lib/mix/tasks/site.serve.ex @@ -0,0 +1,35 @@ +defmodule Mix.Tasks.Site.Serve do + @moduledoc "Serve the built site. Only for use in development" + @shortdoc "serve the built site" + use Mix.Task + require Logger + + @impl Mix.Task + def run(_args) do + Logger.info("Starting development server...") + + {:ok, _} = + Application.ensure_all_started([ + :telemetry, + :plug, + :thousand_island, + :bandit + ]) + + {:ok, pid} = SloaneSH.serve() + + unless iex_running?() do + ref = Process.monitor(pid) + + receive do + {:DOWN, ^ref, _, _, _} -> + Logger.info("Development server terminated") + :ok + end + end + end + + defp iex_running? do + Code.ensure_loaded?(IEx) and IEx.started?() + end +end diff --git a/lib/sloane_sh.ex b/lib/sloane_sh.ex index 7df2d54..8b4582f 100644 --- a/lib/sloane_sh.ex +++ b/lib/sloane_sh.ex @@ -4,8 +4,9 @@ defmodule SloaneSH do """ alias SloaneSH.Build - alias SloaneSH.Watch alias SloaneSH.Context + alias SloaneSH.Serve + alias SloaneSH.Watch def build(_opts \\ []) do context() @@ -19,6 +20,11 @@ defmodule SloaneSH do |> Watch.start_link() end + def serve do + context() + |> Serve.start_link() + end + def context do Context.new() |> Context.init() diff --git a/lib/sloane_sh/serve.ex b/lib/sloane_sh/serve.ex new file mode 100644 index 0000000..bd4bc7e --- /dev/null +++ b/lib/sloane_sh/serve.ex @@ -0,0 +1,21 @@ +defmodule SloaneSH.Serve do + @moduledoc """ + Task to use `Bandit` to start `SloaneSH.Serve.Plug` + """ + use Supervisor + + alias __MODULE__ + + def start_link(ctx) do + Supervisor.start_link(__MODULE__, ctx, name: __MODULE__) + end + + @impl Supervisor + def init(_ctx) do + children = [ + {Bandit, plug: Serve.Plug} + ] + + Supervisor.init(children, strategy: :one_for_one) + end +end diff --git a/lib/sloane_sh/serve/plug.ex b/lib/sloane_sh/serve/plug.ex new file mode 100644 index 0000000..2c6299b --- /dev/null +++ b/lib/sloane_sh/serve/plug.ex @@ -0,0 +1,29 @@ +defmodule SloaneSH.Serve.Plug do + @moduledoc """ + Basic HTTP server for testing and local development. + + Inspired by https://github.com/mbuhot/plug_static_index_html + """ + use Plug.Builder + + plug Plug.Logger + plug :rewrite_for_index_html + plug Plug.Static, at: "/", from: {:sloane_sh, "priv/output"} + plug :not_found + + def rewrite_for_index_html(conn, _) do + if String.match?(conn.request_path, ~r[.+\..+$]) do + conn + else + %{ + conn + | request_path: Path.join(conn.request_path, "index.html"), + path_info: conn.path_info ++ ["index.html"] + } + end + end + + def not_found(conn, _) do + send_resp(conn, 404, "Not found") + end +end diff --git a/lib/sloane_sh/watch.ex b/lib/sloane_sh/watch.ex index 7068ed1..93e7011 100644 --- a/lib/sloane_sh/watch.ex +++ b/lib/sloane_sh/watch.ex @@ -19,11 +19,10 @@ defmodule SloaneSH.Watch do def init(%Context{} = ctx) do {:ok, watcher_pid} = FileSystem.start_link( - dirs: - dbg([ - ctx.config.pages_dir, - ctx.config.posts_dir - ]) + dirs: [ + ctx.config.pages_dir, + ctx.config.posts_dir + ] ) FileSystem.subscribe(watcher_pid) diff --git a/mix.exs b/mix.exs index dfdc4c7..191ad5f 100644 --- a/mix.exs +++ b/mix.exs @@ -24,7 +24,9 @@ defmodule SloaneSH.MixProject do {:file_system, "~> 1.0.0"}, {:typed_struct, "~> 0.3.0"}, {:earmark, "~> 1.4"}, - {:earmark_parser, "~> 1.4"} + {:earmark_parser, "~> 1.4"}, + {:plug, "~> 1.15"}, + {:bandit, "~> 1.2"} ] end end diff --git a/mix.lock b/mix.lock index d9f79b7..11f5828 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,14 @@ %{ + "bandit": {:hex, :bandit, "1.2.2", "569fe5d0efb107c9af37a1e37e25ce2ceec293101a2d4bc512876fc3207192b5", [:mix], [{:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "2f89adb7281c78d4e75733e0a9e1b24f46f84d2993963d6fa57d0eafadec5f03"}, "earmark": {:hex, :earmark, "1.4.46", "8c7287bd3137e99d26ae4643e5b7ef2129a260e3dcf41f251750cb4563c8fb81", [:mix], [], "hexpm", "798d86db3d79964e759ddc0c077d5eb254968ed426399fbf5a62de2b5ff8910a"}, "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, + "hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"}, + "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, + "plug": {:hex, :plug, "1.15.3", "712976f504418f6dff0a3e554c40d705a9bcf89a7ccef92fc6a5ef8f16a30a97", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4365a3c010a56af402e0809208873d113e9c38c401cabd88027ef4f5c01fd2"}, + "plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"}, + "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, + "thousand_island": {:hex, :thousand_island, "1.3.2", "bc27f9afba6e1a676dd36507d42e429935a142cf5ee69b8e3f90bff1383943cd", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0e085b93012cd1057b378fce40cbfbf381ff6d957a382bfdd5eca1a98eec2535"}, "typed_struct": {:hex, :typed_struct, "0.3.0", "939789e3c1dca39d7170c87f729127469d1315dcf99fee8e152bb774b17e7ff7", [:mix], [], "hexpm", "c50bd5c3a61fe4e198a8504f939be3d3c85903b382bde4865579bc23111d1b6d"}, + "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, } diff --git a/priv/site/pages/about.md b/priv/site/pages/about.md new file mode 100644 index 0000000..489193a --- /dev/null +++ b/priv/site/pages/about.md @@ -0,0 +1 @@ +Yes it does!!