mirror of
https://github.com/ash-project/igniter.git
synced 2024-09-20 05:22:52 +12:00
improvement: Add Igniter.Libs.Phoenix
for working with Phoenix
improvement: deprecate duplicate `Igniter.Code.Module.move_to_use` function improvement: `Igniter.Project.Config.configures?/4` that takes a config file improvement: Add `Igniter.Util.Warning` for formatting code in warnings
This commit is contained in:
parent
29387cd2f2
commit
d00dd671c7
7 changed files with 270 additions and 16 deletions
|
@ -70,7 +70,11 @@ defmodule Igniter.Code.Function do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Moves to a function call by the given name and arity, matching the given predicate, in the current scope"
|
@doc "Moves to a function call by the given name and arity, matching the given predicate, in the current scope"
|
||||||
@spec move_to_function_call_in_current_scope(Zipper.t(), atom, non_neg_integer()) ::
|
@spec move_to_function_call_in_current_scope(
|
||||||
|
Zipper.t(),
|
||||||
|
atom,
|
||||||
|
non_neg_integer() | list(non_neg_integer())
|
||||||
|
) ::
|
||||||
{:ok, Zipper.t()} | :error
|
{:ok, Zipper.t()} | :error
|
||||||
def move_to_function_call_in_current_scope(zipper, name, arity, predicate \\ fn _ -> true end)
|
def move_to_function_call_in_current_scope(zipper, name, arity, predicate \\ fn _ -> true end)
|
||||||
|
|
||||||
|
|
|
@ -474,7 +474,7 @@ defmodule Igniter.Code.Module do
|
||||||
def move_to_module_using(zipper, module) do
|
def move_to_module_using(zipper, module) do
|
||||||
with {:ok, mod_zipper} <- move_to_defmodule(zipper),
|
with {:ok, mod_zipper} <- move_to_defmodule(zipper),
|
||||||
{:ok, mod_zipper} <- Igniter.Code.Common.move_to_do_block(mod_zipper),
|
{:ok, mod_zipper} <- Igniter.Code.Common.move_to_do_block(mod_zipper),
|
||||||
{:ok, _} <- move_to_using(mod_zipper, module) do
|
{:ok, _} <- move_to_use(mod_zipper, module) do
|
||||||
{:ok, mod_zipper}
|
{:ok, mod_zipper}
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
|
@ -482,20 +482,19 @@ defmodule Igniter.Code.Module do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def move_to_using(zipper, using) do
|
@deprecated "Use `move_to_use/2` instead."
|
||||||
Igniter.Code.Function.move_to_function_call_in_current_scope(
|
def move_to_using(zipper, module), do: move_to_use(zipper, module)
|
||||||
zipper,
|
|
||||||
:use,
|
|
||||||
[1, 2],
|
|
||||||
fn zipper ->
|
|
||||||
with {:ok, actual_using} <- Igniter.Code.Function.move_to_nth_argument(zipper, 0) do
|
|
||||||
Igniter.Code.Common.nodes_equal?(actual_using, using)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc "Moves the zipper to the `use` statement for a provided module."
|
@doc "Moves the zipper to the `use` statement for a provided module."
|
||||||
|
def move_to_use(zipper, [module]), do: move_to_use(zipper, module)
|
||||||
|
|
||||||
|
def move_to_use(zipper, [module | rest]) do
|
||||||
|
case move_to_use(zipper, module) do
|
||||||
|
{:ok, zipper} -> {:ok, zipper}
|
||||||
|
_ -> move_to_use(zipper, rest)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def move_to_use(zipper, module) do
|
def move_to_use(zipper, module) do
|
||||||
Igniter.Code.Function.move_to_function_call_in_current_scope(zipper, :use, [1, 2], fn call ->
|
Igniter.Code.Function.move_to_function_call_in_current_scope(zipper, :use, [1, 2], fn call ->
|
||||||
Igniter.Code.Function.argument_matches_predicate?(
|
Igniter.Code.Function.argument_matches_predicate?(
|
||||||
|
|
208
lib/igniter/libs/phoenix.ex
Normal file
208
lib/igniter/libs/phoenix.ex
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
defmodule Igniter.Libs.Phoenix do
|
||||||
|
@moduledoc "Codemods & utilities for working with Phoenix"
|
||||||
|
|
||||||
|
def add_scope(igniter, route, contents, opts \\ []) do
|
||||||
|
{igniter, router} =
|
||||||
|
case Keyword.fetch(opts, :router) do
|
||||||
|
{:ok, router} ->
|
||||||
|
{igniter, router}
|
||||||
|
|
||||||
|
:error ->
|
||||||
|
select_router(igniter)
|
||||||
|
end
|
||||||
|
|
||||||
|
scope_code =
|
||||||
|
case Keyword.fetch(opts, :arg2) do
|
||||||
|
{:ok, arg2} ->
|
||||||
|
contents = Sourceror.parse_string!(contents)
|
||||||
|
|
||||||
|
quote do
|
||||||
|
scope unquote(route), unquote(arg2) do
|
||||||
|
unquote(contents)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|> Sourceror.to_string()
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
"""
|
||||||
|
scope #{inspect(route)} do
|
||||||
|
#{contents}
|
||||||
|
end
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
if router do
|
||||||
|
Igniter.Code.Module.find_and_update_module!(igniter, router, fn zipper ->
|
||||||
|
case move_to_scope_location(zipper) do
|
||||||
|
{:ok, zipper, append_or_prepend} ->
|
||||||
|
{:ok, Igniter.Code.Common.add_code(zipper, scope_code, append_or_prepend)}
|
||||||
|
|
||||||
|
:error ->
|
||||||
|
{:warning,
|
||||||
|
Igniter.Util.Warning.formatted_warning(
|
||||||
|
"Could not add a scope for #{inspect(route)} to your router. Please add it manually.",
|
||||||
|
scope_code
|
||||||
|
)}
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
Igniter.add_warning(
|
||||||
|
igniter,
|
||||||
|
Igniter.Util.Warning.formatted_warning(
|
||||||
|
"Could not add a scope for #{inspect(route)} to your router. Please add it manually.",
|
||||||
|
scope_code
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_pipeline(igniter, name, contents, opts \\ []) do
|
||||||
|
{igniter, router} =
|
||||||
|
case Keyword.fetch(opts, :router) do
|
||||||
|
{:ok, router} ->
|
||||||
|
{igniter, router}
|
||||||
|
|
||||||
|
:error ->
|
||||||
|
select_router(igniter)
|
||||||
|
end
|
||||||
|
|
||||||
|
pipeline_code = """
|
||||||
|
pipeline #{inspect(name)} do
|
||||||
|
#{contents}
|
||||||
|
end
|
||||||
|
"""
|
||||||
|
|
||||||
|
if router do
|
||||||
|
Igniter.Code.Module.find_and_update_module!(igniter, router, fn zipper ->
|
||||||
|
Igniter.Code.Function.move_to_function_call(zipper, :pipeline, 2, fn zipper ->
|
||||||
|
Igniter.Code.Function.argument_matches_predicate?(
|
||||||
|
zipper,
|
||||||
|
0,
|
||||||
|
&Igniter.Code.Common.nodes_equal?(&1, name)
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|> case do
|
||||||
|
{:ok, _} ->
|
||||||
|
{:warning,
|
||||||
|
Igniter.Util.Warning.formatted_warning(
|
||||||
|
"The #{name} pipeline already exists in the router. Attempting to add scope: ",
|
||||||
|
pipeline_code
|
||||||
|
)}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
case move_to_pipeline_location(zipper) do
|
||||||
|
{:ok, zipper, append_or_prepend} ->
|
||||||
|
{:ok, Igniter.Code.Common.add_code(zipper, pipeline_code, append_or_prepend)}
|
||||||
|
|
||||||
|
:error ->
|
||||||
|
{:warning,
|
||||||
|
Igniter.Util.Warning.formatted_warning(
|
||||||
|
"Could not add the #{name} pipline to your router. Please add it manually.",
|
||||||
|
pipeline_code
|
||||||
|
)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
Igniter.add_warning(
|
||||||
|
igniter,
|
||||||
|
Igniter.Util.Warning.formatted_warning(
|
||||||
|
"Could not add the #{name} pipline to your router. Please add it manually.",
|
||||||
|
pipeline_code
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def select_router(igniter, label \\ "Which router should be modified?") do
|
||||||
|
case list_routers(igniter) do
|
||||||
|
{igniter, []} ->
|
||||||
|
{igniter, nil}
|
||||||
|
|
||||||
|
{igniter, [router]} ->
|
||||||
|
{igniter, router}
|
||||||
|
|
||||||
|
{igniter, routers} ->
|
||||||
|
{igniter, Owl.IO.select(routers, label: label, render_as: &inspect/1)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_routers(igniter) do
|
||||||
|
Igniter.Code.Module.find_all_matching_modules(igniter, fn _mod, zipper ->
|
||||||
|
router_name =
|
||||||
|
Module.concat([to_string(Igniter.Code.Module.module_name_prefix()) <> "Web"])
|
||||||
|
|
||||||
|
with :error <-
|
||||||
|
Igniter.Code.Function.move_to_function_call(zipper, :use, 2, fn zipper ->
|
||||||
|
Igniter.Code.Function.argument_matches_predicate?(
|
||||||
|
zipper,
|
||||||
|
0,
|
||||||
|
&Igniter.Code.Common.nodes_equal?(&1, router_name)
|
||||||
|
) &&
|
||||||
|
Igniter.Code.Function.argument_matches_predicate?(
|
||||||
|
zipper,
|
||||||
|
1,
|
||||||
|
&Igniter.Code.Common.nodes_equal?(&1, :router)
|
||||||
|
)
|
||||||
|
end),
|
||||||
|
:error <- Igniter.Code.Module.move_to_use(zipper, Phoenix.Router) do
|
||||||
|
false
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp move_to_pipeline_location(zipper) do
|
||||||
|
with {:pipeline, :error} <-
|
||||||
|
{:pipeline,
|
||||||
|
Igniter.Code.Function.move_to_function_call_in_current_scope(zipper, :pipeline, 2)},
|
||||||
|
:error <-
|
||||||
|
Igniter.Code.Function.move_to_function_call_in_current_scope(zipper, :scope, [2, 3, 4]) do
|
||||||
|
case Igniter.Code.Module.move_to_use(zipper, Phoenix.Router) do
|
||||||
|
{:ok, zipper} -> {:ok, zipper, :after}
|
||||||
|
:error -> :error
|
||||||
|
end
|
||||||
|
else
|
||||||
|
{:pipeline, {:ok, zipper}} ->
|
||||||
|
{:ok, zipper, :after}
|
||||||
|
|
||||||
|
{:ok, zipper} ->
|
||||||
|
{:ok, zipper, :before}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp move_to_scope_location(zipper) do
|
||||||
|
with :error <-
|
||||||
|
Igniter.Code.Function.move_to_function_call_in_current_scope(zipper, :scope, [2, 3, 4]),
|
||||||
|
{:pipeline, :error} <- {:pipeline, last_pipeline(zipper)} do
|
||||||
|
case Igniter.Code.Module.move_to_use(zipper, Phoenix.Router) do
|
||||||
|
{:ok, zipper} -> {:ok, zipper, :after}
|
||||||
|
:error -> :error
|
||||||
|
end
|
||||||
|
else
|
||||||
|
{:ok, zipper} ->
|
||||||
|
{:ok, zipper, :before}
|
||||||
|
|
||||||
|
{:pipeline, {:ok, zipper}} ->
|
||||||
|
{:ok, zipper, :after}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp last_pipeline(zipper) do
|
||||||
|
case Igniter.Code.Function.move_to_function_call_in_current_scope(zipper, :pipeline, 2) do
|
||||||
|
{:ok, zipper} ->
|
||||||
|
with zipper when not is_nil(zipper) <- Sourceror.Zipper.right(zipper),
|
||||||
|
{:ok, zipper} <- last_pipeline(zipper) do
|
||||||
|
{:ok, zipper}
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
{:ok, zipper}
|
||||||
|
end
|
||||||
|
|
||||||
|
:error ->
|
||||||
|
:error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -225,8 +225,27 @@ defmodule Igniter.Project.Config do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Returns `true` if the given configuration path is set somewhere after the provided zipper."
|
@doc "Returns `true` if the given configuration path is set somewhere after the provided zipper, or in the given configuration file."
|
||||||
@spec configures?(Zipper.t(), list(atom), atom()) :: boolean()
|
@spec configures?(Igniter.t(), file :: String.t(), list(atom), atom()) :: boolean()
|
||||||
|
def configures?(igniter, file, path, app_name) do
|
||||||
|
file_path = Path.join("config", file)
|
||||||
|
|
||||||
|
igniter =
|
||||||
|
Igniter.include_existing_elixir_file(igniter, file_path, required?: false)
|
||||||
|
|
||||||
|
case Rewrite.source(igniter.rewrite, file_path) do
|
||||||
|
{:ok, source} ->
|
||||||
|
source
|
||||||
|
|> Rewrite.Source.get(:quoted)
|
||||||
|
|> Zipper.zip()
|
||||||
|
|> configures?(path, app_name)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec configures?(zipper :: Zipper.t(), list(atom), atom()) :: boolean()
|
||||||
def configures?(zipper, config_path, app_name) do
|
def configures?(zipper, config_path, app_name) do
|
||||||
if Enum.count(config_path) == 1 do
|
if Enum.count(config_path) == 1 do
|
||||||
config_item = Enum.at(config_path, 0)
|
config_item = Enum.at(config_path, 0)
|
||||||
|
|
20
lib/igniter/util/warning.ex
Normal file
20
lib/igniter/util/warning.ex
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
defmodule Igniter.Util.Warning do
|
||||||
|
@moduledoc "Utilities for emitting well formatted warnings"
|
||||||
|
def warn_with_code_sample(igniter, message, code) do
|
||||||
|
Igniter.add_warning(igniter, formatted_warning(message, code))
|
||||||
|
end
|
||||||
|
|
||||||
|
def formatted_warning(message, code) do
|
||||||
|
formatted =
|
||||||
|
Code.format_string!(code)
|
||||||
|
|> IO.iodata_to_binary()
|
||||||
|
|> String.split("\n")
|
||||||
|
|> Enum.map_join("\n", &(" " <> &1))
|
||||||
|
|
||||||
|
"""
|
||||||
|
#{message}
|
||||||
|
|
||||||
|
#{formatted}
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
end
|
2
mix.exs
2
mix.exs
|
@ -91,6 +91,8 @@ defmodule Igniter.MixProject do
|
||||||
{:spitfire, "~> 0.1 and >= 0.1.3"},
|
{:spitfire, "~> 0.1 and >= 0.1.3"},
|
||||||
{:sourceror, "~> 1.4"},
|
{:sourceror, "~> 1.4"},
|
||||||
{:jason, "~> 1.4"},
|
{:jason, "~> 1.4"},
|
||||||
|
{:owl, "~> 0.9"},
|
||||||
|
{:ucwidth, "~> 0.2"},
|
||||||
# can't use spark because spark depends on this
|
# can't use spark because spark depends on this
|
||||||
{:nimble_options, "~> 1.0"},
|
{:nimble_options, "~> 1.0"},
|
||||||
# Dev/Test dependencies
|
# Dev/Test dependencies
|
||||||
|
|
2
mix.lock
2
mix.lock
|
@ -24,10 +24,12 @@
|
||||||
"mix_test_watch": {:hex, :mix_test_watch, "1.2.0", "1f9acd9e1104f62f280e30fc2243ae5e6d8ddc2f7f4dc9bceb454b9a41c82b42", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "278dc955c20b3fb9a3168b5c2493c2e5cffad133548d307e0a50c7f2cfbf34f6"},
|
"mix_test_watch": {:hex, :mix_test_watch, "1.2.0", "1f9acd9e1104f62f280e30fc2243ae5e6d8ddc2f7f4dc9bceb454b9a41c82b42", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "278dc955c20b3fb9a3168b5c2493c2e5cffad133548d307e0a50c7f2cfbf34f6"},
|
||||||
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
|
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
|
||||||
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
|
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
|
||||||
|
"owl": {:hex, :owl, "0.9.0", "9b33d64734bd51d3fc1d6ed01b12f8c2ed23e1fbf8c43658a6dfbff62578bd03", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "cd70b55327985f8f24d38cb7de5bf8a8d24040e1b49cca2345508f8119ce81fd"},
|
||||||
"rewrite": {:hex, :rewrite, "0.10.5", "6afadeae0b9d843b27ac6225e88e165884875e0aed333ef4ad3bf36f9c101bed", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "51cc347a4269ad3a1e7a2c4122dbac9198302b082f5615964358b4635ebf3d4f"},
|
"rewrite": {:hex, :rewrite, "0.10.5", "6afadeae0b9d843b27ac6225e88e165884875e0aed333ef4ad3bf36f9c101bed", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "51cc347a4269ad3a1e7a2c4122dbac9198302b082f5615964358b4635ebf3d4f"},
|
||||||
"sourceror": {:hex, :sourceror, "1.4.0", "be87319b1579191e25464005d465713079b3fd7124a3938a1e6cf4def39735a9", [:mix], [], "hexpm", "16751ca55e3895f2228938b703ad399b0b27acfe288eff6c0e629ed3e6ec0358"},
|
"sourceror": {:hex, :sourceror, "1.4.0", "be87319b1579191e25464005d465713079b3fd7124a3938a1e6cf4def39735a9", [:mix], [], "hexpm", "16751ca55e3895f2228938b703ad399b0b27acfe288eff6c0e629ed3e6ec0358"},
|
||||||
"spitfire": {:hex, :spitfire, "0.1.3", "7ea0f544005dfbe48e615ed90250c9a271bfe126914012023fd5e4b6b82b7ec7", [:mix], [], "hexpm", "d53b5107bcff526a05c5bb54c95e77b36834550affd5830c9f58760e8c543657"},
|
"spitfire": {:hex, :spitfire, "0.1.3", "7ea0f544005dfbe48e615ed90250c9a271bfe126914012023fd5e4b6b82b7ec7", [:mix], [], "hexpm", "d53b5107bcff526a05c5bb54c95e77b36834550affd5830c9f58760e8c543657"},
|
||||||
"statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"},
|
"statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"},
|
||||||
|
"ucwidth": {:hex, :ucwidth, "0.2.0", "1f0a440f541d895dff142275b96355f7e91e15bca525d4a0cc788ea51f0e3441", [:mix], [], "hexpm", "c1efd1798b8eeb11fb2bec3cafa3dd9c0c3647bee020543f0340b996177355bf"},
|
||||||
"yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"},
|
"yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"},
|
||||||
"yaml_elixir": {:hex, :yaml_elixir, "2.9.0", "9a256da867b37b8d2c1ffd5d9de373a4fda77a32a45b452f1708508ba7bbcb53", [:mix], [{:yamerl, "~> 0.10", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "0cb0e7d4c56f5e99a6253ed1a670ed0e39c13fc45a6da054033928607ac08dfc"},
|
"yaml_elixir": {:hex, :yaml_elixir, "2.9.0", "9a256da867b37b8d2c1ffd5d9de373a4fda77a32a45b452f1708508ba7bbcb53", [:mix], [{:yamerl, "~> 0.10", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "0cb0e7d4c56f5e99a6253ed1a670ed0e39c13fc45a6da054033928607ac08dfc"},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue