defmodule Serum.DevServer do @moduledoc """ The Serum development server. """ alias Serum.DevServer.Logger alias Serum.DevServer.Service alias Serum.Project alias Serum.Project.Loader, as: ProjectLoader alias Serum.Result @doc """ Starts the Serum development server. This function returns `{:ok, pid}` on success where `pid` is a process ID of a supervision tree for the Serum development server. """ @spec run(binary, pos_integer) :: Result.t(pid()) def run(dir, port) do uniq = Base.url_encode64(:crypto.strong_rand_bytes(6)) site = Path.expand("serum_" <> uniq, System.tmp_dir!()) with {:ok, %Project{} = proj} <- ProjectLoader.load(dir, site), {:ok, pid} when is_pid(pid) <- do_run(dir, site, port, proj) do {:ok, pid} else {:error, {:shutdown, {:failed_to_start_child, _, :eaddrinuse}}} -> msg = "could not start the Serum development server. " <> "Make sure the port #{port} is not used by other applications" {:error, msg} {:error, {:shutdown, reason}} when not is_list(reason) -> msg = "could not start the Serum development server: #{inspect(reason)}" {:error, msg} {:error, _} = error -> error end end @spec do_run(binary(), binary(), integer(), Project.t()) :: Supervisor.on_start() defp do_run(dir, site, port, proj) do trap_exit = Process.flag(:trap_exit, true) base = proj.base_url ms_options = [ port: port, base: base, callbacks: [Logger], index: true, route_overrides: [ {"/serum_live_reloader", Serum.DevServer.LiveReloadHandler, nil} ] ] children = [ {Service.GenServer, {dir, site, port}}, %{ id: Microscope, start: {Microscope, :start_link, [site, ms_options]} } ] sup_opts = [strategy: :one_for_one, name: Serum.DevServer.Supervisor] start_result = Supervisor.start_link(children, sup_opts) Process.flag(:trap_exit, trap_exit) start_result end end