lib/mix/task.compile.domo_phoenix_hot_reload.ex

defmodule Mix.Tasks.Compile.DomoPhoenixHotReload do
  @moduledoc """
  The module to process domo plan into type ensurers.

  In case of compile.all mix task the plan is processed automatically
  in :after_compile hook. That is not the case with Phoenix hot-reload
  which calls each compiler's mix task directly that eventually keeps :after_compile
  hook intact and stays in plan collection mode forever.

  The :domo_phoenix_hot_reload compiler process plan on execution.
  """

  use Mix.Task.Compiler

  alias Domo.CodeEvaluation
  alias Domo.Raises
  alias Mix.Tasks.Compile.DomoCompiler
  alias Mix.TasksServer

  @mix_project Application.compile_env(:domo, :mix_project, Mix.Project)

  @impl true
  def run(args) do
    cond do
      not hot_reload_compiler_in_project?() -> {:noop, []}
      not elixir_run_before?() -> Raises.raise_no_elixir_compiler_was_run()
      CodeEvaluation.in_plan_collection?() -> DomoCompiler.process_plan(:ok, args)
      true -> {:noop, []}
    end
  end

  defp hot_reload_compiler_in_project? do
    compilers = get_in(@mix_project.config(), [:compilers]) || []
    Enum.member?(compilers, :domo_phoenix_hot_reload)
  end

  defp elixir_run_before? do
    project = @mix_project.get()
    elixir_command = Mix.Task.task_name(Mix.Tasks.Compile.Elixir)
    tuple = {:task, elixir_command, project}

    if function_exported?(TasksServer, :get, 1) do
      TasksServer.get(tuple)
    else
      # There is no function before Elixir v1.11.2
      Agent.get(TasksServer, &Map.get(&1, tuple), :infinity)
    end
  end
end