diff --git a/lib/mix/lib/mix/tasks/format.ex b/lib/mix/lib/mix/tasks/format.ex index 131a1eb383f..9c5c3a06d1b 100644 --- a/lib/mix/lib/mix/tasks/format.ex +++ b/lib/mix/lib/mix/tasks/format.ex @@ -126,8 +126,8 @@ defmodule Mix.Tasks.Format do def run(args) do {opts, args} = OptionParser.parse!(args, strict: @switches) - formatter_opts = eval_dot_formatter(opts) - formatter_opts = fetch_deps_opts(formatter_opts) + {formatter_dir, formatter_opts} = eval_dot_formatter(opts) + formatter_opts = fetch_deps_opts(formatter_opts, in_directory: formatter_dir) args |> expand_args(formatter_opts) @@ -138,8 +138,11 @@ defmodule Mix.Tasks.Format do defp eval_dot_formatter(opts) do case dot_formatter(opts) do - {:ok, dot_formatter} -> eval_file_with_keyword_list(dot_formatter) - :error -> [] + {:ok, dot_formatter} -> + {Path.dirname(dot_formatter), eval_file_with_keyword_list(dot_formatter)} + + :error -> + {nil, []} end end @@ -153,6 +156,16 @@ defmodule Mix.Tasks.Format do # This function reads exported configuration from the imported dependencies and deals with # caching the result of reading such configuration in a manifest file. + defp fetch_deps_opts(formatter_opts, in_directory: nil) do + fetch_deps_opts(formatter_opts) + end + + defp fetch_deps_opts(formatter_opts, in_directory: formatter_dir) do + File.cd!(formatter_dir, fn -> + fetch_deps_opts(formatter_opts) + end) + end + defp fetch_deps_opts(formatter_opts) do deps = Keyword.get(formatter_opts, :import_deps, []) diff --git a/lib/mix/test/mix/tasks/format_test.exs b/lib/mix/test/mix/tasks/format_test.exs index 12b71dea779..c90dd39dfdd 100644 --- a/lib/mix/test/mix/tasks/format_test.exs +++ b/lib/mix/test/mix/tasks/format_test.exs @@ -219,6 +219,46 @@ defmodule Mix.Tasks.FormatTest do end end + test "can read exported configuration from dependencies with .formatter.exs in a different folder", + context do + Mix.Project.push(__MODULE__.FormatWithDepsApp) + + in_tmp context.test, fn -> + File.write!(".formatter.exs", """ + [import_deps: [:my_dep]] + """) + + File.mkdir_p!("deps/my_dep/") + + File.write!("deps/my_dep/.formatter.exs", """ + [export: [locals_without_parens: [my_fun: 2]]] + """) + + File.mkdir_p!("subfolder") + + File.cd!("subfolder", fn -> + File.write!("a.ex", """ + my_fun :foo, :bar + """) + + Mix.Tasks.Format.run(["--dot-formatter", "../.formatter.exs", "a.ex"]) + + assert File.read!("a.ex") == """ + my_fun :foo, :bar + """ + end) + + manifest_path = Path.join(Mix.Project.manifest_path(), "cached_formatter_deps") + assert File.regular?(manifest_path) + + # Let's check that the manifest gets updated if it's stale. + File.touch!(manifest_path, {{1970, 1, 1}, {0, 0, 0}}) + + Mix.Tasks.Format.run(["subfolder/a.ex"]) + assert File.stat!(manifest_path).mtime > {{1970, 1, 1}, {0, 0, 0}} + end + end + test "validates dependencies in :import_deps", context do Mix.Project.push(__MODULE__.FormatWithDepsApp)