Packages
mob
0.6.1
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.26
0.6.25
0.6.24
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.2
0.6.1
0.6.0
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.11
0.5.10
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
0.1.0
BEAM-on-device mobile framework for Elixir
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/erlfmt.ex
defmodule Mix.Tasks.Erlfmt do
@moduledoc """
Format `.erl` files (or check formatting with `--check`).
Wraps `erlfmt`'s library API. Exists because the upstream `erlfmt` Hex package
ships an escript build but no `mix` task; the project's pre-commit checklist
(`CLAUDE.md`) references `mix erlfmt --check src/` so this task makes that
instruction actually work.
## Usage
mix erlfmt --check src/ # exit 0 if clean, exit 1 if any file would change
mix erlfmt --write src/ # rewrite files in place
Either `--check` or `--write` is required. Paths can be files or directories;
directories are walked for `*.erl` files.
"""
use Mix.Task
@shortdoc "Format Erlang sources via erlfmt"
@impl Mix.Task
def run(args) do
{opts, paths} =
OptionParser.parse!(args,
strict: [check: :boolean, write: :boolean],
aliases: [c: :check, w: :write]
)
if !opts[:check] and !opts[:write] do
Mix.raise("mix erlfmt requires --check or --write")
end
if paths == [], do: Mix.raise("mix erlfmt requires at least one path")
# erlfmt is `only: :dev, runtime: false` in mob's mix.exs, so when
# mob is compiled as a dep of a downstream project (without erlfmt
# in *their* deps), the static reference would warn. Resolve at
# runtime via apply so the compiler doesn't complain — and surface
# a clean error if the dep really isn't on the path.
unless Code.ensure_loaded?(:erlfmt) do
Mix.raise(
"mix erlfmt requires the `:erlfmt` dep — add `{:erlfmt, \"~> 1.8\", " <>
"only: :dev, runtime: false}` to your project's mix.exs and rerun `mix deps.get`."
)
end
Application.ensure_all_started(:erlfmt)
files = Enum.flat_map(paths, &collect_erl_files/1)
{ok_count, changed} =
Enum.reduce(files, {0, []}, fn file, {ok, changed} ->
case apply(:erlfmt, :format_file, [String.to_charlist(file), [:return]]) do
{:ok, formatted, _warnings} ->
original = File.read!(file)
# erlfmt returns iodata that may include codepoints > 255 (e.g.
# em-dashes inside strings/comments). `:unicode.characters_to_binary`
# handles those; `IO.iodata_to_binary` would crash with ArgumentError.
new = :unicode.characters_to_binary(formatted)
cond do
new == original ->
{ok + 1, changed}
opts[:write] ->
File.write!(file, new)
Mix.shell().info("formatted #{file}")
{ok + 1, changed}
true ->
{ok, [file | changed]}
end
{:skip, _} ->
{ok + 1, changed}
{:error, reason} ->
Mix.shell().error("#{file}: #{inspect(reason)}")
{ok, [file | changed]}
end
end)
cond do
changed == [] ->
Mix.shell().info("erlfmt: #{ok_count} file(s) checked, all formatted")
:ok
opts[:check] ->
Mix.shell().error(
"erlfmt: #{length(changed)} file(s) need formatting:\n " <>
Enum.join(changed, "\n ") <>
"\n\nRun `mix erlfmt --write <path>` to fix."
)
exit({:shutdown, 1})
true ->
:ok
end
end
defp collect_erl_files(path) do
cond do
File.dir?(path) -> Path.wildcard("#{path}/**/*.erl")
File.regular?(path) and String.ends_with?(path, ".erl") -> [path]
true -> []
end
end
end