diff --git a/lib/mix/tasks/phx/install/dashboard.ex b/lib/mix/tasks/phx/install/dashboard.ex index 18ce436..458ae3d 100644 --- a/lib/mix/tasks/phx/install/dashboard.ex +++ b/lib/mix/tasks/phx/install/dashboard.ex @@ -29,7 +29,8 @@ defmodule Mix.Tasks.Phx.Install.Dashboard do %Igniter.Mix.Task.Info{ group: :phoenix, example: "mix phx.install.dashboard", - adds_deps: [{:phoenix_live_dashboard, "~> 0.8"}] + adds_deps: [{:phoenix_live_dashboard, "~> 0.8"}], + composes: ["phx.install.endpoint", "phx.install.live"] } end @@ -43,10 +44,15 @@ defmodule Mix.Tasks.Phx.Install.Dashboard do igniter |> Igniter.Project.Deps.add_dep({:phoenix, "~> 1.7"}) |> Igniter.Project.Deps.add_dep({:phoenix_live_dashboard, "~> 0.8"}) + |> Igniter.compose_task("phx.install.endpoint") + |> Igniter.compose_task("phx.install.live") |> add_dashboard_route(app_name, router_module, telemetry_module) end defp add_dashboard_route(igniter, app_name, router_module, telemetry_module) do + dashboard_call = + ~s|live_dashboard "/dashboard", metrics: #{inspect(telemetry_module)}| + dev_routes_code = """ if Application.compile_env(#{inspect(app_name)}, :dev_routes) do import Phoenix.LiveDashboard.Router @@ -54,7 +60,7 @@ defmodule Mix.Tasks.Phx.Install.Dashboard do scope "/dev" do pipe_through :browser - live_dashboard "/dashboard", metrics: #{inspect(telemetry_module)} + #{dashboard_call} end end """ @@ -66,33 +72,88 @@ defmodule Mix.Tasks.Phx.Install.Dashboard do [1, 2], &Igniter.Code.Function.argument_equals?(&1, 0, "/dashboard") ) do - {:ok, _} -> {:ok, zipper} - :error -> insert_dashboard_route(zipper, app_name, dev_routes_code) + {:ok, _} -> + {:ok, zipper} + + :error -> + insert_dashboard_route(zipper, dashboard_call, dev_routes_code) end end) end - defp insert_dashboard_route(zipper, app_name, dev_routes_code) do - case find_dev_routes_block(zipper, app_name) do - {:ok, _} -> + defp insert_dashboard_route(zipper, dashboard_call, dev_routes_code) do + case find_dev_routes_block(zipper) do + {:ok, dev_routes_zipper} -> + add_dashboard_to_existing_dev_scope(dev_routes_zipper, dashboard_call, dev_routes_code) + + :error -> + {:ok, Igniter.Code.Common.add_code(zipper, dev_routes_code)} + end + end + + defp add_dashboard_to_existing_dev_scope(dev_routes_zipper, dashboard_call, dev_routes_code) do + with {:ok, body_zipper} <- Igniter.Code.Common.move_to_do_block(dev_routes_zipper), + {:ok, scope_zipper} <- + Igniter.Code.Function.move_to_function_call_in_current_scope( + body_zipper, + :scope, + [1, 2], + &Igniter.Code.Function.argument_equals?(&1, 0, "/dev") + ) do + import_code = "import Phoenix.LiveDashboard.Router" + with_import = Igniter.Code.Common.add_code(scope_zipper, import_code, placement: :before) + + fresh_zipper = + with_import + |> Sourceror.Zipper.root() + |> Sourceror.Zipper.zip() + + with {:ok, dev_routes2} <- find_dev_routes_block(fresh_zipper), + {:ok, body2} <- Igniter.Code.Common.move_to_do_block(dev_routes2), + {:ok, scope2} <- + Igniter.Code.Function.move_to_function_call_in_current_scope( + body2, + :scope, + [1, 2], + &Igniter.Code.Function.argument_equals?(&1, 0, "/dev") + ), + {:ok, scope_body2} <- Igniter.Code.Common.move_to_do_block(scope2) do + {:ok, Igniter.Code.Common.add_code(scope_body2, dashboard_call)} + else + _ -> + {:warning, + Igniter.Util.Warning.formatted_warning( + "Found existing dev_routes block but couldn't add dashboard route. Please add manually:", + dev_routes_code + )} + end + else + _ -> {:warning, Igniter.Util.Warning.formatted_warning( - "Found existing dev_routes block but couldn't add dashboard. Please add manually:", + "Found existing dev_routes block but couldn't add dashboard route. Please add manually:", dev_routes_code )} - - :error -> - {:ok, Igniter.Code.Common.add_code(zipper, dev_routes_code)} end end - defp find_dev_routes_block(zipper, app_name) do + defp find_dev_routes_block(zipper) do Igniter.Code.Common.move_to(zipper, fn z -> case Sourceror.Zipper.node(z) do - {:if, _, [{:compile_env, _, [_, ^app_name, :dev_routes | _]} | _]} -> true - {:if, _, [{:compile_env, _, [:erlang, :binary_to_atom, [^app_name | _], _]} | _]} -> true - _ -> false + {:if, _, + [ + {{:., _, [{:__aliases__, _, [:Application]}, :compile_env]}, _, args} + | _ + ]} -> + args_contain_dev_routes?(args) + + _ -> + false end end) end + + defp args_contain_dev_routes?([_, {:__block__, _, [:dev_routes]} | _]), do: true + defp args_contain_dev_routes?([_, :dev_routes | _]), do: true + defp args_contain_dev_routes?(_), do: false end diff --git a/lib/mix/tasks/phx/install/router.ex b/lib/mix/tasks/phx/install/router.ex index d993646..66e8526 100644 --- a/lib/mix/tasks/phx/install/router.ex +++ b/lib/mix/tasks/phx/install/router.ex @@ -22,7 +22,8 @@ defmodule Mix.Tasks.Phx.Install.Router do def info(_argv, _composing_task) do %Igniter.Mix.Task.Info{ group: :phoenix, - example: "mix phx.install.router" + example: "mix phx.install.router", + composes: ["phx.install.core"] } end @@ -33,6 +34,7 @@ defmodule Mix.Tasks.Phx.Install.Router do igniter |> Igniter.Project.Deps.add_dep({:phoenix, "~> 1.7"}) + |> Igniter.compose_task("phx.install.core") |> create_router(web_module) |> create_error_json(web_module) |> create_conn_case(web_module, endpoint_module) diff --git a/test/mix/tasks/phx/install/dashboard_test.exs b/test/mix/tasks/phx/install/dashboard_test.exs index 129f442..1b16da5 100644 --- a/test/mix/tasks/phx/install/dashboard_test.exs +++ b/test/mix/tasks/phx/install/dashboard_test.exs @@ -113,5 +113,57 @@ defmodule Mix.Tasks.Phx.Install.DashboardTest do assert result.issues == [] end + + test "standalone invocation composes prerequisites and adds browser pipeline" do + igniter = + test_project() + |> Igniter.compose_task("phx.install.dashboard", ["--session-signing-salt", "sessionsalt"]) + |> apply_igniter!() + + source = Rewrite.source!(igniter.rewrite, "lib/test_web/router.ex") + content = Rewrite.Source.get(source, :content) + + assert content =~ "pipeline :browser" + assert content =~ "pipe_through" and content =~ ":browser" + assert content =~ "live_dashboard" + end + + test "adds live_dashboard into existing dev_routes block" do + dev_routes_block = """ + if Application.compile_env(:test, :dev_routes) do + scope "/dev" do + pipe_through :browser + end + end + """ + + igniter = + test_project() + |> Igniter.compose_task("phx.install.endpoint", ["--session-signing-salt", "sessionsalt"]) + |> Igniter.compose_task("phx.install.router") + |> apply_igniter!() + |> Igniter.Project.Module.find_and_update_module!( + TestWeb.Router, + fn zipper -> + {:ok, Igniter.Code.Common.add_code(zipper, dev_routes_block)} + end + ) + |> apply_igniter!() + |> Igniter.compose_task("phx.install.dashboard") + |> apply_igniter!() + + source = Rewrite.source!(igniter.rewrite, "lib/test_web/router.ex") + content = Rewrite.Source.get(source, :content) + + assert content =~ "live_dashboard" + assert content =~ "import Phoenix.LiveDashboard.Router" + + occurrences = + content + |> String.split("dev_routes") + |> length() + + assert occurrences == 2, "expected exactly one dev_routes block, found #{occurrences - 1}" + end end end diff --git a/test/mix/tasks/phx/install/router_test.exs b/test/mix/tasks/phx/install/router_test.exs index 245c0ca..64b31c0 100644 --- a/test/mix/tasks/phx/install/router_test.exs +++ b/test/mix/tasks/phx/install/router_test.exs @@ -29,7 +29,7 @@ defmodule Mix.Tasks.Phx.Install.RouterTest do test "creates error_json module" do test_project() |> Igniter.compose_task("phx.install.router") - |> assert_creates("lib/test_web/error_json.ex") + |> assert_creates("lib/test_web/controllers/error_json.ex") end test "error_json has correct structure" do @@ -38,7 +38,7 @@ defmodule Mix.Tasks.Phx.Install.RouterTest do |> Igniter.compose_task("phx.install.router") |> apply_igniter!() - source = Rewrite.source!(igniter.rewrite, "lib/test_web/error_json.ex") + source = Rewrite.source!(igniter.rewrite, "lib/test_web/controllers/error_json.ex") content = Rewrite.Source.get(source, :content) assert content =~ "defmodule TestWeb.ErrorJSON" @@ -77,7 +77,7 @@ defmodule Mix.Tasks.Phx.Install.RouterTest do |> apply_igniter!() assert igniter.rewrite.sources["lib/my_app_web/router.ex"] - assert igniter.rewrite.sources["lib/my_app_web/error_json.ex"] + assert igniter.rewrite.sources["lib/my_app_web/controllers/error_json.ex"] assert igniter.rewrite.sources["lib/my_app_web/conn_case.ex"] router_content =