Packages
phosphor_live_view
2.1.0
Typed Phosphor icons for Phoenix LiveView with compile-time SVG components and dynamic helpers.
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/phosphor.gen.ex
defmodule Mix.Tasks.Phosphor.Gen do
use Mix.Task
@shortdoc "Generate the Phosphor manifest data"
@moduledoc """
Reads the SVG sources under `priv/phosphor_icons/raw/<weight>` and converts them
into a single manifest data file consumed at compile time.
"""
@weights ~w(thin light regular bold fill duotone)a
@src_root "priv/phosphor_icons/raw"
@dest_dir "lib/phosphor/generated"
@manifest_path Path.join(@dest_dir, "manifest_data.exs")
@impl true
def run(argv) do
Mix.shell().info("[phosphor] starting manifest generation...")
{opts, _rest, _invalid} =
OptionParser.parse(argv, strict: [weights: :string, clean: :boolean])
weights = parse_weights(opts)
if Keyword.get(opts, :clean, false), do: clean_dest!()
File.mkdir_p!(@dest_dir)
icons = discover_icons!()
manifest = build_manifest(icons, weights)
write_manifest!(manifest)
Mix.shell().info("[phosphor] generation complete: #{@manifest_path}")
end
defp parse_weights(opts) do
opts
|> Keyword.get(:weights)
|> case do
nil ->
@weights
list when is_binary(list) ->
list
|> String.split([",", " "], trim: true)
|> Enum.map(&String.to_atom/1)
end
|> Enum.filter(&(&1 in @weights))
|> Enum.uniq()
end
defp clean_dest! do
Mix.shell().info("[phosphor] cleaning #{@dest_dir}...")
manifest_file = Path.join(@dest_dir, "manifest_data.exs")
old_weights = Path.join(@dest_dir, "weights")
old_icons = Path.join(@dest_dir, "icons")
old_functions = Path.join(@dest_dir, "phosphor_functions.ex")
File.rm(manifest_file)
File.rm_rf(old_weights)
File.rm_rf(old_icons)
File.rm(old_functions)
end
defp discover_icons! do
regular_dir = Path.join(@src_root, "regular")
unless File.dir?(regular_dir) do
Mix.raise("could not find directory #{regular_dir}. Check the priv/phosphor_icons sources.")
end
regular_dir
|> File.ls!()
|> Enum.filter(&(Path.extname(&1) == ".svg"))
|> Enum.sort()
|> Enum.map(&Path.rootname/1)
end
defp build_manifest(icons, weights) do
icons
|> Enum.reduce([], fn icon_name, acc ->
svgs =
weights
|> Enum.reduce(%{}, fn weight, map ->
case read_svg(icon_name, weight) do
nil -> map
svg -> Map.put(map, weight, svg)
end
end)
if map_size(svgs) == 0 do
Mix.shell().warning("[phosphor] skipping #{icon_name}: no weights available")
acc
else
name = String.replace(icon_name, "-", "_")
[
%{
name: name,
svgs: svgs,
default_weight: default_weight(svgs)
}
| acc
]
end
end)
|> Enum.sort_by(& &1.name)
end
defp read_svg(icon_name, :regular) do
icon_name
|> regular_path()
|> read_svg_file()
end
defp read_svg(icon_name, weight) do
icon_name
|> weight_path(weight)
|> read_svg_file()
end
defp read_svg_file(path) do
if File.exists?(path) do
path
|> File.read!()
|> String.trim()
else
nil
end
end
defp regular_path(name) do
Path.join([@src_root, "regular", name <> ".svg"])
end
defp weight_path(name, weight) do
filename = "#{name}-#{Atom.to_string(weight)}.svg"
Path.join([@src_root, Atom.to_string(weight), filename])
end
defp default_weight(svgs) do
cond do
Map.has_key?(svgs, :regular) -> :regular
svgs == %{} -> :regular
true -> svgs |> Map.keys() |> Enum.sort() |> hd()
end
end
defp write_manifest!(manifest) do
prepared =
manifest
|> Enum.map(fn %{svgs: svgs} = entry ->
Map.put(entry, :svgs, normalize_svgs(svgs))
end)
contents =
[
"# Autogenerated by mix phosphor.gen. Do not edit manually.\n",
inspect(prepared, limit: :infinity, charlists: :as_lists),
"\n"
]
File.write!(@manifest_path, contents)
end
defp normalize_svgs(svgs) do
@weights
|> Enum.reduce([], fn weight, acc ->
case Map.fetch(svgs, weight) do
{:ok, svg} -> [{weight, svg} | acc]
:error -> acc
end
end)
|> Enum.reverse()
|> Enum.into(%{})
end
end