defmodule Mix.Tasks.Dog.Classes do @shortdoc "Lists all component classes" @moduledoc """ Returns a list of all component classes. ## Usage List all component classes: mix dog.classes -m MyAppWeb.CoreComponents Write classes to file: mix dog.classes -m MyAppWeb.CoreComponents -o assets/classes.txt Check whether a file is up-to-date: mix dog.classes -m MyAppWeb.CoreComponents -o assets/classes.txt --check """ use Mix.Task @parser_opts [ aliases: [m: :module, o: :output], strict: [module: :string, output: :string, check: :boolean] ] @requirements ["app.config"] @impl Mix.Task def run(args) do with {switches, [], []} <- OptionParser.parse(args, @parser_opts), {:ok, module} <- Keyword.fetch(switches, :module) do path = Keyword.get(switches, :output) check = Keyword.get(switches, :check, false) cond do is_nil(path) -> print_classes(module) check -> check_file(path, module) true -> write_file(path, module) end else _ -> IO.puts(@moduledoc) end end defp list_classes(module) do [module] |> Module.safe_concat() |> Doggo.classes() |> Enum.join("\n") end defp print_classes(module) do module |> list_classes() |> IO.puts() end defp write_file(path, module) do content = file_content(path, module) File.write(path, content) end defp check_file(path, module) do existing_content = File.read!(path) expected_content = file_content(path, module) relative_path = Path.relative_to_cwd(path) if existing_content == expected_content do IO.puts("The file '#{relative_path}' is up to date.") else IO.puts(""" The file '#{relative_path}' is out of date. To update the file, run: mix dog.classes -m #{module} -o #{relative_path} """) exit({:shutdown, 1}) end end defp file_content(path, module) do file_header = file_header(path, module) classes = list_classes(module) file_header <> classes end defp file_header(path, module) do """ # This file was automatically generated by running: # mix dog.classes -m #{module} -o #{path} """ end end